为绕过浏览器对每个域cookie 数的限制,有些开发者提出了子cookie 的概念。子cookie 是在单个
cookie 存储的小块数据,本质上是使用cookie 的值在单个cookie 中存储多个名/值对。最常用的子cookie
模式如下:
name=name1=value1&name2=value2&name3=value3&name4=value4&name5=value5
子cookie 的格式类似于查询字符串。这些值可以存储为单个cookie,而不用单独存储为自己的名/
值对。结果就是网站或Web 应用程序能够在单域cookie 数限制下存储更多的结构化数据。
要操作子cookie,就需要再添加一些辅助方法。解析和序列化子cookie 的方式不一样,且因为对子
cookie 的使用而变得更复杂。比如,要取得某个子cookie,就需要先取得cookie,然后在解码值之前需
要先像下面这样找到子cookie:
class SubCookieUtil {
static get(name, subName) {
let subCookies = SubCookieUtil.getAll(name);
return subCookies ? subCookies[subName] : null;
}
static getAll(name) {
let cookieName = encodeURIComponent(name) + “=”,
cookieStart = document.cookie.indexOf(cookieName),
cookieValue = null,
cookieEnd,
subCookies,
parts,
result = {};
if (cookieStart > -1) {
cookieEnd = document.cookie.indexOf(“;”, cookieStart);
if (cookieEnd == -1) {
cookieEnd = document.cookie.length;
}
cookieValue = document.cookie.substring(cookieStart +
cookieName.length, cookieEnd);
if (cookieValue.length > 0) {
subCookies = cookieValue.split(“&”);
for (let i = 0, len = subCookies.length; i < len; i++) {
parts = subCookies[i].split(“=”);
result[decodeURIComponent(parts[0])] =
decodeURIComponent(parts[1]);
}
return result;
}
}
return null;
}
// 省略其他代码
};
取得子cookie 有两个方法:get()和getAll()。get()用于取得一个子cookie 的值,getAll()
用于取得所有子cookie,并以对象形式返回,对象的属性是子cookie 的名称,值是子cookie 的值。get()
方法接收两个参数:cookie 的名称和子cookie 的名称。这个方法先调用getAll()取得所有子cookie,
然后返回要取得的子cookie(如果不存在则返回null)。
SubCookieUtil.getAll()方法在解析cookie 值方面与CookieUtil.get()方法非常相似。不同
的是SubCookieUtil.getAll()方法不会立即解码cookie 的值,而是先用和号(&)拆分,将所有子
cookie 保存到数组。然后,再基于等号(=)拆分每个子cookie,使parts 数组的第一个元素是子cookie
的名称,第二个元素是子cookie 的值。两个元素都使用decodeURIComponent()解码,并添加到result
对象,最后返回result 对象。如果cookie 不存在则返回null。
可以像下面这样使用这些方法:
// 假设document.cookie=data=name=Nicholas&book=Professional%20JavaScript
// 取得所有子cookie
let data = SubCookieUtil.getAll(“data”);
alert(data.name); // “Nicholas”
alert(data.book); // “Professional JavaScript”
// 取得个别子cookie
alert(SubCookieUtil.get(“data”, “name”)); // “Nicholas”
alert(SubCookieUtil.get(“data”, “book”)); // “Professional JavaScript”
要写入子cookie,可以使用另外两个方法:set()和setAll()。这两个方法的实现如下:
class SubCookieUtil {
// 省略之前的代码
static set(name, subName, value, expires, path, domain, secure) {
let subcookies = SubCookieUtil.getAll(name) || {};
subcookies[subName] = value;
SubCookieUtil.setAll(name, subcookies, expires, path, domain, secure);
}
static setAll(name, subcookies, expires, path, domain, secure) {
let cookieText = encodeURIComponent(name) + “=”,
subcookieParts = new Array(),
subName;
for (subName in subcookies){
if (subName.length > 0 && subcookies.hasOwnProperty(subName)){
subcookieParts.push(
‘
e
n
c
o
d
e
U
R
I
C
o
m
p
o
n
e
n
t
(
s
u
b
N
a
m
e
)
=
{encodeURIComponent(subName)}=
encodeURIComponent(subName)={encodeURIComponent(subcookies[subName])}’);
}
}
if (cookieParts.length > 0) {
cookieText += subcookieParts.join(“&”);
if (expires instanceof Date) {
cookieText += ; expires=${expires.toGMTString()}
;
}
if (path) {
cookieText += ; path=${path}
;
}
if (domain) {
cookieText += ; domain=${domain}
;
}
if (secure) {
cookieText += “; secure”;
}
} else {
cookieText += ; expires=${(new Date(0)).toGMTString()}
;
}
document.cookie = cookieText;
}
// 省略其他代码
};
set()方法接收7 个参数:cookie 的名称、子cookie 的名称、子cookie 的值、可选的Date 对象用
于设置cookie 的过期时间、可选的cookie 路径、可选的cookie 域和可选的布尔值secure 标志。所有
可选的参数都作用于cookie 本身,而不是子cookie。为了在同一个cookie 中存储多个子cookie,路径、
域和secure 标志也必须相同。过期时间作用于整个cookie,可以在写入个别子cookie 时另行设置。在
这个方法内部,第一步是取得给定cookie 名称下包含的所有子cookie。逻辑或操作符(||)在这里用于在getAll()返回null 的情况下将subcookies 设置为新对象。然后,在subcookies 上设置完子
cookie 的值,再将参数传给setAll()。
setAll()方法接收6 个参数:cookie 的名称、包含所有子cookie 的对象,然后是set()方法中使
用的4 个可选参数。这个方法会在for-in 循环中迭代第二个参数的属性。为保证只存储合适的数据,
这里使用了hasOwnProperty()方法确保只有实例属性才会序列化为子cookie。因为存在属性名等于
空字符串的可能,所以在添加到subcookieParts 数组之前也要检查属性名的长度。subcookieParts
数组包含了子cookie 的名/值对,这样我们可以方便地使用join()方法用和号将它们拼接成字符串。剩
下的逻辑与CookieUtil.set()一样。
可以像下面这样使用这些方法:
// 假设document.cookie=data=name=Nicholas&book=Professional%20JavaScript
// 设置两个子cookie
SubCookieUtil.set(“data”, “name”, “Nicholas”);
SubCookieUtil.set(“data”, “book”, “Professional JavaScript”);
// 设置所有子cookie 并传入过期时间
SubCookieUtil.setAll(“data”, { name: “Nicholas”, book: “Professional JavaScript” },
new Date(“January 1, 2010”));
// 修改"name"的值并修改整个cookie 的过期时间
SubCookieUtil.set(“data”, “name”, “Michael”, new Date(“February 1, 2010”));
最后一组子cookie 相关的方法是要删除子cookie 的。常规cookie 可以通过直接设置过期时间为某
个过去的时间删除,但删除子cookie 没有这么简单。为了删除子cookie,需要先取得所有子cookie,把
要删除的那个删掉,然后再把剩下的子cookie 设置回去。下面是相关方法的实现:
class SubCookieUtil {
// 省略之前的代码
static unset(name, subName, path, domain, secure) {
let subcookies = SubCookieUtil.getAll(name);
if (subcookies){
delete subcookies[subName]; // 删除
SubCookieUtil.setAll(name, subcookies, null, path, domain, secure);
}
}
static unsetAll(name, path, domain, secure) {
SubCookieUtil.setAll(name, null, new Date(0), path, domain, secure);
}
}
这里定义的这两个方法有两个不同的目的。unset()方法用于从cookie 中删除一个子cookie,其他
子cookie 不受影响;而unsetAll()方法与CookieUtil.unset()一样,会删除整个cookie。与set()
和setAll()一样,路径、域和secure 标志必须与创建cookie 时使用的一样。可以像下面这样使用这
两个方法:
// 只删除"name"子cookie
SubCookieUtil.unset(“data”, “name”);
// 删除整个cookie
SubCookieUtil.unsetAll(“data”);
如果实际开发中担心碰到每个域的cookie 限制,则可以考虑使用子cookie 这个方案。此时要特别注
意cookie 的大小,不要超过对单个cookie 大小的限制。
javascript基础从小白到高手系列一千七百四十:子cookie
最新推荐文章于 2024-09-03 17:02:58 发布