在 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 函数的大小以及确保我拥有的解决方案是有效的感兴趣。
比当前投票最多的答案更短、更可靠、性能更高:
const getCookieValue = (name) => (
document.cookie.match('(^|;)\\s*' + name + '\\s*=\\s*([^;]+)')?.pop() || ''
)
此处显示了各种方法的性能比较:
关于方法的一些注意事项:
正则表达式方法不仅在大多数浏览器中是最快的,它也产生了最短的函数。另外应该指出的是,根据official spec (RFC 2109),分号后面的空格分隔在 document.cookie 中的 cookie 是可选的,并且可以提出不应该依赖它的论点。此外,等号 (=) 之前和之后允许有空格,并且可以提出一个参数,即这个潜在的空格应该被考虑到任何可靠的 document.cookie 解析器中。上面的正则表达式说明了上述两个空白条件。
这只会命中 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]}
read_cookie(k,r)
部分,但不确定您的评论是什么:) 它的重点是将 r
定义为未定义,从而节省输入 var r
的几个字节
cookies[C[0]] = c[i].substring(C[0].length + 1);
假设
基于这个问题,我相信这个功能的一些假设/要求包括:
它将用作库函数,因此可以放入任何代码库;
因此,它需要在许多不同的环境中工作,即使用遗留的 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 在评论中指出的那样,正则表达式转义正则表达式可以缩短几个字符。我认为保持转义正则表达式一致(您可能在其他地方使用它)会很好,但他们的建议值得考虑。
笔记
这两个函数都不会处理 null
或 undefined
,即如果有一个名为“null”的 cookie,readCookie(null)
将返回它的值。如果您需要处理这种情况,请相应地调整代码。
来自谷歌分析 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。
这个怎么样?
function getCookie(k){var v=document.cookie.match('(^|;) ?'+k+'=([^;]*)(;|$)');return v?v[2]:null}
计算 89 个字节,没有函数名。
以下函数将允许区分空字符串和未定义的 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];
}
来了..干杯!
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;
}
};
这两个函数在读取 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] 周围不必要的括号。
要真正消除尽可能多的膨胀,请考虑根本不使用包装函数:
try {
var myCookie = document.cookie.match('(^|;) *myCookie=([^;]*)')[2]
} catch (_) {
// handle missing cookie
}
只要您熟悉 RegEx,该代码就相当干净且易于阅读。
要在 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 ""
(编辑:首先发布了错误的版本......然后是一个非功能版本。更新为当前版本,它使用与第二个示例非常相似的 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 并将值放入存储中
到期默认为会话,除非您指定一个
使用 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 字节。
这是使用 javascript 字符串函数的最简单的解决方案。
document.cookie.substring(document.cookie.indexOf("COOKIE_NAME"),
document.cookie.indexOf(";",
document.cookie.indexOf("COOKIE_NAME"))).
substr(COOKIE_NAME.length);
COOKIE_NAME
是字符串还是变量?你的例子没有意义...
只是为了参加比赛,这是我的建议:
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。
此功能不适用于 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/
您可以验证 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。
现在是 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
如果不存在,则获取 cookie 值或 undefined
:
document
.cookie
.split('; ')
.filter(row => row.startsWith('cookie_name='))
.map(c=>c.split('=')[1])[0];
getCookieValue(a, b)
采用参数b
?a
和b
做了什么。a
参数不是正则表达式转义,虽然它很有用,但不安全。getCookieValue('.*')
之类的东西会返回任何随机 cookie