前端开发 浏览器缓存|现代前端开发流程图|前端项目开发文档模板
关于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
} else if (typeof exports === “object”) {
// CommonJS
} else {
// 浏览器
}
})(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前端的整个开发流程图
评论前必须登录!
注册