為了防止屬性名沖突, JavaScript 介紹了一種?symbols?的類型。在 2015 - 2019 中,symbols 提供一種方法去模擬私有屬性。
網(wǎng)站設(shè)計制作過程拒絕使用模板建站;使用PHP+MYSQL原生開發(fā)可交付網(wǎng)站源代碼;符合網(wǎng)站優(yōu)化排名的后臺管理系統(tǒng);成都網(wǎng)站設(shè)計、網(wǎng)站制作收費合理;免費進行網(wǎng)站備案等企業(yè)網(wǎng)站建設(shè)一條龍服務(wù).我們是一家持續(xù)穩(wěn)定運營了十載的成都創(chuàng)新互聯(lián)公司網(wǎng)站建設(shè)公司。
創(chuàng)建 symbol 最簡單的方式是調(diào)用?Symbol()?方法。有兩個關(guān)鍵屬性使得 symbols 變得特殊:
Symbols 可以用于對象 key。只有字符串和 symbol 可以被用于對象 key。
任何兩個 sybmols 都不相等
const?symbol1?=?Symbol();?const?symbol2?=?Symbol();?symbol1?===?symbol2;?//?false?const?obj?=?{};?obj[symbol1]?=?'Hello';?obj[symbol2]?=?'World';?obj[symbol1];?//?'Hello'?obj[symbol2];?//?'World'?復(fù)制代碼
盡管 symbol() 看起來是個對象,實際上它也屬于?7 種基本類型。對 Symbol 使用 new 操作符會導(dǎo)致一個錯誤。
const?symbol1?=?Symbol();?typeof?symbol1;?//?'symbol'?symbol1?instanceof?Object;?//?false?//?Throws?"TypeError:?Symbol?is?not?a?constructor"?new?Symbol();?復(fù)制代碼
Symbol 方法使用單個字符串參數(shù)當(dāng)做描述符。Symbol 的描述符只是用于 debug 的目的。描述符在 symbol 調(diào)用 toString 的時候出現(xiàn)。然而,兩個相同描述符的 symbol 也是不相等的。
const?symbol1?=?Symbol('my?symbol');?const?symbol2?=?Symbol('my?symbol');?symbol1?===?symbol2;?//?false?console.log(symbol1);?//?'Symbol(my?symbol)'?復(fù)制代碼
通常情況下,除非你有合適的理由,不然一般不建議使用全局 symbol 注冊,這么做有可能導(dǎo)致命名沖突。
JavaScript 中的第一個內(nèi)置 symbol 是 Symbol.iterator。一個有 Symbol.iterator 方法當(dāng)做迭代的對象。也就意味著。你可以使用這個對象作為循環(huán)的右操作符。
比如獲取斐波那契數(shù)列:
const?fibonacci?=?{???[Symbol.iterator]:?function*()?{?????let?a?=?1;?????let?b?=?1;?????let?temp;?????yield?b;?????while?(true)?{???????temp?=?a;???????a?=?a?+?b;???????b?=?temp;???????yield?b;?????}???}?};?//?Prints?every?Fibonacci?number?less?than?100?for?(const?x?of?fibonacci)?{???if?(x?>=?100)?{?????break;???}???console.log(x);?}?復(fù)制代碼
為什么 Symbol.iterator 是 symbol 而不是 string? 假設(shè)不使用 Symbol.iterator,迭代名定義為一個字符串屬性的 iterator。也就是說,假設(shè)有一個可迭代的類,如下:
class?MyClass?{???constructor(obj)?{?????Object.assign(this,?obj);???}???iterator()?{?????const?keys?=?Object.keys(this);?????let?i?=?0;?????return?(function*()?{???????if?(i?>=?keys.length)?{?????????return;???????}???????yield?keys[i++];?????})();???}?}?復(fù)制代碼
MyClass 允許你迭代對象 keys。但是上面的類有個潛在的錯誤。假設(shè)用戶故意給對象傳遞一個 iterator 的屬性。比如:
const?obj?=?new?MyClass({?iterator:?'not?a?function'?});?復(fù)制代碼
這樣的話,迭代就會失效。JavaScript 在你使用 for/of 迭代時,會拋出一個錯誤 obj is not iterable。這是因為上面的代碼覆蓋了類中的迭代屬性。這是類似原型污染的安全問題。在想當(dāng)然拷貝用戶數(shù)據(jù)的時候容易發(fā)生這樣的問題,尤其是?proto?和 constructor 這樣的屬性。
關(guān)鍵模式在于 symbol 可以清楚的分割用戶數(shù)據(jù)和對象數(shù)據(jù)。由于符號無法用JSON表示,因此不存在將數(shù)據(jù)傳遞到具有錯誤 Symbol.iterator 屬性的 Express API 的風(fēng)險。 在將用戶數(shù)據(jù)與內(nèi)置函數(shù)和方法(如Mongoose模型)混合的對象中,可以使用符號來確保用戶數(shù)據(jù)不會與內(nèi)置功能沖突。
既然任意兩個 symbol 都不相等,symbol 可以方便的模擬 JavaScript 中的私有屬性。 Symbols 不會在?Object.key(),中出現(xiàn),因為除非你明確 export 一個 symbol,沒有任何代碼可以訪問到這個屬性,除非使用 Object.getOwnPropertySymbols() 方法。
function?getObj()?{???const?symbol?=?Symbol('test');???const?obj?=?{};???obj[symbol]?=?'test';???return?obj;?}?const?obj?=?getObj();?Object.keys(obj);?//?[]?//?Unless?you?explicitly?have?a?reference?to?the?symbol,?you?can't?access?the?//?symbol?property.?obj[Symbol('test')];?//?undefined?//?You?can?still?get?a?reference?to?the?symbol?using?`getOwnPropertySymbols()`?const?[symbol]?=?Object.getOwnPropertySymbols(obj);?obj[symbol];?//?'test'?復(fù)制代碼
Symbols 作為私有屬性方便的一點是,它不會在 JSON.stringify() 中出現(xiàn)。
Symbols 處理對象內(nèi)部狀態(tài)保證用戶數(shù)據(jù)和程序數(shù)據(jù)分離是很不錯的一個工具。使用 symbols 就不需要再加上各種前綴表示程序狀態(tài)。下次可以試試 symbol。