前端开发 jQuery.cookie.js源码解析

前端开发 浏览器缓存|现代前端开发流程图|前端项目开发文档模板

关于cookie操作相关函数,不少人写过封装。

这里分析一下jquery插件jquery.cookie.js的源码。

源码与介绍网址:
https://github.com/carhartl/jquery-cookie#readme

题外话,代码比较简单,几分钟就看完了。写此文的目的,就当是看完,总结,一点沉淀罢了。
以后可能还会分析这种篇幅比较小的源码(300行以内的)

正文开始

cookie是什么?
这都不知道的话,请百度。或者犀牛书第20章第2节(586页),里面同样也封装了增删改查的操作方法。

我这里大致做一点准备。
浏览器暴露cookie的操作方式?

document.cookie
读取的话,拿回的是一个字符串,大致长这么个样:

“name=laoyao; age=18″
设置的话,如下
[quote]document.cookie=”name=laoyao; age=18″;
document.cookie=”name=xxx”;[/quote]
得到的结果是

“name=xxx; age=18”
结果并不是

“name=xxx”
这种set方式,看起来比较奇怪,有点像重置似的,但是,确实不是。

再看看cookie一些属性(具体含义请看书,亲)
[quote]max-age :有效期(ie不支持,故源码中没有采用此属性来做删除)
expire :过期时间
domain :域
path :路径
secure :网络传递形式是否采取安全的形式[/quote]
设置这些属性值的方式也是类似的

document.cookie=”name=laoyao; age=18; path=/; expire=Thu, 19 Feb 2016 09:08:15 GMT”;
先看api设计(github上面抄过来的)

设置
[quote]$.cookie(‘name’, ‘value’);
$.cookie(‘name’, ‘value’, { expires: 7 });
$.cookie(‘name’, ‘value’, { expires: 7, path: ‘/’ });[/quote]
读取
[quote]$.cookie(‘name’); // => “value”
$.cookie(‘nothing’); // => undefined
$.cookie(); // => { “name”: “value” }[/quote]
删除
[quote]$.removeCookie(‘name’); // => true
$.removeCookie(‘nothing’); // => false

要删除不同域和不同路径下的一个key,要指定domain和path
$.cookie(‘name’, ‘value’, { path: ‘/’ });
// This won’t work!
$.removeCookie(‘name’); // => false
// This will work!
$.removeCookie(‘name’, { path: ‘/’ }); // => true
[/quote]
其他的配置属性啥的先不看了。后面看具体源码时,具体分析。

第一步. ok,先搭框架

(function(factory) {
    if (typeof define === “function” && define.amd) {
        // AMD
        define([“jquery”], factory);
    } else if (typeof exports === “object”) {
        // CommonJS
        factory(require(“jquery”));
    } else {
        // 浏览器
        factory(jquery);
    }
})(function($) {
    //set 和get
    $.cookie = function() {};
    //remove
    $.removeCookie = function() {};
});

这个是封装一般框架(jq插件以及其他库)的通用写法。

看起来确实挺吓人的,这里我大致说一下
总体是个匿名函数自执行

(function(factory){…})(fun);
如果这个也看不懂的话,再简化一下
[quote]var f = function(factory){…};
f(fun);[/quote]
其中fun是一个函数,此函数需要传进jquery的$。
[quote]var fun = function($) {

//set 和get
$.cookie = function(){};

//remove
$.removeCookie = function(){};

};[/quote]
f中的函数体最后一个else里面的
factory(jquery);
这句话正是调用了fun并且传进了jquery。
第一个if是支持amd模块用法的,
第二个if是支持node.js和CommonJS的
这个就是整体框架。

第二步. 根据设计好的api,来编写代码

设置和读取cookie的实现,注意这里不是插件原封不动的源码

// set 和get
$.cookie = function(key, value, options) {
    // 首先判断出是set还是get
    // set cookie
    if (value !== undefined) {
        // 拷贝options
        options = $.extend({}, options)
        // 如果设置了过期时间
        if (typeof options.expires === ‘number’) {
            // expires表示天数,过期时间为当前时间加上expires天
            var days = options.expires,
                t = (options.expires = new Date())
            t.setTime(+t + days * 86400 * 1000)
        }
        // 拼接cookie字符串
        return (document.cookie = [
            encodeURIComponent(key),
            ‘=’,
            String(value),
            options.expires ? ‘; expires=’ + options.expires.toUTCString() : ”,
            options.path ? ‘; path=’ + options.path : ”,
            options.domain ? ‘; domain=’ + options.domain : ”,
            options.secure ? ‘; secure’ : ”,
        ].join(”))
    }
    // 不用写else分支,因为上if里面最后是return
    // get cookie
    var result = key ? undefined : {}
    //比如把”name=laoyao; age=18″,变成[“name=laoyao”,”age=18″]
    var cookies = document.cookie ? document.cookie.split(‘; ‘) : []
    for (var i = 0, l = cookies.length; i < l; i++) {
        //把”name=laoyao”,变成了[name,laoyao],把”age=18″,变成了[age,18]
        var parts = cookies[i].split(‘=’)
        var name = decodeURIComponent(parts.shift())
        //如果是查询某个key
        if (key && key === name) {
            result = value
            break
        }
        //查询所有
        if (!key) {
            result[name] = parts
        }
    }
    return result
}

稍微解释下,
我这里写的总体逻辑还是蛮清晰的,
先判断出是set还是get,
如果是set就拼字符串,用的数组来拼的,没用加号。
注意,这里键值进行了编码。下面get时就得解码。

删除cookie实现很简单,将过期时间设置为当前时间之前即可。

