JS前端操作 Cookie源碼示例解析
引言
前端操作Cookie的場景其實并不多見,Cookie也因為各種問題被逐漸淘汰,但是我們不用Cookie也可以學習一下它的思想,或者通過這次的源碼來學習其他的一些知識。
今天帶來的是:js-cookie
源碼分析
使用
根據(jù)README,我們可以看到js-cookie的使用方式:
// 設置
Cookies.set('name', 'value');
// 設置過期時間
Cookies.set('name', 'value', { expires: 7 })
// 獲取
Cookies.get('name') // => 'value'
// 獲取所有
Cookies.get() // => { name: 'value' }
// 獲取指定域名下
Cookies.get('foo', { domain: 'sub.example.com' })
// 刪除
Cookies.remove('name')
還有很多其他用和配置說明,大家可以自己去看看。
源碼
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的源碼并不多,我們先來看看導出的是什么:
export default init(defaultConverter, { path: '/' })
這里是直接導出了init函數(shù)的返回值,我們來看看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)建了一個對象,這個對象有set、get、remove、withAttributes、withConverter這幾個方法,這幾個方法都是在init函數(shù)內(nèi)部定義的,我們來看看這幾個方法的實現(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)
}
一行一行來看:
if (typeof document === 'undefined') {
return
}
首先判斷是否有document對象,如果沒有則直接返回,這說明js-cookie只能在瀏覽器環(huán)境下使用。
attributes = assign({}, defaultAttributes, attributes)
然后合并配置項,將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是否是一個數(shù)字,如果是數(shù)字則將其轉換為一個Date對象;
這里的864e5是一個常量,結尾的e5代表后面加5個0,也就是86400000表示一天的毫秒數(shù)。
然后判斷expires是否存在,如果存在則將其轉換為UTC時間。
name = encodeURIComponent(name)
.replace(/%(2[346B]|5E|60|7C)/g, decodeURIComponent)
.replace(/[()]/g, escape)
這里對name進行了編碼,然后將name中的%、(、)進行了轉義。
escape是一個內(nèi)置函數(shù),它的作用是將一個字符串轉換為UTF-8編碼的字符串,這里的escape是將(、)轉換為%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轉換為字符串,這里的stringifiedAttributes是一個字符串,最后的結果是這樣的:
stringifiedAttributes = '; path=/; expires=Wed, 21 Oct 2015 07:28:00 GMT'
最后將name、value、stringifiedAttributes拼接起來,然后賦值給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方法的實現(xiàn)比較簡單,主要是解析document.cookie,然后將其轉換為一個對象,來逐行解析:
if (typeof document === 'undefined' || (arguments.length && !name)) {
return
}
對比于set方法,這里多了一個(arguments.length && !name)的判斷,這里是防止傳入空字符串的name。
var cookies = document.cookie ? document.cookie.split('; ') : []
這里是將document.cookie分割為一個數(shù)組,每一項是一個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進行了解碼,然后將name和value存儲到jar對象中,如果傳入了name,則在找到對應的name后就跳出循環(huán)。
return name ? jar[name] : jar
最后返回jar對象,如果傳入了name,則返回對應的value,否則返回整個jar對象。
這里的核心是converter.read,這個方法是用來解析value的,這里的converter是一個對象,它有兩個方法:
/* 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進行解碼,write方法是將value進行編碼。
remove
function remove(name, attributes) {
set(
name,
'',
assign({}, attributes, {
expires: -1
})
)
}
remove方法就是使用set方法將value設置為空字符串,然后將expires設置為-1,這樣就相當于刪除了cookie。
withAttributes & withConverter
Object.create({
withAttributes: function (attributes) {
return init(assign({}, defaultAttributes, attributes))
},
withConverter: function (converter) {
return init(assign({}, defaultConverter, converter))
}
})
這兩個方法就是用來設置defaultAttributes和defaultConverter的,這兩個對象是用來設置cookie的默認屬性和默認的converter。
總結
通過學習js-cookie的源碼,我們可以了解到cookie的基本使用,如果想深入了解cookie,可以參考MDN。
同時我們也學會了很多字符串的處理方法,比如encodeURIComponent、decodeURIComponent、split、join等等。
以上就是JS前端操作 Cookie源碼示例解析的詳細內(nèi)容,更多關于JS前端操作Cookie的資料請關注腳本之家其它相關文章!
相關文章
微信小程序技巧之show內(nèi)容展示,上傳文件編碼問題
這篇文章主要介紹了微信小程序技巧之show內(nèi)容展示,上傳文件編碼問題,具有一定的參考價值,有興趣的可以了解一下。2017-01-01
微信小程序之MaterialDesign--input組件詳解
本篇文章主要介紹了微信小程序之MaterialDesign--input組件詳解,具有一定的參考價值,感興趣的小伙伴們可以參考一下。2017-02-02
微信小程序 ecshop地址三級聯(lián)動實現(xiàn)實例代碼
這篇文章主要介紹了微信小程序 ecshop地址3級聯(lián)動實現(xiàn)實例代碼的相關資料,需要的朋友可以參考下2017-02-02
Js原型鏈constructor prototype __proto__屬性實例詳解
這篇文章主要介紹了Js原型鏈constructor prototype __proto__屬性實例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-10-10
詳解微信小程序 通過控制CSS實現(xiàn)view隱藏與顯示
這篇文章主要介紹了微信小程序 通過控制CSS實現(xiàn)view隱藏與顯示的相關資料,需要的朋友可以參考下2017-05-05

