本篇採用意譯,原文連接在文章末尾附上。json
爲了防止屬性名衝突, JavaScript 介紹了一種 symbols 的類型。在 2015 - 2019 中,symbols 提供一種方法去模擬私有屬性。安全
建立 symbol 最簡單的方式是調用 Symbol() 方法。有兩個關鍵屬性使得 symbols 變得特殊:less
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 註冊,這麼作有可能致使命名衝突。ui
JavaScript 中的第一個內置 symbol 是 Symbol.iterator。一個有 Symbol.iterator 方法當作迭代的對象。也就意味着。你可使用這個對象做爲循環的右操做符。this
好比獲取斐波那契數列:spa
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。也就是說,假設有一個可迭代的類,以下:debug
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 的屬性。好比:3d
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。