// remove
$.removeCookie = function(key, options) {
    // 如果没有该key直接返回了
    if ($.cookie(key) === undefined) {
        return false
    }
    //通过设置过期时间为前一天,来达到删除的目的
    $.cookie(key, ”, $.extend({}, options, { expires: -1 }))
    //返回是否已经删除
    return !$.cookie(key)
}

第三步,扩充api以及bug对应

这里就直接贴源码了。
源码

(function(factory) {
    if (typeof define === ‘function’ && define.amd) {
        // AMD
        define([‘jquery’], factory)
    } else if (typeof exports === ‘object’) {
        // CommonJS
        factory(require(‘jquery’))
    } else {
        // Browser globals
        factory(jQuery)
    }
})(function($) {
    var pluses = /\+/g
    function encode(s) {
        return config.raw ? s : encodeURIComponent(s)
    }
    function decode(s) {
        return config.raw ? s : decodeURIComponent(s)
    }
    function stringifyCookieValue(value) {
        return encode(config.json ? JSON.stringify(value) : String(value))
    }
    function parseCookieValue(s) {
        if (s.indexOf(‘”‘) === 0) {
            // This is a quoted cookie as according to RFC2068, unescape…
            s = s
                .slice(1, -1)
                .replace(/\\”/g, ‘”‘)
                .replace(/\\\\/g, ‘\\’)
        }
        try {
            // Replace server-side written pluses with spaces.
            // If we can’t decode the cookie, ignore it, it’s unusable.
            // If we can’t parse the cookie, ignore it, it’s unusable.
            s = decodeURIComponent(s.replace(pluses, ‘ ‘))
            return config.json ? JSON.parse(s) : s
        } catch (e) {}
    }
    function read(s, converter) {
        var value = config.raw ? s : parseCookieValue(s)
        return $.isFunction(converter) ? converter(value) : value
    }
    var config = ($.cookie = function(key, value, options) {
        // Write
        if (value !== undefined && !$.isFunction(value)) {
            options = $.extend({}, config.defaults, options)
            if (typeof options.expires === ‘number’) {
                var days = options.expires,
                    t = (options.expires = new Date())
                t.setTime(+t + days * 864e5)
            }
            return (document.cookie = [
                encode(key),
                ‘=’,
                stringifyCookieValue(value),
                options.expires ? ‘; expires=’ + options.expires.toUTCString() : ”, // use expires attribute, max-age is not supported by IE
                options.path ? ‘; path=’ + options.path : ”,
                options.domain ? ‘; domain=’ + options.domain : ”,
                options.secure ? ‘; secure’ : ”,
            ].join(”))
        }
        // Read
        var result = key ? undefined : {}
        // To prevent the for loop in the first place assign an empty array
        // in case there are no cookies at all. Also prevents odd result when
        // calling $.cookie().
        var cookies = document.cookie ? document.cookie.split(‘; ‘) : []
        for (var i = 0, l = cookies.length; i < l; i++) {
            var parts = cookies[i].split(‘=’)
            var name = decode(parts.shift())
            var cookie = parts.join(‘=’)
            if (key && key === name) {
                // If second argument (value) is a function it’s a converter…
                result = read(cookie, value)
                break
            }
            // Prevent storing a cookie that we couldn’t decode.
            if (!key && (cookie = read(cookie)) !== undefined) {
                result[name] = cookie
            }
        }
        return result
    })
    config.defaults = {}
    $.removeCookie = function(key, options) {
        if ($.cookie(key) === undefined) {
            return false
        }
        // Must not alter options, thus extending a fresh object…
        $.cookie(key, ”, $.extend({}, options, { expires: -1 }))
        return !$.cookie(key)
    }
})

比之前的代码多了一些东西,具体来看看
1.内部函数encode和decode
根据$.cookie是否配置raw属性来决定是否要对key进行编解码。开头网址有介绍。
2.内部函数stringifyCookieValue
根据$.cookie是否配置json属性来决定是否key对应value可以存json对象字符串。

源码中拼字符串时,源码对value,进行了此操作
stringifyCookieValue(value)
3.内部函数parseCookieValue
其作用是当读取cookie时,如果对应的value是一个json字符串。要进行一些解析处理,便于JSON.parse。
此函数看起来还是比较麻烦点的
把所有的\”替换成”,
把所有的\\替换成\,
把所有的加号替换成了空格,注释里说服务器端过来的是加号。(没测试过)
4.内部函数read,是在get cookie实现中调用的。
这里涉及到一个东西,因为cookie取出来的都是字符串,我想要数字怎么办。
能不能取的时候,就带回数字。如下
$.cookie(‘foo’, ’42’);
$.cookie(‘foo’, Number); // => 42
我传进一个Number函数,表示取出一个数字。
read就是判断value是否是函数,如果是函数就调用,converter(value)

注意上面是取,所以在判断是否是set时,源码中if语句是这么写的
if (value !== undefined && !$.isFunction(value))
要求第二参数不能为函数的。

5.最后来看一下源码中get cookie的for语句实现

for (var i = 0, l = cookies.length; i < l; i++) {
    var parts = cookies[i].split(‘=’)
    var name = decode(parts.shift())
    //万一值中本来就有等号呢,所以还得拼接回去
    var cookie = parts.join(‘=’)
    if (key && key === name) {
        // If second argument (value) is a function it’s a converter…
        //调用read拿到正确的值
        result = read(cookie, value)
        break
    }
    // Prevent storing a cookie that we couldn’t decode.
    if (!key && (cookie = read(cookie)) !== undefined) {
        result[name] = cookie
    }
}

一般公司前端开发流程|现代前端开发流程图|web前端的整个开发流程图

赞(0)
前端开发者 » 前端开发 jQuery.cookie.js源码解析
64K

评论 抢沙发

评论前必须登录!