在ES6之前,語(yǔ)言包含5中類型骡湖。字符串贱纠,數(shù)字,布爾响蕴,undefined, null.
ES6之后谆焊,包含了新類型Symbol類型。人們一般通過(guò)屬性名來(lái)訪問(wèn)屬性浦夷。無(wú)論屬性名是什么組成辖试,全部由字符串類型來(lái)進(jìn)行訪問(wèn)。私有名稱是為了讓開發(fā)者創(chuàng)建非字符串的名稱军拟。
1. 創(chuàng)建Symbol
除了Symbol剃执,都有各自字面量的形式⌒赶ⅲ可以通過(guò)全局Symbol創(chuàng)建Symbol
// firstName相當(dāng)于一個(gè)變量名稱肾档,不會(huì)轉(zhuǎn)化的變量名稱
let person = {};
let firstName = Symbol(); // firstName就是一個(gè)值,獨(dú)一無(wú)二的值辫继。
person[firstName] = "Nicholas";
console.log(person[firstName])
// 在以前的書寫中
let secondName = 'sN';
person[secondName] = "Nicholas";
console.log(person[secondName]) // 但是這里會(huì)轉(zhuǎn)化為person.sN
Symbole是原始值怒见。使用new Symbol會(huì)拋出錯(cuò)誤。
Symbole可以接受一個(gè)描述文本姑宽。但是這段描述不會(huì)用于屬性訪問(wèn)遣耍。但是建意我們?cè)趧?chuàng)建Symbole對(duì)象增加描述。
// firstName相當(dāng)于一個(gè)變量名稱炮车,不會(huì)轉(zhuǎn)化的變量名稱
let person = {};
let firstName = Symbol('first name'); // firstName就是一個(gè)值舵变,獨(dú)一無(wú)二的值。
person[firstName] = "Nicholas";
console.log(person[firstName]) // "Nicholas"
console.log('first name' in person) // false
console.log(firstName) // Symbol('first name')
Symbol的描述會(huì)被添加在[[Description]]屬性中瘦穆。只有當(dāng)調(diào)用Symbol的toString方法纪隙,描述才會(huì)被打印到日志中。
notice: Symbol是原始值扛或,可以進(jìn)行typeof操作符绵咱,返回Symbol類型。我們可以使用typeof查看瀏覽器對(duì)Symbol的支持情況
2. Symbol的使用方法
所有使用計(jì)算屬性名的地方都可以使用Symbol熙兔。Symbol可以適用于計(jì)算對(duì)象字面量屬性名悲伶、Object.defineProperties()方法中使用
let firstName = Symbol('first name');
let person = {[firstName]: 'Nicholas'};
Object.defineProperties(person, firstName, {writeable: false}) // 屬性設(shè)置為只讀
let lastName = Symbol('last name');
Object.defineProperties(person, {lastName: {value: 'Zakas', writeable: false}})
console.log(person[firstName], person[lastName]); // Nicholas Zakas
3. Symbol共享體系
我們希望在不同的代碼片段中共享一個(gè)Symbol。這時(shí)候住涉,ES6中提供了隨時(shí)可以訪問(wèn)的Symbol注冊(cè)表麸锉。如果想建立共享的Symbol,要使用Symbol.for()方法舆声。只接受一個(gè)參數(shù)淮椰,也就是即將創(chuàng)建的symbole的字符串標(biāo)識(shí)符。或者是描述
let uid = Symbol.for(uid);
let obj = {};
obj[uid] = "12345";
console.log(obj[uid]); // "12345"
console.log(uid); // Symbol(uid)
Symbol.for();會(huì)在全局的Symbol注冊(cè)表中搜索鍵為uid的Symbol主穗。如果存在,則返回值毙芜,不存在忽媒,則進(jìn)行注冊(cè),并且返回新的Symbol腋粥。
eg:
let uid = Symbol.for(uid);
let obj = {
[uid]: "12345";
};
console.log(obj[uid]); // "12345"
let uid2 = Symbol.for(uid);
console.log(uid === uid2); // "12345"
console.log(obj[uid]); // "12345"
console.log(uid2); // Symbol(uid)
還可以使用Symbol.keyFor()在Symbol全局注冊(cè)表中檢索與Symbol有關(guān)的鍵
let uid = Symbol.for(uid);
console.log(Symbol.keyFor(uid)); // uid
let uid2 = Symbol.for(uid);
console.log(Symbol.keyFor(uid2)); // uid
let uid3 = Symbol(uid);
console.log(Symbol.keyFor(uid3)); // undefined 因?yàn)闆](méi)有在全局的注冊(cè)表中注冊(cè)晦雨。所以不能搜到。
4. Symbol與類型強(qiáng)轉(zhuǎn)關(guān)系
因?yàn)槠渌禌](méi)有與Symbol邏輯等價(jià)的值隘冲,因?yàn)镾ymbol使用不太方便闹瞧。并且Symbol不能墻磚轉(zhuǎn)化為字符串和數(shù)字類型,可能會(huì)錯(cuò)誤操作對(duì)象屬性展辞,導(dǎo)致結(jié)果不一致奥邮。
let uid = Symbol('uid'), desc = String(uid);
console.log(uid); // Symbol('uid') 但是他是string類型的
// 但是不能直接與字符串拼接會(huì)報(bào)錯(cuò)
console.log(uid + ''); // 報(bào)錯(cuò)
// 不能強(qiáng)制轉(zhuǎn)化為數(shù)字類型
console.log(uid/1); // 報(bào)錯(cuò)
// 除了邏輯操作符 為true
5. Symbol屬性檢索
Symbol.keyFor()和Object.getOwnPropotyNames()都可以檢索對(duì)象中所有的屬性名。Symbol.keyFor()返回的是可枚舉的屬性名罗珍。Object.getOwnPropotyNames()返回所有屬性名洽腺,一律返回。這兩個(gè)都不可支持Symbol屬性覆旱。Object.getOwnPropotySymbols()放大來(lái)檢索對(duì)象中的Symbol屬性蘸朋。
let uid = Symbol.for(uid);
let obj = {
[uid]: "12345";
};
let symbols = Object.getOwnPropotySymbols(obj);
symbols.length // 1
symbols[0] // Symbol(uid)
obj[symbols[0]] // "12345"
// 對(duì)象中可以繼承Symbol屬性。
6. 通過(guò)well-konwn Symbol暴露內(nèi)部操作
6.1 Symbol.hasInstance方法
每個(gè)函數(shù)中都有Symbol.hasInstance方法扣唱,用于確定對(duì)象是否為函數(shù)實(shí)例藕坯。在Function.prototypes中定義,所有的函數(shù)都繼承了instance的默認(rèn)行為噪沙。為了確保Symbol.hasInstance不會(huì)被意外重寫炼彪,該方法定義為不可寫,不可配置并且不可枚舉曲聂。Symbol.hasInstance只接受被一個(gè)參數(shù)霹购,要檢查的值。
obj instance Array;
// 等價(jià)于
Array[Symbol.hasInstance](obj) // Symbol.hasInstance是個(gè)屬性朋腋?!!!就像Symbol(123)么揍障???
function MyObject() {
// 空函數(shù)
}
object.definePropoty(MyObject, Symbol.hasInstance, {
value: function(v) {
return false;
}
})
let obj = new MyObject();
obj.instanceof MyObject // false
建議不要改寫。
6.2 Symbol.isConcatSpreadable屬性
concat使用方法肛鹏。
let color1 = ['red', 'green'],
let color2 = color1.concat(['blue', 'black']);
let color2 = color1.concat(['yellow'], 'purple');
color2 // ['red', 'green', 'blue', 'black', 'yellow', 'purple']
按照道理复唤,會(huì)直接將參數(shù)一個(gè)一個(gè)的增加在新的數(shù)組中,為什么數(shù)組會(huì)展開呢?因?yàn)樵趈s中,凡是傳入了數(shù)組參數(shù),會(huì)自動(dòng)將他們分割為獨(dú)立元素。Symbol.isConcatSpreadable屬性是一個(gè)Bool值彼绷。true表示對(duì)象有l(wèi)ength屬性,和數(shù)字鍵,他的數(shù)字值型屬性值筹吐,會(huì)獨(dú)立添加到concat調(diào)用結(jié)果中。Symbol這個(gè)屬性默認(rèn)不會(huì)出現(xiàn)在標(biāo)準(zhǔn)對(duì)象中洋侨,是可選屬性舍扰。增加對(duì)象的concat()方法調(diào)用。
let collection = {
0: 'Hello',
1: 'world',
length: 2,
Symbol.isConcatSpreadable: true
}
let msg = ['Hi'].concat(collection);
msg // ['Hi', 'Hello', 'world']
6.3 Symbol.match希坚、Symbol.replace边苹、Symbol.search、Symbol.split屬性
接受正則表達(dá)式作為參數(shù)
.match(regex)判斷正則匹配
.replace(regex, replacement)正則表達(dá)式匹配結(jié)果
.search(regex)進(jìn)行定位
.split(regex)字符串分割
在es6之前是不能對(duì)這幾個(gè)方法進(jìn)行自定義裁僧,進(jìn)行字符串匹配个束。ES6之后用symbol屬性,將regex的元素特性完全暴露聊疲。
Symbol.match茬底、Symbol.replace、Symbol.search获洲、Symbol.split屬性表示match(), replace(), search(), split()方法中的第一個(gè)參數(shù)應(yīng)該調(diào)用的正則表達(dá)式參數(shù)的方法阱表。他們被定義在Regex.prototype中。
let regex2 = {
[Symbol.match]: function (value) {
return false;
}
};
'hello'.match(regex2)
6.4 Symbol.toPrimitive方法
js經(jīng)常做隱式轉(zhuǎn)換贡珊。比如 == 會(huì)轉(zhuǎn)化為基本類型最爬。但是到底轉(zhuǎn)戶為什么基本類型,在之前是內(nèi)部操作飞崖,但是es6之后通過(guò)Symbol.toPrimitive暴露出來(lái)了烂叔,并且可以對(duì)其修改。
Symbol.toPrimitive定義在所有常規(guī)類型的原型上,規(guī)定了轉(zhuǎn)化規(guī)則闸溃。當(dāng)轉(zhuǎn)化是會(huì)進(jìn)行調(diào)用,并傳入提示性參數(shù)坯苹。這個(gè)參數(shù)有3種可能逢防。number
string
default
number :調(diào)用valueof()方法叶沛,返回基本類型,返回它忘朝。否則調(diào)用toString()方法返回灰署,否則拋出錯(cuò)誤
string :調(diào)用toString()方法,返回基本類型局嘁,返回它溉箕。否則調(diào)用valueof()方法返回,否則拋出錯(cuò)誤
default : 多數(shù)在== + 運(yùn)算符或者傳遞單一參數(shù)給date構(gòu)造器被使用悦昵。
多數(shù)情況下肴茄,常規(guī)對(duì)象等價(jià)于數(shù)值模式?但指?寡痰??Date類型除外棋凳,使用string模式
function Templerature(value) {
this.value = value;
}
Templerature.prototype[Symbol.toPrimitive] = function(hint) {
switch (hint) {
case 'string':
return this.value+'!'
}
}
let tmp = new Templerature(32);
String(tmp)
"32!"
6.5 Symbol.toStringTag屬性
function isArray(value) {
return Object.prototype.toString.call(value) === "[object Array]";
}
console.log(isArray([])); // true
ES6通過(guò) Symbol.toStringTag 定義了 Object.prototype.toString.call() 被調(diào)用時(shí)應(yīng)當(dāng)返回什么值拦坠。對(duì)于數(shù)組來(lái)說(shuō),在Symbol.toStringTag 屬性中存儲(chǔ)了 "Array" 值剩岳,于是該函數(shù)的返回值也就是 "Array" 贞滨。
function Person(name) {
this.name = name;
}
Person.prototype[Symbol.toStringTag] = "Person";
let me = new Person("Nicholas");
console.log(me.toString()); // "[object Person]"
console.log(Object.prototype.toString.call(me)); // "[object Person]"
Symbol.toStringTag 的返回值在調(diào)用 me.toString() 的時(shí)候也會(huì)被使用。
function Person(name) {
this.name = name;
}
Person.prototype[Symbol.toStringTag] = "Person";
Person.prototype.toString = function() {
return this.name;
};
let me = new Person("Nicholas");
console.log(me.toString()); // "Nicholas"
console.log(Object.prototype.toString.call(me)); // "[object Person]"
6.6 Symbol.unscopables屬性
with語(yǔ)句的支持卢肃。