localStorage 靈魂五問。 5M 空間?? 10M ---

靈魂五問

  1. localStorage 存儲的鍵值採用什麼字符編碼

  2. 5M 的單位是什麼

  3. localStorage 鍵佔不佔存儲空間

  4. localStorage 的鍵的數量,對寫和讀性能的影響

  5. 寫個方法統計一個 localStorage 已使用空間

我們常說localStorage存儲空間是5M,請問這個5M的單位是什麼?

localStorage  存儲的鍵值採用什麼字符編碼?

打開相對權威的 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 存儲的鍵和值始終採用 UTF-16 DOMString 格式,每個字符使用兩個字節。與對象一樣,整數鍵將自動轉換爲字符串。

答案:UTF-16

MDN 這裏描述的沒有問題,也有問題,因爲 UTF-16,每個字符使用兩個字節,是有前提條件的,就是碼點小於0xFFFF(65535), 大於這個碼點的是四個字節。

5M 的單位是什麼

5M 的單位是什麼?

選項:

  1. 字符的個數

  2. 字節數

  3. 字符的長度值

  4. bit 數

  5. utf-16 編碼單元

以前不知道,現代瀏覽器,準確的應該是 選項 3,字符的長度 ,亦或 選項 5, utf-16 編碼單元

字符的個數,並不等於字符的長度,這一點要知道:

"a".length // 1
"人".length // 1
"𠮷".length // 2
"🔴".length // 2

現代瀏覽器對字符串的處理是基於 UTF-16 DOMString[3]。

但是說 5M 字符串的長度,顯然有那麼點怪異。

而根據 UTF-16 編碼規則,要麼 2 個字節,要麼四個字節,所以不如說是 10M 的字節數,更爲合理。

當然,2個字節作爲一個utf-16的字符編碼單元,也可以說是 5M 的utf-16的編碼單元。

我們先編寫一個 utf-16 字符串計算字節數的方法:非常簡單,判斷碼點決定是 2 還是 4

function sizeofUtf16Bytes(str) {
    var total = 0,
        charCode,
        i,
        len;
    for (i = 0, len = str.length; i < len; i++) {
        charCode = str.charCodeAt(i);
        if (charCode <= 0xffff) {
            total += 2;
        } else {
            total += 4;
        }
    }
    return total;
}

我們再根絕 10M 的字節數來存儲

我們留下 8 個字節數作爲 key,8 個字節可是普通的 4 個字符換,也可是碼點大於 65535 的 3 個字符,也可是是組合。

  1. aaaa

  2. aa🔴

  3. 🔴🔴

在此基礎上增加任意一個字符,都會報錯異常異常。

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);

現代瀏覽器的情況下:

所以,說是 10M 的字節數,更爲準確,也更容易讓人理解。

localStorage 鍵佔不佔存儲空間

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)
}
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)
}

產生異常,存儲失敗。至於更多異常詳情嗎,參見 localstorage_功能檢測 [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);
    }
}

答案: 佔空間

鍵的數量,對讀寫的影響

我們500 * 1000鍵,如下

let keyCount = 500 * 1000;

localStorage.clear();
for (let i = 0; i < keyCount; 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

你單獨執行保存代碼:

localStorage.clear();    
console.time("save_cost");
localStorage.setItem("a""1");
console.timeEnd("save_cost");
// save_cost: 0.033203125 ms

可以多次測試, 影響肯定是有的,也僅僅是數倍,不是特別的大。

反過來,如果是保存的值表較大呢?

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

可以多測試很多次,單次值的大小對存的性能影響非常大,讀取也一樣,合情合理之中。

所以儘量不要保存大的值,因爲其是同步讀取,純大數據,用 indexedDB 就好。

答案:鍵的數量對讀取性能有影響,但是不大。值的大小對性能影響更大,不建議保存大的數據。

寫個方法統計一個 localStorage 已使用空間

現代瀏覽器的精寫版本:

function sieOfLS() {
    return Object.entries(localStorage).map(v => v.join('')).join('').length;
}

測試代碼:

localStorage.clear();
localStorage.setItem("🔴", 1);
localStorage.setItem("🔴🔴🔴🔴🔴🔴🔴🔴", 1111);
console.log("size:", sieOfLS())   // 23
// 🔴*9 + 1 *5 = 2*9 + 1*5 = 23

html 的協議標準

WHATWG 超文本應用程序技術工作組 的 localstorage[5] 協議定了 localStorage 的方法,屬性等等,並沒有明確規定其存儲空間。也就導致各個瀏覽器的最大限制不一樣。

其並不是 ES 的標準。

頁面的 utf-8 編碼

我們的 html 頁面,經常會出現<meta charset="UTF-8">。告知瀏覽器此頁面屬於什麼字符編碼格式,下一步瀏覽器做好解碼工作。

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta >
    <title>容器</title>
</head>

這和 localStorage 的存儲沒有半毛錢的關係。

localStorage 擴容

localStorage 的空間是 10M 的字節數,一般情況是夠用,可是人總是有貪慾。真達到了空間限制,怎麼弄?

localStorage 擴容就是一個話題。

引用

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

[3]

DOMString: https://developer.mozilla.org/en-US/docs/Web/API/DOMString

[4]

localstorage_功能檢測: 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

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/ED-6IFBVrI2VXoHzcIZ4Gg