乐趣区

cookie工具函数封装

cookie 工具函数

let CookieUtil = {get: function (name){let cookieName = encodeURIComponent(name) + "=",
            cookieStart = document.cookie.indexOf(cookieName),
            cookieValue = null;
        if (cookieStart > -1){let cookieEnd = document.cookie.indexOf(";", cookieStart);
            if (cookieEnd == -1){cookieEnd = document.cookie.length;}
            cookieValue = decodeURIComponent(document.cookie.substring(cookieStart + cookieName.length, cookieEnd));
        }
        return cookieValue;
    },
    set: function (name, value, expires, path, domain, secure) {let cookieText = encodeURIComponent(name) + "=" + encodeURIComponent(value);
        if (expires instanceof Date) {cookieText += "; expires=" + expires.toGMTString();
        }
        if (path) {cookieText += "; path=" + path;}
        if (domain) {cookieText += "; domain=" + domain;}
        if (secure) {cookieText += "; secure";}
        document.cookie = cookieText;
    },
    unset: function (name, path, domain, secure){this.set(name, "", new Date(0), path, domain, secure);
    }
};

CookieUtil.get() 方法根据 cookie 的名字获取相应的值。它会在 document.cookie 字符串中查找 cookie 名加上等于号的位置。如果找到了,那么使用 indexOf() 查找该位置之后的第一个分号(表示了该 cookie 的结束位置)。如果没有找到分号,则表示该 cookie 是字符串中的最后一个,则余下的字符串都是 cookie 的值。该值使用 decodeURIComponent() 进行解码并最后返回。如果没有发现 cookie,则返回 null。

CookieUtil.set() 方法在页面上设置一个 cookie,接收如下几个参数:cookie 的名称,cookie 的值,可选的用于指定 cookie 何时应被删除的 Date 对象,cookie 的可选的 URL 路径,可选的域,以及可选的表示是否要添加 secure 标志的布尔值。参数是按照它们的使用频率排列的,只有头两个是必需的。在这个方法中,名称和值都使用 encodeURIComponent() 进行了 URL 编码,并检查其他选项。如果 expires 参数是 Date 对象,那么会使用 Date 对象的 toGMTString() 方法正确格式化 Date 对象,并添加到 expires 选项上。方法的其他部分就是构造 cookie 字符串并将其设置到 document.cookie 中。没有删除已有 cookie 的直接方法。所以,需要使用相同的路径、域和安全选项再次设置 cookie,并将失效时间设置为过去的时间。

CookieUtil.unset() 方法可以处理这种事情。它接收 4 个参数:要删除的 cookie 的名称、可选的路径参数、可选的域参数和可选的安全参数。这些参数加上空字符串并设置失效时间为 1970 年 1 月 1 日(初始化为 0ms 的 Date 对象的值),传给 CookieUtil.set()。这样就能确保删除 cookie

// 设置 cookie
CookieUtil.set("name", "Nicholas");
CookieUtil.set("book", "Professional JavaScript");
// 读取 cookie 的值
alert(CookieUtil.get("name")); //"Nicholas"
alert(CookieUtil.get("book")); //"Professional JavaScript"
// 删除 cookie
CookieUtil.unset("name");
CookieUtil.unset("book");
// 设置 cookie,包括它的路径、域、失效日期
CookieUtil.set("name", "Nicholas", "/books/projs/", "www.wrox.com", new Date("January 1, 2010"));
// 删除刚刚设置的 cookie
CookieUtil.unset("name", "/books/projs/", "www.wrox.com");
// 设置安全的 cookie
CookieUtil.set("name", "Nicholas", null, null, null, true);

子 cookie

为了绕开浏览器的单域名下的 cookie 数限制,一些开发人员使用了一种称为子 cookie(subcookie)的概念。子 cookie 是存放在单个 cookie 中的更小段的数据。也就是使用 cookie 值来存储多个名称值对儿。子 cookie 最常见的的格式如下所示。

name=name1=value1&name2=value2&name3=value3&name4=value4&name5=value5

子 cookie 一般也以查询字符串的格式进行格式化。然后这些值可以使用单个 cookie 进行存储和访问,而非对每个名称键值对儿使用不同的 cookie 存储。最后网站或者 Web 应用程序可以无需达到单域名 cookie 上限也可以存储更加结构化的数据。

