ChatGPT解决这个技术问题 Extra ChatGPT

在 JavaScript 中按名称读取 cookie 的最短函数是什么?

在 JavaScript 中读取 cookie 的最短、准确且跨浏览器兼容的方法是什么?

很多时候,在构建独立脚本(我不能有任何外部依赖项)时,我发现自己添加了一个读取 cookie 的函数,并且通常回退到 QuirksMode.org readCookie() 方法(280 字节,216 缩小。)

function readCookie(name) {
    var nameEQ = name + "=";
    var ca = document.cookie.split(';');
    for(var i=0;i < ca.length;i++) {
        var c = ca[i];
        while (c.charAt(0)==' ') c = c.substring(1,c.length);
        if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
    }
    return null;
}

它可以完成这项工作,但它很丑陋,并且每次都会增加相当多的臃肿。

jQuery.cookie 使用类似这样的方法(修改,165 字节,125 缩小):

function read_cookie(key)
{
    var result;
    return (result = new RegExp('(?:^|; )' + encodeURIComponent(key) + '=([^;]*)').exec(document.cookie)) ? (result[1]) : null;
}

请注意,这不是“代码高尔夫”比赛:我对减小 readCookie 函数的大小以及确保我拥有的解决方案是有效的感兴趣。

存储的 cookie 数据有点丑陋,所以任何处理它们的方法都可能也是如此。
@mVChr 认真的。在什么时候决定应该从分号分隔的字符串访问 cookie?什么时候是个好主意?
为什么这个问题仍然悬而未决,为什么它有赏金?你真的那么拼命想节省 5 个字节吗???
cookieArr = document.cookie.split(';').map(ck=>{return {[ck.split('=')[0].trim()]:ck.split('=')[1] }})

E
EscapeNetscape

比当前投票最多的答案更短、更可靠、性能更高:

const getCookieValue = (name) => (
  document.cookie.match('(^|;)\\s*' + name + '\\s*=\\s*([^;]+)')?.pop() || ''
)

此处显示了各种方法的性能比较:

https://jsben.ch/AhMN6

关于方法的一些注意事项:

正则表达式方法不仅在大多数浏览器中是最快的,它也产生了最短的函数。另外应该指出的是,根据official spec (RFC 2109),分号后面的空格分隔在 document.cookie 中的 cookie 是可选的,并且可以提出不应该依赖它的论点。此外,等号 (=) 之前和之后允许有空格,并且可以提出一个参数,即这个潜在的空格应该被考虑到任何可靠的 document.cookie 解析器中。上面的正则表达式说明了上述两个空白条件。


我刚刚注意到,在 Firefox 中,我在上面发布的正则表达式方法不如循环方法的性能好。我之前运行的测试是在 Chrome 中完成的,其中正则表达式方法的性能略好于其他方法。尽管如此,它仍然是解决所提问题的最短时间。
为什么 getCookieValue(a, b) 采用参数 b
赞成,但不是为了可读性...我花了一段时间才弄清楚 ab 做了什么。
聪明,但以这种方式编写它以节省 1 个字节很愚蠢。
a 参数不是正则表达式转义,虽然它很有用,但不安全。 getCookieValue('.*') 之类的东西会返回任何随机 cookie
R
Ryan M

这只会命中 document.cookie 一次。随后的每个请求都将是即时的。

(function(){
    var cookies;

    function readCookie(name,c,C,i){
        if(cookies){ return cookies[name]; }

        c = document.cookie.split('; ');
        cookies = {};

        for(i=c.length-1; i>=0; i--){
           C = c[i].split('=');
           cookies[C[0]] = C[1];
        }

        return cookies[name];
    }

    window.readCookie = readCookie; // or expose it however you want
})();

恐怕真的没有比这个一般逻辑更快的方法了,除非你可以自由使用依赖于浏览器的 .forEach(即使那样你也没有节省那么多)

您自己的示例稍微压缩为 120 bytes

function read_cookie(k,r){return(r=RegExp('(^|; )'+encodeURIComponent(k)+'=([^;]*)').exec(document.cookie))?r[2]:null;}

如果将其设为 1 个字母的函数名称,则可以将其设为 110 bytes,如果删除 encodeURIComponent,则将其设为 90 bytes

我已经把它归结为 73 bytes,但公平地说,当命名为 readCookie 时它是 82 bytes,然后添加 encodeURIComponent 时它是 102 bytes

function C(k){return(document.cookie.match('(^|; )'+k+'=([^;]*)')||0)[2]}

