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


193

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

很多时候,在构建独立脚本时(我没有任何外部依赖项),我发现自己添加了一个读取cookie的函数,并且通常回退到QuirksMode.orgreadCookie()方法(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;
}

请注意,这不是“ Code Golf”竞赛:我有理由减少我的readCookie函数的大小,并确保我的解决方案有效。


8
存储的cookie数据有点丑陋,因此处理它们的任何方法都可能也是如此。
mVChr

4
@mVChr认真。在什么时候决定应使用以分号分隔的字符串访问cookie?那什么时候是个好主意?
Yahel 2011年

7
为什么这个问题仍然悬而未决?为什么有悬赏金?您真的迫切希望节省5个字节吗???
马克·卡恩

cookieArr = document.cookie.split(';')。map(ck => {return {[ck.split('=')[0] .trim()]:ck.split('=')[1] }})
vladimir.gorea,

Answers:


196

比当前最佳投票答案更简短,更可靠,更高效:

function getCookieValue(a) {
    var b = document.cookie.match('(^|;)\\s*' + a + '\\s*=\\s*([^;]+)');
    return b ? b.pop() : '';
}

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

http://jsperf.com/get-cookie-value-regex-vs-array-functions

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

regex方法不仅在大多数浏览器中最快,而且功能最短。此外,应该指出的是,根据官方规范(RFC 2109),在document.cookie中用于分隔cookie的分号后的空格是可选的,并且可以提出不应依赖它的论点。此外,在等号(=)之前和之后都允许使用空格,并且可以提出这样的论点,即应将该潜在的空格纳入任何可靠的document.cookie解析器中。上面的正则表达式解决了上述两个空白条件。


4
我刚刚注意到,在Firefox中,我上面发布的regex方法不如循环方法那样高效。我之前运行的测试是在Chrome中完成的,其中regex方法的性能要比其他方法好一些。尽管如此,它仍然是解决所提问题的最短方法。
Mac

6
为什么要getCookieValue(a, b)带参数b
布伦特·沃什伯恩

15
Upvoted,而不是可读性...我花了一段时间才能找出什么ab做。
Gigi

9
聪明,但是愚蠢地以这种方式写入以节省1个字节。
松饼人

5
a参数不是正则表达式转义的,虽然它很有用,但并不安全。诸如此类的东西getCookieValue('.*')会返回任何随机cookie
Vitim.us

185

这将仅一次命中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;}

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

我已经将其归结为73 bytes,但公平地说,是82 bytes在命名时readCookie以及102 bytes随后添加时encodeURIComponent

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

我忘记了return语句,关闭并没有什么问题,嘘。
马克·卡恩

3
...什么?差异说你做到了。d.pr/sSte最后没有()调用,因此它正在定义一个匿名函数,但从未执行。
Yahel 2011年

2
在这里,您可以:jsperf.com/pre-increment-vs-post-increment我是对的,++ i更快,至少在您获得此值之前和之后都可以。这就是您在for循环中所做的。
xavierm02

1
一件重要的事情:由于缓存了“ cookies”的值,因此其他窗口或选项卡中的新cookie或更改的cookie将不可见。您可以通过将document.cookie中的字符串值存储在变量中并检查每次访问是否保持不变来避免此问题。
Andreas

2
@StijndeWitt -它是如何回答这个问题?73个字节不足以供您使用吗?:)我得到的最后一个答案下面的答案相同,除了一些空格检查外,大声笑
马克·卡恩

20

假设条件

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

  • 它将用作库函数,因此意在放入任何代码库中。
  • 因此,它将需要在许多不同的环境中工作,例如,使用旧版JS代码,各种质量级别的CMS等。
  • 为了与其他人编写的代码和/或不受您控制的代码进行互操作,该函数不应假设如何对cookie名称或值进行编码。用字符串调用该函数"foo:bar[0]"应返回一个名为“ foo:bar [0]”的cookie(从字面上看)。
  • 在页面生命周期的任何时候都可以编写新的cookie和/或修改现有的cookie

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

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

除非您只读取完全控制的cookie,否则建议直接读取cookiedocument.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对象之前,这会转义 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)将返回其值。如果需要处理这种情况,请相应地修改代码。


#\s替换Regex结束时有什么好处?通用替换可以进一步简化(-,除了转义的括号中没有意义):/[[\]{}()*+?.\\^$|]/g 它甚至可以与以下内容一起进行分拣encodeURIComponent/[()*.\\]/g
Rudu 2011年

@Rudu这个正则表达式转义的正则表达式来自Simon Willison,并且XRegExp库也使用。这是针对一般情况的(例如,如果您正在这样做,new RegExp('[' + str + ']')那么您想逃避-),因此可以缩短它是正确的。由于它只保存几个字节,并且转义多余的字符不会影响最终的正则表达式,因此我倾向于将其保持原样。
杰弗里(Jeffery)在

@Rudu encodeURIComponent()在这种情况下,我也避免使用,因为我认为OP正在寻找可以与任何cookie名称一起使用的通用函数。如果第三方代码设置了一个名为“ foo:bar”的cookie,则使用encodeURIComponent()将意味着尝试读取一个名为“ foo%3Abar”的cookie。
杰弗里(Jeffery)

我知道代码从何而来-它不是完美的(会a<tab>b变成a\<tab>b...,虽然不是,但这不是有效的转义符\<space>)。和#似乎已经JS正则表达式没有任何意义
Rudu

1
许多第三方库都使用encodeURIComponent,如果将cookie命名为foo=,则将其存储为foo%3D没有同样编码的名称,您将找不到它(您的代码将找不到它)-没有正确的答案。如果您可以控制cookie的放置方式,则可以简化答案/假设以帮助解决问题。最简单/最好的方法是仅使用a-zA-Z0-9作为Cookie名称,并避免整个encodeURIComponent和RegExp乱码。但您可能仍然需要担心decodeURIComponentcookie值,因为这是一个推荐的做法。
Rudu 2011年

14

谷歌分析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是否未定义。
electroid

9

这个怎么样?

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

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


聪明的答案。要点。应该得到更多的支持。k可以key为清楚起见而命名,但是无论如何。
GeroldBroser恢复了Monica's

谢谢:-)到目前为止,已接受的答案已更新,并且也包含此答案。但是压缩形式略多一点,只有73个字符。
Simon Steinberger

所以,我知道您的目标是简短。那么为什么还没有在您的站点列表编程拼图和代码高尔夫呢?玩得开心!而且,顺便说一句,我也是登山者。伯格·海尔!;)
GeroldBroser恢复Monica 17'Apr

4

来吧..干杯!

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

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


3

可以读取,写入,覆盖和删除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;
    }
};

1

就读取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]以与此更改一致;还删除了关于result [2]的不必要的括号。


1

要真正消除尽可能多的膨胀,请考虑完全不使用包装器功能:

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

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


0

(编辑:首先发布了错误的版本..并发布了一个不起作用的版本。更新为最新版本,它使用的unparam函数与第二个示例非常相似。)

第一个例子狼人的好主意。我在这两者上都建立了一个相当紧凑的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并将该值放入存储中
  • 过期默认为会话,除非您指定一个

0

使用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个字节。


0

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

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

0

以下函数将允许区分空字符串和未定义的cookie。未定义的Cookie会正确返回,undefined而不是空字符串,这与此处的其他一些答案不同。但这不适用于IE7及以下版本,因为它们不允许数组访问字符串索引。

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

为什么返回列表中的第三项?
nasch

因为我没有让第一组不被抓住。我已经更新了代码。
Joyce Babu
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.