localStorage靈魂五問(wèn). 5M空間?? 10M !!!
靈魂五問(wèn)
- localStorage 存儲(chǔ)的鍵值采用什么字符編碼
- 5M 的單位是什么
- localStorage 鍵占不占存儲(chǔ)空間
- localStorage的鍵的數(shù)量,對(duì)寫和讀性能的影響
- 寫個(gè)方法統(tǒng)計(jì)一個(gè)localStorage已使用空間
我們挨個(gè)解答,之后給各位面試官又多了一個(gè)面試題。
我們常說(shuō)localStorage存儲(chǔ)空間是5M,請(qǐng)問(wèn)這個(gè)5M的單位是什么?
localStorage ?存儲(chǔ)的鍵值采用什么字符編碼?
打開相對(duì)權(quán)威的MDN localStorage#description[1]
The keys and the values stored with?
localStorage?are?always?in the UTF-16?`DOMString`[2]?format, which uses two bytes per character. As with objects, integer keys are automatically converted to strings.
翻譯成中文:
localStorage 存儲(chǔ)的鍵和值始終采用 UTF-16 DOMString 格式,每個(gè)字符使用兩個(gè)字節(jié)。與對(duì)象一樣,整數(shù)鍵將自動(dòng)轉(zhuǎn)換為字符串。
答案:UTF-16
MDN這里描述的沒有問(wèn)題,也有問(wèn)題,因?yàn)閁TF-16,每個(gè)字符使用兩個(gè)字節(jié),是有前提條件的,就是碼點(diǎn)小于0xFFFF(65535), 大于這個(gè)碼點(diǎn)的是四個(gè)字節(jié)。
這是全文的關(guān)鍵。
5M 的單位是什么
5M的單位是什么?
選項(xiàng):
- 字符的個(gè)數(shù)
- 字節(jié)數(shù)
- 字符的長(zhǎng)度值
- bit 數(shù)
- utf-16編碼單元
以前不知道,現(xiàn)代瀏覽器,準(zhǔn)確的應(yīng)該是 選項(xiàng)3,字符的長(zhǎng)度 ,亦或 選項(xiàng)5, utf-16編碼單元
字符的個(gè)數(shù),并不等于字符的長(zhǎng)度,這一點(diǎn)要知道:
"a".length?//?1
"人".length?//?1
"??".length?//?2
"??".length?//?2
現(xiàn)代瀏覽器對(duì)字符串的處理是基于UTF-16?`DOMString`[3]。
但是說(shuō)5M字符串的長(zhǎng)度,顯然有那么點(diǎn)怪異。
而根據(jù) UTF-16編碼規(guī)則,要么2個(gè)字節(jié),要么四個(gè)字節(jié),所以不如說(shuō)是 10M 的字節(jié)數(shù),更為合理。
當(dāng)然,2個(gè)字節(jié)作為一個(gè)utf-16的字符編碼單元,也可以說(shuō)是 5M 的utf-16的編碼單元。
我們先編寫一個(gè)utf-16字符串計(jì)算字節(jié)數(shù)的方法:非常簡(jiǎn)單,判斷碼點(diǎn)決定是2還是4
function?sizeofUtf16Bytes(str)?{
????var?total?=?0,
????????charCode,
????????i,
????????len;
????for?(i?=?0,?len?=?str.length;?i?????????charCode?=?str.charCodeAt(i);
????????if?(charCode?<=?0xffff)?{
????????????total?+=?2;
????????}?else?{
????????????total?+=?4;
????????}
????}
????return?total;
}
我們?cè)俑^10M的字節(jié)數(shù)來(lái)存儲(chǔ)
我們留下8個(gè)字節(jié)數(shù)作為key,8個(gè)字節(jié)可是普通的4個(gè)字符換,也可是碼點(diǎn)大于65535的3個(gè)字符,也可是是組合。
下面的三個(gè)組合,都是可以的,
aaaaaa??????
在此基礎(chǔ)上增加任意一個(gè)字符,都會(huì)報(bào)錯(cuò)異常異常。
const?charTxt?=?"人";
let?count?=?(10?*?1024?*?1024?/?2)?-?8?/?2;
let?content?=?new?Array(count).fill(charTxt).join("");
const?key?=?"aa??";
localStorage.clear();
try?{
????localStorage.setItem(key,?content);
}?catch?(err)?{
????console.log("err",?err);
}
const?sizeKey?=?sizeofUtf16Bytes(key);
const?contentSize?=?sizeofUtf16Bytes(content);
console.log("key?size:",?sizeKey,?content.length);
console.log("content?size:",?contentSize,?content.length);
console.log("total?size:",?sizeKey?+?contentSize,?content.length?+?key.length);
現(xiàn)代瀏覽器的情況下:
所以,說(shuō)是10M的字節(jié)數(shù),更為準(zhǔn)確,也更容易讓人理解。
如果說(shuō)5M,那其單位就是字符串的長(zhǎng)度,而不是字符數(shù)。
答案:字符串的長(zhǎng)度值, 或者utf-16的編碼單元
更合理的答案是 10M字節(jié)空間。
localStorage 鍵占不占存儲(chǔ)空間
我們把 key和val各自設(shè)置長(zhǎng) 2.5M的長(zhǎng)度
const?charTxt?=?"a";
let?count?=?(2.5?*?1024?*?1024);
let?content?=?new?Array(count).fill(charTxt).join("");
const?key?=?new?Array(count).fill(charTxt).join("");
localStorage.clear();
try?{
????console.time("setItem")
????localStorage.setItem(key,?content);
????console.timeEnd("setItem")
}?catch?(err)?{
????console.log("err?code:",?err.code);
????console.log("err?message:",?err.message)
}
執(zhí)行正常。
我們把content的長(zhǎng)度加1, 變?yōu)?2.5 M + 1, key的長(zhǎng)度依舊是 2.5M的長(zhǎng)度
const?charTxt?=?"a";
let?count?=?(2.5?*?1024?*?1024);
let?content?=?new?Array(count).fill(charTxt).join("")?+?1;
const?key?=?new?Array(count).fill(charTxt).join("");
localStorage.clear();
try?{
????console.time("setItem")
????localStorage.setItem(key,?content);
????console.timeEnd("setItem")
}?catch?(err)?{
????console.log("err?code:",?err.code);
????console.log("err?message:",?err.message)
}
image.png產(chǎn)生異常,存儲(chǔ)失敗。至于更多異常詳情嗎,參見 localstorage_功能檢測(cè)[4]:
function?storageAvailable(type)?{
????var?storage;
????try?{
????????storage?=?window[type];
????????var?x?=?'__storage_test__';
????????storage.setItem(x,?x);
????????storage.removeItem(x);
????????return?true;
????}
????catch(e)?{
????????return?e?instanceof?DOMException?&&?(
????????????//?everything?except?Firefox
????????????e.code?===?22?||
????????????//?Firefox
????????????e.code?===?1014?||
????????????//?test?name?field?too,?because?code?might?not?be?present
????????????//?everything?except?Firefox
????????????e.name?===?'QuotaExceededError'?||
????????????//?Firefox
????????????e.name?===?'NS_ERROR_DOM_QUOTA_REACHED')?&&
????????????//?acknowledge?QuotaExceededError?only?if?there's?something?already?stored
????????????(storage?&&?storage.length?!==?0);
????}
}
答案: 占空間
鍵的數(shù)量,對(duì)讀寫的影響
我們500 * 1000鍵,如下
let?keyCount?=?500?*?1000;
localStorage.clear();
for?(let?i?=?0;?i?????localStorage.setItem(i,?"");
}
setTimeout(()?=>?{
????console.time("save_cost");
????localStorage.setItem("a",?"1");
????console.timeEnd("save_cost");
},?2000)
setTimeout(()?=>?{
????console.time("read_cost");
????localStorage.getItem("a");
????console.timeEnd("read_cost");
},?2000)
//?save_cost:?0.05615234375?ms
//?read_cost:?0.008056640625?ms
你單獨(dú)執(zhí)行保存代碼:
localStorage.clear();????
console.time("save_cost");
localStorage.setItem("a",?"1");
console.timeEnd("save_cost");
//?save_cost:?0.033203125?ms
可以多次測(cè)試, 影響肯定是有的,也僅僅是數(shù)倍,不是特別的大。
反過(guò)來(lái),如果是保存的值表較大呢?
const?charTxt?=?"a";
const?count?=?5?*?1024?*?1024??-?1
const?val1?=?new?Array(count).fill(charTxt).join("");
setTimeout(()?=>{
????localStorage.clear();
????console.time("save_cost_1");
????localStorage.setItem("a",?val1);
????console.timeEnd("save_cost_1");
},1000)
setTimeout(()?=>{
????localStorage.clear();
????console.time("save_cost_2");
????localStorage.setItem("a",?"a");
????console.timeEnd("save_cost_2");
},1000)
//?save_cost_1:?12.276123046875?ms
//?save_cost_2:?0.010009765625?ms
可以多測(cè)試很多次,單次值的大小對(duì)存的性能影響非常大,讀取也一樣,合情合理之中。
所以盡量不要保存大的值,因?yàn)槠涫峭阶x取,純大數(shù)據(jù),用indexedDB就好。
答案:鍵的數(shù)量對(duì)讀取性能有影響,但是不大。值的大小對(duì)性能影響更大,不建議保存大的數(shù)據(jù)。
寫個(gè)方法統(tǒng)計(jì)一個(gè)localStorage已使用空間
現(xiàn)代瀏覽器的精寫版本:
function?sieOfLS()?{
????return?Object.entries(localStorage).map(v?=>?v.join('')).join('').length;
}
測(cè)試代碼:
localStorage.clear();
localStorage.setItem("??",?1);
localStorage.setItem("????????????????",?1111);
console.log("size:",?sieOfLS())???//?23
//???*9?+?1?*5?=?2*9?+?1*5?=?23
html的協(xié)議標(biāo)準(zhǔn)
WHATWG 超文本應(yīng)用程序技術(shù)工作組 的localstorage[5] 協(xié)議定了localStorage的方法,屬性等等,并沒有明確規(guī)定其存儲(chǔ)空間。也就導(dǎo)致各個(gè)瀏覽器的最大限制不一樣。
其并不是ES的標(biāo)準(zhǔn)。
頁(yè)面的utf-8編碼
我們的html頁(yè)面,經(jīng)常會(huì)出現(xiàn)。告知瀏覽器此頁(yè)面屬于什么字符編碼格式,下一步瀏覽器做好解碼工作。
????
????
????
????容器
這和localStorage的存儲(chǔ)沒有半毛錢的關(guān)系。
localStorage擴(kuò)容
localStorage的空間是 10M的字節(jié)數(shù),一般情況是夠用,可是人總是有貪欲。真達(dá)到了空間限制,怎么弄?
localStorage擴(kuò)容就是一個(gè)話題。
引用
localStorage[6]
參考資料
[1]localStorage#description: https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage#description
[2]DOMString: https://developer.mozilla.org/en-US/docs/Web/API/DOMString
DOMString: https://developer.mozilla.org/en-US/docs/Web/API/DOMString
localstorage_功能檢測(cè): https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API#localstorage_%E5%8A%9F%E8%83%BD%E6%A3%80%E6%B5%8B
[5]localstorage: https://html.spec.whatwg.org/multipage/webstorage.html#dom-localstorage-dev
[6]localStorage: https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage
