JS前端操作 Cookie源碼示例解析
引言
前端操作Cookie
的場(chǎng)景其實(shí)并不多見(jiàn),Cookie
也因?yàn)楦鞣N問(wèn)題被逐漸淘汰,但是我們不用Cookie
也可以學(xué)習(xí)一下它的思想,或者通過(guò)這次的源碼來(lái)學(xué)習(xí)其他的一些知識(shí)。
今天帶來(lái)的是:js-cookie
源碼分析
使用
根據(jù)README
,我們可以看到js-cookie
的使用方式:
// 設(shè)置 Cookies.set('name', 'value'); // 設(shè)置過(guò)期時(shí)間 Cookies.set('name', 'value', { expires: 7 }) // 獲取 Cookies.get('name') // => 'value' // 獲取所有 Cookies.get() // => { name: 'value' } // 獲取指定域名下 Cookies.get('foo', { domain: 'sub.example.com' }) // 刪除 Cookies.remove('name')
還有很多其他用和配置說(shuō)明,大家可以自己去看看。
源碼
js-cookie
的源碼并不多,src
目錄下的api.mjs
就是我們要分析的源碼,只有一百行左右。
/* eslint-disable no-var */ import assign from './assign.mjs' import defaultConverter from './converter.mjs' function init (converter, defaultAttributes) { function set (name, value, attributes) { if (typeof document === 'undefined') { return } attributes = assign({}, defaultAttributes, attributes) if (typeof attributes.expires === 'number') { attributes.expires = new Date(Date.now() + attributes.expires * 864e5) } if (attributes.expires) { attributes.expires = attributes.expires.toUTCString() } name = encodeURIComponent(name) .replace(/%(2[346B]|5E|60|7C)/g, decodeURIComponent) .replace(/[()]/g, escape) var stringifiedAttributes = '' for (var attributeName in attributes) { if (!attributes[attributeName]) { continue } stringifiedAttributes += '; ' + attributeName if (attributes[attributeName] === true) { continue } // Considers RFC 6265 section 5.2: // ... // 3. If the remaining unparsed-attributes contains a %x3B (";") // character: // Consume the characters of the unparsed-attributes up to, // not including, the first %x3B (";") character. // ... stringifiedAttributes += '=' + attributes[attributeName].split(';')[0] } return (document.cookie = name + '=' + converter.write(value, name) + stringifiedAttributes) } function get (name) { if (typeof document === 'undefined' || (arguments.length && !name)) { return } // To prevent the for loop in the first place assign an empty array // in case there are no cookies at all. var cookies = document.cookie ? document.cookie.split('; ') : [] var jar = {} for (var i = 0; i < cookies.length; i++) { var parts = cookies[i].split('=') var value = parts.slice(1).join('=') try { var found = decodeURIComponent(parts[0]) jar[found] = converter.read(value, found) if (name === found) { break } } catch (e) {} } return name ? jar[name] : jar } return Object.create( { set: set, get: get, remove: function (name, attributes) { set( name, '', assign({}, attributes, { expires: -1 }) ) }, withAttributes: function (attributes) { return init(this.converter, assign({}, this.attributes, attributes)) }, withConverter: function (converter) { return init(assign({}, this.converter, converter), this.attributes) } }, { attributes: { value: Object.freeze(defaultAttributes) }, converter: { value: Object.freeze(converter) } } ) } export default init(defaultConverter, { path: '/' }) /* eslint-enable no-var */
分析
js-cookie
的源碼并不多,我們先來(lái)看看導(dǎo)出的是什么:
export default init(defaultConverter, { path: '/' })
這里是直接導(dǎo)出了init
函數(shù)的返回值,我們來(lái)看看init
函數(shù)的返回值:
function init (converter, defaultAttributes) { // ... return Object.create( { set: set, get: get, remove: function (name, attributes) { set( name, '', assign({}, attributes, { expires: -1 }) ) }, withAttributes: function (attributes) { return init(this.converter, assign({}, this.attributes, attributes)) }, withConverter: function (converter) { return init(assign({}, this.converter, converter), this.attributes) } }, { attributes: { value: Object.freeze(defaultAttributes) }, converter: { value: Object.freeze(converter) } } ) }
這里是使用Object.create
創(chuàng)建了一個(gè)對(duì)象,這個(gè)對(duì)象有set
、get
、remove
、withAttributes
、withConverter
這幾個(gè)方法,這幾個(gè)方法都是在init
函數(shù)內(nèi)部定義的,我們來(lái)看看這幾個(gè)方法的實(shí)現(xiàn):
set
function set(name, value, attributes) { if (typeof document === 'undefined') { return } attributes = assign({}, defaultAttributes, attributes) if (typeof attributes.expires === 'number') { attributes.expires = new Date(Date.now() + attributes.expires * 864e5) } if (attributes.expires) { attributes.expires = attributes.expires.toUTCString() } name = encodeURIComponent(name) .replace(/%(2[346B]|5E|60|7C)/g, decodeURIComponent) .replace(/[()]/g, escape) var stringifiedAttributes = '' for (var attributeName in attributes) { if (!attributes[attributeName]) { continue } stringifiedAttributes += '; ' + attributeName if (attributes[attributeName] === true) { continue } // Considers RFC 6265 section 5.2: // ... // 3. If the remaining unparsed-attributes contains a %x3B (";") // character: // Consume the characters of the unparsed-attributes up to, // not including, the first %x3B (";") character. // ... stringifiedAttributes += '=' + attributes[attributeName].split(';')[0] } return (document.cookie = name + '=' + converter.write(value, name) + stringifiedAttributes) }
一行一行來(lái)看:
if (typeof document === 'undefined') { return }
首先判斷是否有document
對(duì)象,如果沒(méi)有則直接返回,這說(shuō)明js-cookie
只能在瀏覽器環(huán)境下使用。
attributes = assign({}, defaultAttributes, attributes)
然后合并配置項(xiàng),將defaultAttributes
和傳入的attributes
合并,這里的assign
大家直接理解為Object.assign
就好了。
if (typeof attributes.expires === 'number') { attributes.expires = new Date(Date.now() + attributes.expires * 864e5) } if (attributes.expires) { attributes.expires = attributes.expires.toUTCString() }
然后判斷expires
是否是一個(gè)數(shù)字,如果是數(shù)字則將其轉(zhuǎn)換為一個(gè)Date
對(duì)象;
這里的864e5
是一個(gè)常量,結(jié)尾的e5
代表后面加5個(gè)0,也就是86400000
表示一天的毫秒數(shù)。
然后判斷expires
是否存在,如果存在則將其轉(zhuǎn)換為UTC
時(shí)間。
name = encodeURIComponent(name) .replace(/%(2[346B]|5E|60|7C)/g, decodeURIComponent) .replace(/[()]/g, escape)
這里對(duì)name
進(jìn)行了編碼,然后將name
中的%
、(
、)
進(jìn)行了轉(zhuǎn)義。
escape
是一個(gè)內(nèi)置函數(shù),它的作用是將一個(gè)字符串轉(zhuǎn)換為UTF-8
編碼的字符串,這里的escape
是將(
、)
轉(zhuǎn)換為%28
、%29
。
參考:escape()
var stringifiedAttributes = '' for (var attributeName in attributes) { if (!attributes[attributeName]) { continue } stringifiedAttributes += '; ' + attributeName if (attributes[attributeName] === true) { continue } // Considers RFC 6265 section 5.2: // ... // 3. If the remaining unparsed-attributes contains a %x3B (";") // character: // Consume the characters of the unparsed-attributes up to, // not including, the first %x3B (";") character. // ... stringifiedAttributes += '=' + attributes[attributeName].split(';')[0] }
這里是將attributes
轉(zhuǎn)換為字符串,這里的stringifiedAttributes
是一個(gè)字符串,最后的結(jié)果是這樣的:
stringifiedAttributes = '; path=/; expires=Wed, 21 Oct 2015 07:28:00 GMT'
最后將name
、value
、stringifiedAttributes
拼接起來(lái),然后賦值給document.cookie
。
get
function get(name) { if (typeof document === 'undefined' || (arguments.length && !name)) { return } // To prevent the for loop in the first place assign an empty array // in case there are no cookies at all. var cookies = document.cookie ? document.cookie.split('; ') : [] var jar = {} for (var i = 0; i < cookies.length; i++) { var parts = cookies[i].split('=') var value = parts.slice(1).join('=') try { var found = decodeURIComponent(parts[0]) jar[found] = converter.read(value, found) if (name === found) { break } } catch (e) { } } return name ? jar[name] : jar }
get
方法的實(shí)現(xiàn)比較簡(jiǎn)單,主要是解析document.cookie
,然后將其轉(zhuǎn)換為一個(gè)對(duì)象,來(lái)逐行解析:
if (typeof document === 'undefined' || (arguments.length && !name)) { return }
對(duì)比于set
方法,這里多了一個(gè)(arguments.length && !name)
的判斷,這里是防止傳入空字符串的name
。
var cookies = document.cookie ? document.cookie.split('; ') : []
這里是將document.cookie
分割為一個(gè)數(shù)組,每一項(xiàng)是一個(gè)cookie
。
var jar = {} for (var i = 0; i < cookies.length; i++) { var parts = cookies[i].split('=') var value = parts.slice(1).join('=') }
這一步是只要cookie
的name
和value
,其他的一些額外附加信息都不需要。
try { var found = decodeURIComponent(parts[0]) jar[found] = converter.read(value, found) if (name === found) { break } } catch (e) { }
這里是將name
進(jìn)行了解碼,然后將name
和value
存儲(chǔ)到jar
對(duì)象中,如果傳入了name
,則在找到對(duì)應(yīng)的name
后就跳出循環(huán)。
return name ? jar[name] : jar
最后返回jar
對(duì)象,如果傳入了name
,則返回對(duì)應(yīng)的value
,否則返回整個(gè)jar
對(duì)象。
這里的核心是converter.read
,這個(gè)方法是用來(lái)解析value
的,這里的converter
是一個(gè)對(duì)象,它有兩個(gè)方法:
/* eslint-disable no-var */ export default { read: function (value) { if (value[0] === '"') { value = value.slice(1, -1) } return value.replace(/(%[\dA-F]{2})+/gi, decodeURIComponent) }, write: function (value) { return encodeURIComponent(value).replace( /%(2[346BF]|3[AC-F]|40|5[BDE]|60|7[BCD])/g, decodeURIComponent ) } } /* eslint-enable no-var */
read
方法是將value
進(jìn)行解碼,write
方法是將value
進(jìn)行編碼。
remove
function remove(name, attributes) { set( name, '', assign({}, attributes, { expires: -1 }) ) }
remove
方法就是使用set
方法將value
設(shè)置為空字符串,然后將expires
設(shè)置為-1
,這樣就相當(dāng)于刪除了cookie
。
withAttributes & withConverter
Object.create({ withAttributes: function (attributes) { return init(assign({}, defaultAttributes, attributes)) }, withConverter: function (converter) { return init(assign({}, defaultConverter, converter)) } })
這兩個(gè)方法就是用來(lái)設(shè)置defaultAttributes
和defaultConverter
的,這兩個(gè)對(duì)象是用來(lái)設(shè)置cookie
的默認(rèn)屬性和默認(rèn)的converter
。
總結(jié)
通過(guò)學(xué)習(xí)js-cookie
的源碼,我們可以了解到cookie
的基本使用,如果想深入了解cookie
,可以參考MDN。
同時(shí)我們也學(xué)會(huì)了很多字符串的處理方法,比如encodeURIComponent
、decodeURIComponent
、split
、join
等等。
以上就是JS前端操作 Cookie源碼示例解析的詳細(xì)內(nèi)容,更多關(guān)于JS前端操作Cookie的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
微信小程序 textarea 詳解及簡(jiǎn)單使用方法
這篇文章主要介紹了微信小程序 textarea 詳解及簡(jiǎn)單使用方法的相關(guān)資料,這里附有實(shí)現(xiàn)實(shí)例代碼,及解決textarea沒(méi)有bindchange事件,無(wú)法在輸入時(shí)給變量賦值的方法, 需要的朋友可以參考下2016-12-12微信小程序 動(dòng)畫的簡(jiǎn)單實(shí)例
這篇文章主要介紹了微信小程序 動(dòng)畫的簡(jiǎn)單實(shí)例的相關(guān)資料,希望通過(guò)本文能幫助到大家,需要的朋友可以參考下2017-10-10微信小程序技巧之show內(nèi)容展示,上傳文件編碼問(wèn)題
這篇文章主要介紹了微信小程序技巧之show內(nèi)容展示,上傳文件編碼問(wèn)題,具有一定的參考價(jià)值,有興趣的可以了解一下。2017-01-01微信小程序之MaterialDesign--input組件詳解
本篇文章主要介紹了微信小程序之MaterialDesign--input組件詳解,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-02-02微信小程序 ecshop地址三級(jí)聯(lián)動(dòng)實(shí)現(xiàn)實(shí)例代碼
這篇文章主要介紹了微信小程序 ecshop地址3級(jí)聯(lián)動(dòng)實(shí)現(xiàn)實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2017-02-02微信小程序 scroll-view隱藏滾動(dòng)條詳解
這篇文章主要介紹了微信小程序 scroll-view隱藏滾動(dòng)條和跳轉(zhuǎn)頁(yè)面的相關(guān)資料,需要的朋友可以參考下2017-01-01Js原型鏈constructor prototype __proto__屬性實(shí)例詳解
這篇文章主要介紹了Js原型鏈constructor prototype __proto__屬性實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10詳解微信小程序 通過(guò)控制CSS實(shí)現(xiàn)view隱藏與顯示
這篇文章主要介紹了微信小程序 通過(guò)控制CSS實(shí)現(xiàn)view隱藏與顯示的相關(guān)資料,需要的朋友可以參考下2017-05-05