1、Symbol類型
ES5
包含五種原始數據類型:字符串、數字、布爾值、null
和undefined
,ES6
引入了第6
中原始類型Symbol
在之前的對象屬性名都是字符串,很容易造成屬性名衝突,比如使用他人提供的對象,想爲這個對象添加新的方法,新方法的名字很有可能與現有的方法產生衝突,Symbol
就可以保證每個屬性的名字都是獨一無二的,這樣就從根本上防止了屬性衝突
1.1、基本使用
Symbol
通過函數生成,Symbol
不是構造函數,它是一個原始類型的值,不是對象,不能通過new
關鍵字創建
let name = Symbol();
let person = {
[name]: 'ithanmang'
}
console.log(name);//Symbol()
console.log(person);//{Symbol(): "ithanmang"}
Symbol
可以接受一個可選參數,可以添加一段文本來描述即將創建的Symbol
,這段描述不可用於屬性訪問,但是建議每次創建Symbol
時都添加這樣一段描述,以便於閱讀代碼和調試
let name = Symbol('name property');
let person = {
[name]: 'ithanmang'
}
console.log(name);//Symbol(name property)
console.log(person);//{Symbol(name property): "ithanmang"}
Symbol
的描述符被存儲在內部的[[Description]]
屬性中,通過調用Symbol
的toString
方法可以讀取到這個屬性
let des = name.toString();
console.log(des);//Symbol(name property)
1.2、唯一性
每一個Sysbol
值都是不相等的,和描述符無關,因此Symbol
可以作爲標識符,用於對象的屬性名,就能保證不會出現同名的屬性,這對於一個對象由多個模塊構成的情況非常有用,能防止某一個鍵被不小心改寫或者覆蓋
let name1 = Symbol('name property');
let name2 = Symbol('name property');
console.log(name1);//Symbol(name property)
console.log(name2);//Symbol(name property)
console.log(Object.is(name1, name2));//false
上面兩個Symbol
雖然打印出來沒有區別但是二者時不相等的
let name1 = Symbol('name1 property');
let person = {
[name1]: 'ithanmang'
}
Object.defineProperty(person, name1, {
writable: false
})
let name2 = Symbol('name2 property');
Object.defineProperties(person, {
[name2]: {
value: 'dxy',
writable: false
}
})
console.log(person[name1]);//ithanmang
console.log(person[name2]);//dxy
需要注意的是,Symbol
值作爲對象屬性時,不能用點運算符
let sym1 = Symbol();
let person = {};
person.sym1 = 'test';
console.log(person[sym1]);//undefined
console.log(person['sym1']);//test
由上面代碼可以得知,person.sym1
的屬性名是一個字符串'sym1'
1.3、共享體系
有時候希望在不同的代碼中共享一個Symbol
,因此ES6
提供了一個可以隨時訪問的全局Symbol
註冊表
1. Symbol.for()
通過Symbol.for()
可以創建一個共享的Symbol
,它接受一個參數,也就是即將創建的Symbol
的字符串標識符,這個參數同樣被用作Symbol
的描述
let uuid = Symbol.for('uuid');
let person = {};
person[uuid] = '550e8400-e29b-41d4-a716-446655440000';
console.log(person[uuid]);//550e8400-e29b-41d4-a716-446655440000
console.log(uuid);//Symbol(uuid)
Symbol.for()
方法首先在全局Symbol
註冊表中搜索鍵位uuid
的Symbol
是否存在,如果存在則直接返回已有的Symbol
,否則,創建一個新的Symbol
並使用這個鍵在Symbol
全局註冊表中註冊,隨即返回新創建的Symbol
如果後續再傳入同樣的鍵調用Symbol.for()
會返回相同的Symbol
let uuid = Symbol.for('uuid');
let person = {};
person[uuid] = '550e8400-e29b-41d4-a716-446655440000';
console.log(person[uuid]);//550e8400-e29b-41d4-a716-446655440000
console.log(uuid);//Symbol(uuid)
let uuid2 = Symbol.for('uuid');
person[uuid2] = '33a6c1ed-387a-46ee-903c-f88a52256893';
console.log(uuid === uuid2);//true
console.log(person);//{Symbol(uuid): "33a6c1ed-387a-46ee-903c-f88a52256893"}
上面代碼中第二次調用Symbol.for()
創建的Symbol
裏面的標識一樣,因此會直接返回之前創建的Symbol
,所以uuid1 === uuid2
爲true
,並且給person
賦值的時候會覆蓋之前的值
2. Symbol.keyFor()
Symbol.keyFor()
可以檢索在全局註冊表中與Symbol
有關的鍵,參數是一個Symbol
類的值
let uuid = Symbol.for("uuid");
console.log(Symbol.keyFor(uuid));//uuid
let uuid2 = Symbol.for('uuid');
console.log(Symbol.keyFor(uuid2));//uuid
console.log(Symbol.keyFor(uuid3));//undefined
uuid
和uuid2
都返回了uuid
這個鍵,而在Symbol
全局註冊表中,不存在uuid3
這個Symbol
,也就是不存在與之有關的鍵,因此返回undefined
需要注意的是Symbol.for()
爲Symbol
值等級的名字,是全局環境的,可以在不同的iframe
中取到同一個值
let iframe = document.createElement('iframe');
iframe.src = String(window.location);
document.body.appendChild(iframe);
console.log(iframe.contentWindow,Symbol.for('uuid') === Symbol.for('uuid'));//true
1.4、類型轉換
類型轉換是javascript
得一個重要屬性,然而其他類型沒有與Symbol
邏輯等價等價得值,因此Symbol
使用起來不是很靈活
使用console.log()
方法輸出得Symbol
輸出的內容,它會調用Symbol
的String()
方法並輸出有用的信息,也可以直接調用string()
方法來獲得相同的內容
let uuid = Symbol.for('uuid');
let desc = String(uuid);
console.log(desc);//Symbol(uuid)
String()
函數調用了uuid.toString()
方法,返回Symbol
描述的內容,但是嘗試將Symbol
與一個字符串拼接,會導致程序拋出異常
let uuid = Symbol.for('uuid');
let desc = uuid + '';//TypeError: Cannot convert a Symbol value to a string
同樣Symbol
也不能被強制轉換成數字類型,將Symbol
與每一個數學運算符混合使用都會導致程序拋出異常
let uuid = Symbol.for('uuid');
let desc = uuid + 1;//TypeError: Cannot convert a Symbol value to a number
1.5、屬性檢索
Symbol
作爲屬性,改屬性不會出現在for...in、for...of
循環中,也不會被Object.getOwnPropertyNames()、Object.keys()、JSON、stringfy()
返回,因此ES6
添加了一個新的方法Object.getOwnPropertySymbols()
來檢索對象中的Symbol
屬性
Object.getOwnPropertySymbols()
返回的是一個包含所有自由屬性的數組
let name = Symbol.for('name value');
let job = Symbol('job value');
let address = Symbol.for('address value');
let person = {
id: 1001,
[name]: 'ithanmang',
[job]: 'coder',
}
Object.defineProperties(person, {
[address]: {
value: 'beijing',
writable: false
}
})
let symbols = Object.getOwnPropertySymbols(person);
//[Symbol(name value), Symbol(job value), Symbol(address value)]
console.log(symbols);
還可以通過一個新的API-Reflect.ownKeys()
,可以獲取所有類型的鍵名
let keys = Reflect.ownKeys(person);
//["id", Symbol(name value), Symbol(job value), Symbol(address value)]
console.log(keys);