范围仅在输入函数时创建。因此,您可以收集您的两个 var 声明。
@xavierm02 - 什么?我假设您正在谈论 read_cookie(k,r) 部分,但不确定您的评论是什么:) 它的重点是将 r 定义为未定义,从而节省输入 var r 的几个字节
给你:jsperf.com/pre-increment-vs-post-increment我是对的,++i 更快,至少如果你得到前后的值。这就是你在 for 循环中所做的。
如果 cookie 值中包含等号,则此函数不会返回 cookie 的完整值。您可以通过使用子字符串来更改此设置以删除 cookie 名称,而不是依赖拆分 cookies[C[0]] = c[i].substring(C[0].length + 1);
一件重要的事情:由于“cookies”的值被缓存,来自其他窗口或选项卡的新 cookie 或更改的 cookie 将不可见。您可以通过将 document.cookie 中的字符串值存储在变量中并检查每次访问时它是否未更改来避免此问题。
J
Jeffery To

假设

基于这个问题,我相信这个功能的一些假设/要求包括:

它将用作库函数,因此可以放入任何代码库;

因此,它需要在许多不同的环境中工作,即使用遗留的 JS 代码、各种质量级别的 CMS 等;

为了与其他人编写的代码和/或您无法控制的代码进行互操作,该函数不应对 cookie 名称或值的编码方式做出任何假设。用字符串“foo:bar[0]”调用函数应该返回一个名为“foo:bar[0]”的cookie(字面意思);

在页面生命周期的任何时候,都可以写入新的 cookie 和/或修改现有的 cookie。

在这些假设下,很明显 encodeURIComponent / decodeURIComponent 不应使用;这样做假定设置 cookie 的代码也使用这些函数对其进行了编码。

如果 cookie 名称可以包含特殊字符,则正则表达式方法会出现问题。 jQuery.cookie 通过在存储 cookie 时编码 cookie 名称(实际上是名称和值)并在检索 cookie 时解码名称来解决这个问题。正则表达式解决方案如下。

除非您只读取您完全控制的 cookie,否则建议直接从 document.cookie 读取 cookie 并且不缓存结果,因为没有办法知道缓存是否无效再次阅读 document.cookie

(虽然访问和解析 document.cookies 会比使用缓存稍微慢一些,但它不会像读取 DOM 的其他部分那么慢,因为 cookie 在 DOM/渲染树中不发挥作用。)

基于循环的函数

这是基于 PPK(基于循环)功能的 Code Golf 答案:

function readCookie(name) {
    name += '=';
    for (var ca = document.cookie.split(/;\s*/), i = ca.length - 1; i >= 0; i--)
        if (!ca[i].indexOf(name))
            return ca[i].replace(name, '');
}

缩小后为 128 个字符(不包括函数名):

function readCookie(n){n+='=';for(var a=document.cookie.split(/;\s*/),i=a.length-1;i>=0;i--)if(!a[i].indexOf(n))return a[i].replace(n,'');}

基于正则表达式的函数

更新:如果你真的想要一个正则表达式解决方案:

