您好,登錄后才能下訂單哦!
為了防止屬性名沖突, JavaScript 介紹了一種?symbols?的類型。在 2015 - 2019 中,symbols 提供一種方法去模擬私有屬性。
創建 symbol 最簡單的方式是調用?Symbol()?方法。有兩個關鍵屬性使得 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'?復制代碼
盡管 symbol() 看起來是個對象,實際上它也屬于?7 種基本類型。對 Symbol 使用 new 操作符會導致一個錯誤。
const?symbol1?=?Symbol();?typeof?symbol1;?//?'symbol'?symbol1?instanceof?Object;?//?false?//?Throws?"TypeError:?Symbol?is?not?a?constructor"?new?Symbol();?復制代碼
Symbol 方法使用單個字符串參數當做描述符。Symbol 的描述符只是用于 debug 的目的。描述符在 symbol 調用 toString 的時候出現。然而,兩個相同描述符的 symbol 也是不相等的。
const?symbol1?=?Symbol('my?symbol');?const?symbol2?=?Symbol('my?symbol');?symbol1?===?symbol2;?//?false?console.log(symbol1);?//?'Symbol(my?symbol)'?復制代碼
通常情況下,除非你有合適的理由,不然一般不建議使用全局 symbol 注冊,這么做有可能導致命名沖突。
JavaScript 中的第一個內置 symbol 是 Symbol.iterator。一個有 Symbol.iterator 方法當做迭代的對象。也就意味著。你可以使用這個對象作為循環的右操作符。
比如獲取斐波那契數列:
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);?}?復制代碼
為什么 Symbol.iterator 是 symbol 而不是 string? 假設不使用 Symbol.iterator,迭代名定義為一個字符串屬性的 iterator。也就是說,假設有一個可迭代的類,如下:
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++];?????})();???}?}?復制代碼
MyClass 允許你迭代對象 keys。但是上面的類有個潛在的錯誤。假設用戶故意給對象傳遞一個 iterator 的屬性。比如:
const?obj?=?new?MyClass({?iterator:?'not?a?function'?});?復制代碼
這樣的話,迭代就會失效。JavaScript 在你使用 for/of 迭代時,會拋出一個錯誤 obj is not iterable。這是因為上面的代碼覆蓋了類中的迭代屬性。這是類似原型污染的安全問題。在想當然拷貝用戶數據的時候容易發生這樣的問題,尤其是?proto?和 constructor 這樣的屬性。
關鍵模式在于 symbol 可以清楚的分割用戶數據和對象數據。由于符號無法用JSON表示,因此不存在將數據傳遞到具有錯誤 Symbol.iterator 屬性的 Express API 的風險。 在將用戶數據與內置函數和方法(如Mongoose模型)混合的對象中,可以使用符號來確保用戶數據不會與內置功能沖突。
既然任意兩個 symbol 都不相等,symbol 可以方便的模擬 JavaScript 中的私有屬性。 Symbols 不會在?Object.key(),中出現,因為除非你明確 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'?復制代碼
Symbols 作為私有屬性方便的一點是,它不會在 JSON.stringify() 中出現。
Symbols 處理對象內部狀態保證用戶數據和程序數據分離是很不錯的一個工具。使用 symbols 就不需要再加上各種前綴表示程序狀態。下次可以試試 symbol。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。