为了更好地操作子 cookie,必须建立一系列新方法。子 cookie 的解析和序列化会因子 cookie 的期望用途而略有不同并更加复杂些。例如,要获得一个子 cookie,首先要遵循与获得 cookie 一样的基本步骤,但是在解码 cookie 值之前,需要按如下方法找出子 cookie 的信息。

let SubCookieUtil = {get: function (name, subName){let subCookies = this.getAll(name);
        if (subCookies){return subCookies[subName];
        } else {return null;}
    },
    getAll: function(name){let cookieName = encodeURIComponent(name) + "=",
            cookieStart = document.cookie.indexOf(cookieName),
            cookieValue = null,
            cookieEnd,
            subCookies,
            i,
            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 (i=0, len=subCookies.length; i < len; i++){parts = subCookies[i].split("=");
                    result[decodeURIComponent(parts[0])] =
                    decodeURIComponent(parts[1]);
                }
                return result;
            }
        }
        return null;
    },
    set: function (name, subName, value, expires, path, domain, secure) {let subcookies = this.getAll(name) || {};
        subcookies[subName] = value;
        this.setAll(name, subcookies, expires, path, domain, secure);
    },
    setAll: function(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(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;
    },
    unset: function (name, subName, path, domain, secure){let subcookies = this.getAll(name);
        if (subcookies){delete subcookies[subName];
            this.setAll(name, subcookies, null, path, domain, secure);
        }
    },
    unsetAll: function(name, path, domain, secure){this.setAll(name, null, new Date(0), path, domain, secure);
    }
};
  1. get() 获取单个子 cookie 的值,接收两个参数:cookie 的名字和子 cookie 的名字。其实就是调用 getAll() 获取所有的子 cookie,然后只返回所需的那一个(如果 cookie 不存在则返回 null)。
  2. getAll() 获取所有子 cookie 并将它们放入一个对象中返回,对象的属性为子 cookie 的名称,对应值为子 cookie 对应的值。
  3. set() 方法接收 7 个参数:cookie 名称、子 cookie 名称、子 cookie 值、可选的 cookie 失效日期或时间的 Date 对象、可选的 cookie 路径、可选的 cookie 域和可选的布尔 secure 标志。所有的可选参数都是作用于 cookie 本身而非子 cookie。在这个方法中,第一步是获取指定 cookie 名称对应的所有子 cookie。逻辑或操作符“||”用于当 getAll() 返回 null 时将 subcookies 设置为一个新对象。然后,在 subcookies 对象上设置好子 cookie 值并传给 setAll()。
  4. setAll() 方法接收 6 个参数:cookie 名称、包含所有子 cookie 的对象以及和 set() 中一样的 4 个可选参数。这个方法使用 for-in 循环遍历第二个参数中的属性。为了确保确实是要保存的数据,使用了 hasOwnProperty() 方法,来确保只有实例属性被序列化到子 cookie 中。由于可能会存在属性名为空字符串的情况,所以在把属性名加入结果对象之前还要检查一下属性名的长度。将每个子 cookie 的名值对儿都存入 subcookieParts 数组中,以便稍后可以使用 join() 方法以 & 号组合起来。剩下的方法则和 CookieUtil.set() 一样。
  5. unset() 方法用于删除某个 cookie 中的单个子 cookie 而不影响其他的;
  6. unsetAll() 方法则等同于 CookieUtil.unset(),用于删除整个 cookie。

和 set() 及 setAll() 一样,路径、域和 secure 标志必须和之前创建的 cookie 包含的内容一致。针对整个 cookie 的失效日期则可以在任何一个单独的子 cookie 写入的时候同时设置。

SubCookieUtil.getAll() 方法和 CookieUtil.get() 在解析 cookie 值的方式上非常相似。区别在于 cookie 的值并非立即解码,而是先根据 & 字符将子 cookie 分割出来放在一个数组中,每一个子 cookie 再根据等于号分割,这样在 parts 数组中的前一部分便是子 cookie 名,后一部分则是子 cookie 的值。这两个项目都要使用 decodeURIComponent() 来解码,然后放入 result 对象中,最后作为方法的返回值。如果 cookie 不存在,则返回 null。

// 假设 document.cookie=data=name=Nicholas&book=Professional%20JavaScript
// 取得全部子 cookie
var 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
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"));
// 修改名字的值,并修改 cookie 的失效日期
SubCookieUtil.set("data", "name", "Michael", new Date("February 1, 2010"));
// 仅删除名为 name 的子 cookie
SubCookieUtil.unset("data", "name");
// 删除整个 cookie
SubCookieUtil.unsetAll("data");
退出移动版