為啥需要 Symbol
ES5
里面對象的屬性名都是字符串,如果你需要使用人提供的對象,你對這個(gè)對象有哪些屬性也不是很清楚,但又想為這個(gè)對象新增一些屬性显设,那么你新增的屬性名就很可能和原來的屬性名發(fā)送沖突,顯然我們是不希望這種情況發(fā)生的惦蚊。所以,我們需要確保每個(gè)屬性名都是獨(dú)一無二的,這樣就可以防止屬性名的沖突了屈溉。因此,ES6
里就引入了Symbol
,用它來產(chǎn)生一個(gè)獨(dú)一無二的值泼返。
Symbol 是什么
Symbol
實(shí)際上是ES6
引入的一種原始數(shù)據(jù)類型,除了Symbol
,JavaScript
還有其他 5 種數(shù)據(jù)類型,分別是Undefined, Null. Boolean, String, Number
, 對象 ,這 5 種數(shù)據(jù)類型都是ES5
中就有的最仑。
怎么生成一個(gè) Symbol 類型的值
既然我們已經(jīng)知道了
Symbol
是一種原始的數(shù)據(jù)類型,那么怎么生成這種數(shù)據(jù)類型的值呢?Symbol
值是通過Symbol
函數(shù)生成的,如下:
let sym = Symbol();
console.log(sym); // Symbol()
console.log(typeof sym); // symbol
Symbol 函數(shù)不能用 new
Symbol
函數(shù)不是一個(gè)構(gòu)造函數(shù),前面不能用new
操作符梦重。所以Symbol
類型的值也不是一個(gè)對象,不能添加任何屬性,它只是一個(gè)類似于字符型的數(shù)據(jù)類型仑乌。如果強(qiáng)行在Symbol
函數(shù)前加上new
操作符,會(huì)報(bào)錯(cuò),如下:
let sym = new Symbol();
console.log(sym); // Uncaught TypeError: Symbol is not a constructor
Symbol 函數(shù)的參數(shù)
字符串作為參數(shù)
用上面的方法生成的
Symbol
值不好進(jìn)行區(qū)分,Symbol
函數(shù)還可以接受一個(gè)字符串參數(shù),來對產(chǎn)生的Symbol
值進(jìn)行描述百拓,方便我們區(qū)分不同的Symbol
值。
let s1 = Symbol('s1');
let s2 = Symbol('s2');
let s3 = Symbol('s3');
console.log(s1); // Symbol('s1')
console.log(s2); // Symbol('s2')
console.log(s1 === s2); // false
console.log(s2 === s3); // false
1.給
Symbol
函數(shù)加了參數(shù)之后,控制臺(tái)輸出的時(shí)候可以區(qū)分到底是哪一個(gè)值;
Symbol
函數(shù)的參數(shù)只是對當(dāng)前Symbol
值的描述,因此相同參數(shù)的Symbol
函數(shù)返回值是不相等的;
對象作為參數(shù)
如果
Symbol
函數(shù)的參數(shù)是一個(gè)對象,就會(huì)調(diào)用該對象的toString
方法,將其轉(zhuǎn)化為一個(gè)字符串,然后才生成一個(gè)Symbol
值晰甚。所以,說到底,Symbol
函數(shù)的參數(shù)只能是字符申衙传。
Symbol值不可以進(jìn)行運(yùn)算
既然
Symbol
是一種數(shù)據(jù)類型,那我們一定想知道Symbol
值是否能進(jìn)行運(yùn)算。告訴你,Symbol
值是不能進(jìn)行運(yùn)算的,不僅不能和Symbol
值進(jìn)行運(yùn)算,也不能和其他類型的值進(jìn)行運(yùn)算,否則會(huì)報(bào)錯(cuò)厕九。Symbol
值可以顯式轉(zhuǎn)化為 字符串 和 布爾值, 但是不能轉(zhuǎn)為 數(shù)值蓖捶。
var mysym1 = Symbol('my symbol');
mysym1.toString(); // Symbol('my symbol')
String(mysym1) // Symbol('my symbol')
var mysym2 = Symbol();
Boolean(mysym2); // true
Number(mysym2); // 報(bào)錯(cuò)
Symbol 作屬性名
symbol
就是為對象的屬性名而生,那么Symbol
值怎么作為對象的屬性名呢?有下面幾種寫法:
let a = {};
let s4 = Symbol();
// 第一種寫法
a[s4] = 'mySymbol';
// 第二種寫法
a = {
[s4]: 'mySymbol'
}
// 第三種寫法
Object.defineProperty(a, s4, { value: 'mySymbol' });
a.s4; // undefined
a.s4 = 'mySymbol';
a[s4] // undefined
a['s4']// 'mySymbol'
1,使用對象的
Symbol
值作為屬性名時(shí),獲取相應(yīng)的屬性值不能用點(diǎn)運(yùn)算符;
2.如果用點(diǎn)運(yùn)算符來給對象的屬性賦
Symbol
類型的值,實(shí)際上屬性名會(huì)變成一個(gè)字符串,而不是一個(gè)Symbol
值;
3,在對象內(nèi)部,使用
Symbol
值定義屬性時(shí),Symbol
值必須放在方括號(hào)之中,否則只是一個(gè)字符串。
Symbol值作為屬性名的遍歷
使用
for.in
和for..of
都無法逸歷到Symbol
值的屬性,Symbol
值作為對象的屬性名,也無法通過Object.keys()
止剖、Object.getOwnPropertyNames()
來獲取了。
但是,不用擔(dān)心,這種平常的需求肯定是會(huì)有解決辦法的落君。
我們可以使用
Object.getownPropertySymbols()
方法獲取一個(gè)對象上的Symbol
屬性名穿香。也可以使用Reflect.ownKeys()
返回所有類型的屬性名,包括常規(guī)屬性名和Symbol
屬性名
let s5 = Symbol('s5');
let s6 = Symbol('s6');
let a = {
[s5]: 's5',
[s6]: 's6'
}
Object.getOwnPropertySymbols(a); // [Symbol('s5'), Symbol('s6')]
a.hello = 'hello';
Reflect.ownKeys(a); // [hello', Symbol('s5'), Symbol('s6')]
利用
Symbol
值作為對象屬性的名稱時(shí),不會(huì)被常規(guī)方法遍歷到這一特性,可以為對象定義一些非私有的但是又希望只有內(nèi)部可用的方法绎速。
只能拿到
String
類型的屬性名
Object.getOwnPropertyNames(testObject)
Object.keys(testObject)
只能拿到
Symbol
類型的屬性名
Object.getOwnpropertySymbols(testObject)
能拿到對象 任何類型 的屬性名
Reflect.ownKeys(testObject)
Symbol.for() 和 Symbol.keyFor()
Symbol.for
(函數(shù)也可以用來生成Symbol
值,但該函數(shù)有一個(gè)特殊的用處,就是可以重復(fù)使用一個(gè)Symbol
皮获。
let s1 = Symbol('s1');
let s2 = Symbol('s2');
let s3 = Symbol.for('s1'); // Symbol(s1)
let s4 = Symbol.for('s2'); // Symbol(s2)
let s11 = Symbol.keyFor(s1); // undefined
let s33 = Symbol.keyFor(s3); // s1
console.log(s11); // undefined
console.log(s33); // s1
Symbol.for()
函數(shù)要接受一個(gè)字符串作為參數(shù),先搜索有沒有以該參數(shù)作為名稱的Symbol
值,如果有,就直接返回這個(gè)Symbol
值,否則就新建并返回一個(gè)以該字符串為名稱的Symbol
值。
Symbol.keyFor()
函數(shù)是用來查找一個(gè)Symbol
值的登記信息的,
Symbol()
寫法沒有登記機(jī)制,所以返回undefined
;
而
Symbol.for()
函數(shù)會(huì)將生成的Symbol
值登記在全局環(huán)境中,所以Symbol.keyFor()
函數(shù)可以查找到用Symbol.for()
函數(shù)生成的Symbol
值纹冤。
內(nèi)置Symbol值
ES6提供了11個(gè)內(nèi)置的
Symbol
值,分別是
Symbol.hasInstance,
Symbol.isConcatSpreadable洒宝、
Symbol.species,
Symbol.match,
Symbol.replfice.
Symbol.search,
Symbol.split,
Symbol.iterator
Symbol.toPrimitive,
Symbol.toStringTag,
Symbol.unscopables
等。