ES6 引入了一種新的原始數據類型Symbol,表示獨一無二的值。
聲明定義symbol的幾種方式
// 第一種定義方式
let s = Symbol(); console.log(typeof s); // symbol
由於symbol的值是獨一無二的,因此根據此特性,兩個變量即使值是一樣的,也不相等。
let s1 = Symbol(); let s2 = Symbol(); console.log(s1 === s2); // false
// 注意,Symbol函數的參數只是表示對當前 Symbol 值的描述,因此相同參數的Symbol函數的返回值是不相等的。
let s = Symbol('hello');
let s1 = Symbol('hello');
console.log(s === s1); // false
給symbol添加參數以后,就等於為它們加上了描述,輸出的時候就能夠分清,到底是哪一個值。
注意:Symbol函數前不能使用new命令,否則會報錯。這是因為生成的 Symbol 是一個原始類型的值,不是對象。也就是說,由於 Symbol 值不是對象,所以不能添加屬性。基本上,它是一種類似於字符串的數據類型。
// 第二種定義方式(登記機制)
let cms = Symbol.for();
console.log(cms); // Symbol(undefined)
let cms1 = Symbol.for("symbol");
console.log(cms1); // Symbol(symbol)
let cms2 = Symbol.for("symbol");
console.log(cms1 === cms2); //true
Symbol.for()與Symbol()這兩種寫法,都會生成新的 Symbol。它們的區別是,前者會被登記在全局環境中供搜索,后者不會。Symbol.for()每次調用,返回同一個Symbol值。但是Symbol每次調用會返回不同的值。
Symbol.keyFor()方法返回一個已登記的 Symbol 類型值的key。
let s1 = Symbol.for("foo"); Symbol.keyFor(s1) // "foo" let s2 = Symbol("foo"); Symbol.keyFor(s2) // undefined
上面代碼中,變量s2屬於未登記的 Symbol 值,所以返回undefined。
ymbol.prototype.description
ES2019 提供了一個實例屬性description,直接返回 Symbol 的描述。
let s = Symbol("hello world");
// ES2019
console.log(s.description); // hello word
作為屬性名的 Symbol
由於每一個 Symbol 值都是不相等的,這意味着 Symbol 值可以作為標識符,用於對象的屬性名,就能保證不會出現同名的屬性。這對於一個對象由多個模塊構成的情況非常有用,能防止某一個鍵被不小心改寫或覆蓋。
let mySymbol = Symbol(); let a = {} // 寫法1: a[mySymbol] = "hello"; // 寫法2 let a1 = { [mySymbol]: "hello" } console.log(a[mySymbol]); // hello console.log(a1[mySymbol]); // hello
注意,Symbol 值作為對象屬性名時,不能用點運算符。
const mySymbol = Symbol(); const a = {}; a.mySymbol = 'Hello!'; a[mySymbol] // undefined a['mySymbol'] // "Hello!"
上面代碼中,因為點運算符后面總是字符串,所以不會讀取mySymbol作為標識名所指代的那個值,導致a的屬性名實際上是一個字符串,而不是一個 Symbol 值。
同理,在對象的內部,使用 Symbol 值定義屬性時,Symbol 值必須放在方括號之中。
let s = Symbol(); let obj = { [s]: function (arg) { ... } }; obj[s](123);
采用增強的對象寫法,上面代碼的obj對象可以寫得更簡潔一些。
let obj = {
[s](arg) { ... }
};
屬性名的遍歷
Symbol 作為屬性名,遍歷對象的時候,該屬性不會出現在for...in、for...of循環中,也不會被Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回。
let a = Symbol("foo");
let b = Symbol("baz");
let obj = {
c: "jacascript",
d: "vue"
}
// 寫法1:
obj[a] = "html";
obj[b] = "css";
for (let item in obj) {
console.log(obj[item]);
}
// jacascript
// vue
有一個Object.getOwnPropertySymbols()方法,可以獲取指定對象的所有 Symbol 屬性名。該方法返回一個數組,成員是當前對象的所有用作屬性名的 Symbol 值。
let a = Symbol("foo");
let b = Symbol("baz");
let obj = {
c: "jacascript",
d: "vue"
}
// 寫法1:
obj[a] = "html";
obj[b] = "css";
for (let item in obj) {
console.log(obj[item]); // jacascript vue
}
const mySymbol = Object.getOwnPropertySymbols(obj);
console.log(mySymbol); // [ Symbol(foo), Symbol(baz) ]
另一個新的 API,Reflect.ownKeys()方法可以返回所有類型的鍵名,包括常規鍵名和 Symbol 鍵名。
let a = Symbol("foo");
let b = Symbol("baz");
let obj = {
c: "jacascript",
d: "vue"
}
// 寫法1:
obj[a] = "html";
obj[b] = "css";
for (let item in obj) {
console.log(obj[item]); // jacascript vue
}
const mySymbol = Object.getOwnPropertySymbols(obj);
console.log(mySymbol); // [ Symbol(foo), Symbol(baz) ]
const mySymbol1 = Reflect.ownKeys(obj);
console.log(mySymbol1); // [ 'c', 'd', Symbol(foo), Symbol(baz) ]
上面代碼,對象obj上定義了2個屬性c、然后添加了2個Symbol屬性。循環遍歷的時候只打印了自己的屬性。通過Object.getOwnPropertySymbols()方法返回得到所有的Symbol屬性的數組。
通過Reflect.ownKeys();方法返回全部屬性數組。
使用symbol解決字符串耦合的問題
例如:打印一個對象中兩個同名人的分數
let user1 = "李四"; let user2 = "李四"; let grade = { [user1]: { js: 100, css: 89, html: 50 }, [user2]: { js: 90, css: 99, html: 70 }, } console.log(grade); // 李四: {js: 90, css: 99, html: 70}
上面代碼,打印后我們會發現,前者會被后者覆蓋掉。因為對象中兩個key是耦合的。
解決方案:定義成Symbol方式
let user1 = { name: "李四", key: Symbol() }; let user2 = { name: "李四", key: Symbol() }; let grade = { [user1.key]: { js: 100, css: 89, html: 50 }, [user2.key]: { js: 90, css: 99, html: 70 }, } console.log(grade[user1.key]); // { js: 100, css: 89, html: 50 } console.log(grade[user2.key]); // { js: 90, css: 99, html: 70 }
注意:存取值的時候,需采用對象[]的方式把變量放入中括號中。否則會當成字符串。
