不管你信不信,面試官讓我使用 JS 計(jì)算 LocalStorage 的容量!
Hello,大家好,我是 Sunday。
現(xiàn)在的面試總會(huì)遇到很多“奇葩”的問題,不過本著“存在即合理”的態(tài)度,從“深入JS”的角度來看,這些個(gè)性化的面試題還是挺有意思的。
所以,咱們今天就來看一個(gè)“奇葩”面試題:如何使用JS來計(jì)算LocalStorage的容量
本文為譯文:https://javascript.plainenglish.io/believe-it-or-not-interviewer-asks-me-to-calculate-localstorage-capacity-using-javascript-12928482e096
01:什么時(shí)候需要關(guān)注 LocalStorage 空間?
LocalStorage 是瀏覽器提供的一種本地會(huì)話存儲(chǔ)的方式,最大支持 5M 的存儲(chǔ)空間。
雖說它的存儲(chǔ)空間是有上限的,但是我相信很多同學(xué)都和我一樣,在日常開發(fā)中其實(shí)并沒有關(guān)注過這個(gè)問題。畢竟 5M 的默認(rèn)存儲(chǔ)針對(duì)數(shù)據(jù)而言已經(jīng)非常大了。
但是,如果你在面對(duì)一些足夠復(fù)雜的項(xiàng)目,涉及到大量的數(shù)據(jù)本地存儲(chǔ)且沒有使用 IndexDB 的前提下,那么關(guān)注 LocalStorage 存儲(chǔ)空間位置,就變得有意義了。
02:如何計(jì)算容量
為了計(jì)算總?cè)萘?,我們將使?10KB 為單位,相當(dāng)于 10240 字節(jié)。我們將不斷向 LocalStorage 添加 10KB 塊,直到它已滿并引發(fā)錯(cuò)誤。此時(shí),我們統(tǒng)計(jì)所有累積的數(shù)據(jù),這就是總存儲(chǔ)量!
在 JavaScript 中,字符串使用 UTF-16 編碼存儲(chǔ)。這意味著每個(gè)字符通常占用 2 個(gè)字節(jié)的內(nèi)存空間。但是,對(duì)于某些特殊字符(例如表情符號(hào)或某些不太常見的語言字符),它們可能最多使用 4 個(gè)字節(jié)。
"a".length // 1
"??".length // 2
要估計(jì)字符串占用的內(nèi)存空間(以字節(jié)為單位),可以將字符串的長(zhǎng)度乘以字符的平均字節(jié)大小。對(duì)于常規(guī)字符集(不包括特殊字符),我們可以這樣估計(jì):
var str = "Hello, World!";
var bytes = str.length * 2; // 2 bytes
console.log(bytes + " bytes"); // 輸出: 26 bytes
要計(jì)算以千字節(jié) (KB) 為單位的大小,可以將字節(jié)總數(shù)除以 1024(1 KB = 1024 字節(jié))
var kb = bytes / 1024;
console.log(kb + " KB"); // 輸出: 0.025 KB
注意:這是一個(gè)粗略的估計(jì)。由于 JavaScript 引擎的實(shí)現(xiàn)、字符串中是否存在特殊字符以及其他因素,實(shí)際消耗的內(nèi)存可能會(huì)有所不同。
下面是一段 JavaScript 代碼,可幫助您計(jì)算 LocalStorage 的總?cè)萘浚?/p>
// 要制作精確的 10KB 字符串,我們需要一個(gè)長(zhǎng)度為 5120 個(gè)字符的字符串。 UTF-16編碼下,每個(gè)字符占用2個(gè)字節(jié)。因此,5120 個(gè)字符等于 10240 個(gè)字節(jié),相當(dāng)于 10KB。
// 然后,它會(huì)重復(fù)將此字符串添加到 LocalStorage,直到拋出錯(cuò)誤,表明 LocalStorage 已滿。
const computedTotal = () => {
return new Promise((resolve) => {
let str = "0123456789";
// 首先,我們創(chuàng)建一個(gè)10 KB字符串
while (str.length < 5120) {
str += "0123456789";
}
// 清除本地存儲(chǔ)
localStorage.clear();
let temp = str;
// 繼續(xù)將10 KB累積到LocalStore中
const timer = setInterval(() => {
try {
localStorage.setItem("temp", temp);
temp += str; // 將另外10 KB添加到臨時(shí)字符串
} catch {
// 如果拋出錯(cuò)誤,這意味著我們已經(jīng)超出了最大存儲(chǔ)空間
// 考慮每個(gè)字符為2字節(jié),以KB為單位計(jì)算大小
resolve((temp.length * 2) / 1024);
clearInterval(timer);
// 計(jì)算后記得清除LocalStore
localStorage.clear();
}
}, 0);
});
};
(async () => {
const total = await computedTotal();
console.log(`最大容量: ${total}KB`); // 10240KB
})();
圖片
注意:
運(yùn)行該函數(shù)后,我們看到結(jié)果是10MB
的字節(jié)數(shù),這偏離了眾所周知的 5MB 限制。
根據(jù)UTF-16編碼規(guī)則,每個(gè)字符(chart)要么是2字節(jié),要么是4字節(jié)。因此,官網(wǎng)中所說的 5MB,其單位就是字符串的長(zhǎng)度。
03:如何計(jì)算已使用的容量
要計(jì)算已使用的容量,我們需要做的就是迭代 LocalStorage 上的存儲(chǔ)屬性并計(jì)算每個(gè)存儲(chǔ)屬性的長(zhǎng)度。將它們?nèi)考悠饋砭偷玫搅丝傄延萌萘浚?/p>
const computedUse = () => {
let cache = 0;
// 循環(huán)訪問LocalStore中的所有 key
for(let key in localStorage) {
// 檢查 key 是否是LocalStore的一部分(而不是其原型)
if (localStorage.hasOwnProperty(key)) {
// 將每個(gè)項(xiàng)目的長(zhǎng)度添加到總緩存中
cache += localStorage.getItem(key).length * 2; // 每個(gè)字符計(jì)為2個(gè)字節(jié)
}
}
// 將總數(shù)從字節(jié)轉(zhuǎn)換為千字節(jié)并固定為2位小數(shù)
return (cache / 1024).toFixed(2);
};
(async () => {
// 創(chuàng)建一個(gè)大字符串“o”用于測(cè)試目的
let o = '0123456789';
for(let i = 0 ; i < 1000; i++) {
o += '0123456789';
}
// 將大字符串存儲(chǔ)在LocalStore中
localStorage.setItem('o', o);
// 計(jì)算使用容量
const useCache = computedUse();
console.log(`計(jì)算結(jié)果: ${useCache}KB`); // 19.55KB
})();
圖片
04:計(jì)算可用容量
在確定了 LocalStorage 的總?cè)萘亢鸵延萌萘亢?,我們可以通過從總?cè)萘恐袦p去已用容量來輕松計(jì)算剩余可用容量。
下面是執(zhí)行此計(jì)算的JavaScript 函數(shù):
const computedsurplus = (total, use) => {
return total - use;
};
(async () => {
// 計(jì)算Local存儲(chǔ)總?cè)萘? const total = await computedTotal();
// 創(chuàng)建一個(gè)大字符串“o”用于測(cè)試目的
let o = '0123456789';
for(let i = 0 ; i < 1000; i++) {
o += '0123456789';
}
// 將大字符串存儲(chǔ)在LocalStore中
localStorage.setItem('o', o);
// 假設(shè) computedUse 是一個(gè)計(jì)算已用容量的函數(shù)
const useCache = computedUse();
// 計(jì)算并記錄剩余可用容量
console.log(`剩余可用容量: ${computedsurplus(total, useCache)}KB`); // 10220.45 KB
})();
圖片