function readCookie(name) {
    return (name = new RegExp('(?:^|;\\s*)' + ('' + name).replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&') + '=([^;]*)').exec(document.cookie)) && name[1];
}

在构造 RegExp 对象之前,此 escapes cookie 名称中的任何特殊字符。缩小后,共有 134 个字符(不包括函数名):

function readCookie(n){return(n=new RegExp('(?:^|;\\s*)'+(''+n).replace(/[-[\]{}()*+?.,\\^$|#\s]/g,'\\$&')+'=([^;]*)').exec(document.cookie))&&n[1];}

正如 Rudu 和 cwolves 在评论中指出的那样,正则表达式转义正则表达式可以缩短几个字符。我认为保持转义正则表达式一致(您可能在其他地方使用它)会很好,但他们的建议值得考虑。

笔记

这两个函数都不会处理 nullundefined,即如果有一个名为“null”的 cookie,readCookie(null) 将返回它的值。如果您需要处理这种情况,请相应地调整代码。


V
VKen

来自谷歌分析 ga.js 的代码

function c(a){
    var d=[],
        e=document.cookie.split(";");
    a=RegExp("^\\s*"+a+"=\\s*(.*?)\\s*$");
    for(var b=0;b<e.length;b++){
        var f=e[b].match(a);
        f&&d.push(f[1])
    }
    return d
}

我更改了最后一行 return d[0];,然后使用 if (c('EXAMPLE_CK') == null) 检查是否未定义 cookie。
S
Simon Steinberger

这个怎么样?

function getCookie(k){var v=document.cookie.match('(^|;) ?'+k+'=([^;]*)(;|$)');return v?v[2]:null}

计算 89 个字节,没有函数名。


J
Joyce Babu

以下函数将允许区分空字符串和未定义的 cookie。与此处的其他一些答案不同,未定义的 cookie 将正确返回 undefined 而不是空字符串。

function getCookie(name) {
    return (document.cookie.match('(^|;) *'+name+'=([^;]*)')||[])[2];
}

以上在我检查的所有浏览器上对我来说都很好,但正如@vanovm 在评论中提到的那样,根据规范,键/值可能被空格包围。因此,以下更符合标准。

function getCookie(name) {
    return (document.cookie.match('(?:^|;)\\s*'+name.trim()+'\\s*=\\s*([^;]*?)\\s*(?:;|$)')||[])[1];
}

m
mfalade

来了..干杯!

function getCookie(n) {
    let a = `; ${document.cookie}`.match(`;\\s*${n}=([^;]+)`);
    return a ? a[1] : '';
}

请注意,我使用 ES6 的模板字符串来组成正则表达式。


佚名

this 在您可以读取、写入、覆盖和删除 cookie 的对象中。

var cookie = {
    write : function (cname, cvalue, exdays) {
        var d = new Date();
        d.setTime(d.getTime() + (exdays*24*60*60*1000));
        var expires = "expires="+d.toUTCString();
        document.cookie = cname + "=" + cvalue + "; " + expires;
    },
    read : function (name) {
        if (document.cookie.indexOf(name) > -1) {
            return document.cookie.split(name)[1].split("; ")[0].substr(1)
        } else {
            return "";
        }
    },
    delete : function (cname) {
        var d = new Date();
        d.setTime(d.getTime() - 1000);
        var expires = "expires="+d.toUTCString();
        document.cookie = cname + "=; " + expires;
    }
};

J
Jeff Avallone

这两个函数在读取 cookie 方面看起来同样有效。不过,您可以减少几个字节(它确实进入了 Code Golf 领域):

function readCookie(name) {
    var nameEQ = name + "=", ca = document.cookie.split(';'), i = 0, c;
    for(;i < ca.length;i++) {
        c = ca[i];
        while (c[0]==' ') c = c.substring(1);
        if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length);
    }
    return null;
}

我所做的只是将所有变量声明折叠到一个 var 语句中,删除对子字符串的调用中不必要的第二个参数,并将一个 charAt 调用替换为数组取消引用。

这仍然没有您提供的第二个函数那么短,但即使这样也可以删除几个字节:

function read_cookie(key)
{
    var result;
    return (result = new RegExp('(^|; )' + encodeURIComponent(key) + '=([^;]*)').exec(document.cookie)) ? result[2] : null;
}

我把正则表达式中的第一个子表达式改成捕获子表达式,并将result[1]部分改成result[2]来配合这个变化;还删除了结果 [2] 周围不必要的括号。


A
Abhi Beckert

要真正消除尽可能多的膨胀,请考虑根本不使用包装函数:

try {
    var myCookie = document.cookie.match('(^|;) *myCookie=([^;]*)')[2]
} catch (_) {
    // handle missing cookie
}

只要您熟悉 RegEx,该代码就相当干净且易于阅读。


g
gogo

要在 Map 中按名称访问所有 cookie:

const cookies = "a=b ; c = d ;e=";
const map = cookies.split(";").map((s) => s.split("=").map((s) => s.trim())).reduce((m, [k, v]) => (m.set(k, v), m), new Map());
console.log(map); //Map(3) {'a' => 'b', 'c' => 'd', 'e' => ''}
map.get("a"); //returns "b"
map.get("c"); //returns "d"
map.get("e"); //returns ""

A
Adam

(编辑:首先发布了错误的版本......然后是一个非功能版本。更新为当前版本,它使用与第二个示例非常相似的 unparam 函数。)

第一个例子 cwolves 中的好主意。我在两者的基础上构建了一个相当紧凑的 cookie 读取/写入功能,可以跨多个子域工作。想我会分享,以防其他人跑过这个线程寻找那个。

(function(s){
  s.strToObj = function (x,splitter) {
    for ( var y = {},p,a = x.split (splitter),L = a.length;L;) {
      p = a[ --L].split ('=');
      y[p[0]] = p[1]
    }
    return y
  };
  s.rwCookie = function (n,v,e) {
    var d=document,
        c= s.cookies||s.strToObj(d.cookie,'; '),
        h=location.hostname,
        domain;
    if(v){
      domain = h.slice(h.lastIndexOf('.',(h.lastIndexOf('.')-1))+1);
      d.cookie = n + '=' + (c[n]=v) + (e ? '; expires=' + e : '') + '; domain=.' + domain + '; path=/'
    }
    return c[n]||c
  };
})(some_global_namespace)

如果你什么都不传递 rwCookie,它会将所有 cookie 放入 cookie 存储中

向 rwCookie 传递了一个 cookie 名称,它从存储中获取该 cookie 的值

传递一个 cookie 值,它写入 cookie 并将值放入存储中

到期默认为会话,除非您指定一个


F
Félix Saparelli

使用 cwolves 的答案,但不使用闭包或预先计算的哈希:

// Golfed it a bit, too...
function readCookie(n){
  var c = document.cookie.split('; '),
      i = c.length,
      C;

  for(; i>0; i--){
     C = c[i].split('=');
     if(C[0] == n) return C[1];
  }
}

...并缩小...

function readCookie(n){var c=document.cookie.split('; '),i=c.length,C;for(;i>0;i--){C=c[i].split('=');if(C[0]==n)return C[1];}}

...等于 127 字节。


P
Paul Roub

这是使用 javascript 字符串函数的最简单的解决方案。

document.cookie.substring(document.cookie.indexOf("COOKIE_NAME"), 
                          document.cookie.indexOf(";", 
                          document.cookie.indexOf("COOKIE_NAME"))).
  substr(COOKIE_NAME.length);

COOKIE_NAME 是字符串还是变量?你的例子没有意义...
R
RAM

只是为了参加比赛,这是我的建议:

function getCookie(name) {
   const cookieDict = document.cookie.split(';')
        .map((x)=>x.split('='))
        .reduce((accum,current) => { accum[current[0]]=current[1]; return accum;}, Object());
    return cookieDict[name];
}

上面的代码生成了一个 dict,它将 cookie 存储为键值对(即 cookieDict),然后访问属性 name 以检索 cookie。

这可以有效地表达为单线,但这仅适用于勇敢者:

document.cookie.split(';').map((x)=>x.split('=')).reduce((accum,current) => { accum[current[0]]=current[1]; return accum;}, {})[name]

绝对最好的方法是在页面加载时生成 cookieDict,然后在整个页面生命周期中通过调用 cookieDict['cookiename'] 访问单个 cookie。


T
T. Smith

此功能不适用于 chrome > 80 等较旧的浏览器。

const getCookieValue = (name) => (
  document.cookie.match('(^|;)\\s*' + name + '\\s*=\\s*([^;]+)')?.pop() || ''
)

我通过使用这个函数来解决它,而不是在 cookie 丢失时返回 undefined:

function getCookie(name) {
  // Add the = sign
  name = name + '=';

  // Get the decoded cookie
  var decodedCookie = decodeURIComponent(document.cookie);

  // Get all cookies, split on ; sign
  var cookies = decodedCookie.split(';');

  // Loop over the cookies
  for (var i = 0; i < cookies.length; i++) {
    // Define the single cookie, and remove whitespace
    var cookie = cookies[i].trim();

    // If this cookie has the name of what we are searching
    if (cookie.indexOf(name) == 0) {
      // Return everything after the cookies name
      return cookie.substring(name.length, cookie.length);
    }
  }
}

学分:https://daily-dev-tips.com/posts/vanilla-javascript-cookies-%F0%9F%8D%AA/


M
Marco Concas

您可以验证 cookie 是否存在以及它是否具有定义的值:

function getCookie(cookiename) {
    if (typeof(cookiename) == 'string' && cookiename != '') {
        const COOKIES = document.cookie.split(';');
        for (i = 0; i < COOKIES.length; i++) {
            if (COOKIES[i].trim().startsWith(cookiename)) {
                return COOKIES[i].split('=')[1];
            }
        }
    }

    return null;
}

const COOKIE_EXAMPLE = getCookie('example');
if (COOKIE_EXAMPLE == 'stackoverflow') { ... }
// If is set a cookie named "example" with value "stackoverflow"
if (COOKIE_EXAMPLE != null) { ... }
// If is set a cookie named "example" ignoring the value

如果 cookie 不存在,它将返回 null。


H
Henry

现在是 2022 年,除了 Internet Explorer 之外的所有东西都支持 URLSearchParams API (^1) 和 String.prototype.replaceAll API (^2),所以我们可以可怕地 (ab) 使用它们:

const cookies = new URLSearchParams(document.cookie.replaceAll('&', '%26').replaceAll('; ', '&'));
cookies.get('cookie name'); // returns undefined if not set, string otherwise

M
Mariano Ruiz

如果不存在,则获取 cookie 值或 undefined

document
  .cookie
  .split('; ')
  .filter(row => row.startsWith('cookie_name='))
  .map(c=>c.split('=')[1])[0];