ES6中基礎範例增添到了7種,比上一個版本多了一個Symbol
,貌似湧現了很長時刻,但卻因沒有運用場景,一向看成一個觀點層來明白它,我想,用它的最好的體式格局,照樣要主動的去深切相識它吧,所以我從基礎部分和總結的有用場景來剖析這個特徵。已相識運用要領或許時刻緊急者能夠從有用場景一節最先瀏覽
base
起首,它給我的第一以為就是ES6做出了許多開釋言語特徵方面的轉變
,它能讓我們越發相識言語內部機制,Symbol以對象的鍵值定義,比方
let key = Symbol('test');
let obj = {};
obj[key] = 'alone';
obj[key]; // "alone"
Symbol正如其名,示意一個唯一的標示,以屬性的體式格局存在於對象當中,它吸收一個參數,沒有本質的作用,只是為了做一個形貌。以上我們經由過程直接量的體式格局來定義它,而且取值時,也須要運用key舉行讀取,假如湧現跨作用域的狀況,是不是是就不能獵取了?
function sent(key){
accept({[key]:"2018"})
}
function accept(obj) {
obj[???] //我怎樣拌?
}
以上兩個作用域中,假如不把key通報過來,是沒法讀取的,一個屬性還好,然則假如多了,那末靠參數通報key是不現實的. 在這類狀況下,我們能夠運用 Symbol.for
來為它再增加一個標示
,它接收一個參數String{key}。一般,它做為一個偏功用性的標記來示意,在全劇中它是唯一的。
function sent(key){
return accept({[key]:"2018"},key)
}
function accept(obj,key) {
console.log(Symbol.keyFor(key)) //CURRENT_YEAR
return obj[Symbol.for(Symbol.keyFor(key))] //CURRENT_YEAR
}
sent(Symbol.for('CURRENT_YEAR'))
而且運用 Symbol.for
來天生,會在存入當前全局高低文中一個<List>
構造中,我們稱它為GlobalSymbolRegistry
, 望文生義,它是全局的,所以運用key時我們須要鄭重,尤其是在大型項目中。
須要還注重以下幾點:
- 讀取它須要運用
getOwnPropertySymbols
要領,詳細請參看MDN - Symbol() !== Symbol()
but
Symbol.for(‘t’) === Symbol.for(‘t’) - GlobalSymbolRegistry對象存在於當前窗口歷程中,直到封閉窗口,才清撤除
現在的瀏覽器版本中把Symbol打印出來是字符串的花樣,並沒有顯現詳細的對象構造,我們能夠直接打印 Symbol,來檢察對應的prototype屬性以及內部要領,所以
Symbol().__proto__ === Symbol.prototype
在運用 Symbol 做key值時,它閱歷了以下步驟
- 假如指向對象是沒有定義的則拋出範例毛病
- 假如形貌符為undefined則為”
- 把形貌符轉換為String花樣
- 天生唯一的key,並返回
- 末了一步,把這個key賦給對象,並以Symbol(des)的體式格局顯現,其內部照樣以key為準,所以 Symbol() !== Symbol() ,即使他們看起來都是 字符串的”Symbol()”
所以如許寫也是能夠的,然則貌似沒有什麼意義
var n = 1;
var key = Symbol('numer')
n[key] = ‘Symbol Number’
n[key]的時刻把n隱式轉換成封裝對象,併為他增加Symbol,但並沒有方法去經由過程封裝對象回訪這個Symbol
除了純真的用key之外,在Symbol類下另有一些有意義的要領,following :
iterator
為指向對象增加 iterator 接口,比方運用數組解構
或許運用for of
,它接收一個generator函數
class IteratorExec {
constructor(){ this.count = 1 }
*[Symbol.iterator] = function* (){
yield this.count++;
yield this.count++;
yield this.count++;
}
}
let obj = new IteratorExec()
[...obj] //[1,2,3]
經由過程增加iterator
運用數據解構,還能夠運用for of
let values = [];
for (let value of obj) { values.push(value) }
values; //[1,2,3]
注:ES6中Map,Set,數組和增加了Iterator
接口的對象,具有Iterator接口.
asyncIterator
這不是ES6中的特徵,貌似放到了ES7中,能夠提早意淫一下以下代碼:
for await (const line of readLines(filePath)) {
console.log(line);
}
toPrimitive
在對對象範例舉行轉換時,會舉行一次 toPrimitive
,應用這個Symbol能夠轉變目的對象的轉換規則,轉變了之前的 “[object Object]”的牢固情勢
let obj = {
[Symbol.toPrimitive](hint){
switch(hint){
case 'number': return 5;
case 'string': return 'string';
case 'default': return 'default'
}
}
}
obj+11 // 'default11'
obj*2 // 10
這裏須要注重+ Number操縱是不屬於 ‘number’ 的,其他一般,如許就能夠定義轉對象範例的轉換規則了。
toStringTag
在javascript統統皆為對象,而在每一個對象中,都邑有一個內部屬性[[Class]]示意其對象範例,這在Symbol.toStringTag
,中是能夠修正的,也就是說 ‘[object Object]’ 後邊的字符串是可自定義的
let obj = {
[Symbol.toStringTag]:'custom'
}
Object.prototype.toString(obj); // [object Object]
obj.toString(); //[object custom]
一般我們運用Object.prototype.toString讀取對象屬性,恰是由於向後兼容,範例在對象自身的toString上完成了這類特徵,而老式要領照舊運用。然則我們能夠運用以下體式格局:
obj = {
[Symbol.toStringTag]:'custom'
get [Symbol.toStringTag](){
return 'custom'
}
}
Object.prototype.toString.call(obj)
我們把obj傳入實行toString,能夠到達這類結果,能夠料想es6中,Object.toString是遭到高低文的影響的. 明顯,我們上面的兩個例子都是獵取的Object.prototype.toString 二者有很大區分,只要它才正確轉換
,假如你的toString不全即是它,那是沒法轉換的,比方
var n = new Number();
n[Symbol.toStringTag] = 123;
n.toString(); // “0”
太稚子了,太無聊了?,Number私有的toString是直接把[[PrimitiveValue]]
轉換成了字符串,這裏人人要萬萬留意,不要誤認為一切的對象增加了Symbol.toStringTag
都能夠轉變,假如當前對象不是純對象,那末你能夠為此對象增加一個 getter
返回對應的範例,如許外部在運用Object…call的時,會獵取自定的範例。所以,這須要外部合營運用,你增加getter,人家不call你也是沒方法的。
別的Symbol暴露了幾種為原生對象定義了一些範例,比方
Math.toString(); //[object Math]
其他範例有 JSON, Promise, Map, TypedArray, DataView, ArrayBuffer, Genterator等等
unscopeables
const object1 = {
property1: 42
};
object1[Symbol.unscopables] = {
property1: true
};
with (object1) {
console.log(property1);
}
這個功用我以為可用性為0,基礎不必,with就是據對制止的.
hasInstance
關於 instance
運算符,為此操縱增加一個鈎子,第一參數是instance的左值,我們能夠返回true|false來定義運算符的返回值
var obj1 = {
[Symbol.hasInstance](instance){
return Array.isArray(Array)
}
}
class Array1 {
static [Symbol.hasInstance](instance) {
return Array.isArray(instance);
}
}
[] instance obj1 //true
console.log([] instanceof Array1); //true
isConcatSpreadable
示意[].concat是不是能夠睜開,默許是true.
let arr = [1,2];
arr.concat([3,4],5) //[1,2,3,4,5]
arr[Symbol.isConcatSpreadable] = false;
arr.concat([3,4],5) //[[1,2],3,4,5]
// 也能夠把[3,4]提出來處置懲罰
let arr2 = [3,4]
arr2[Symbol.isConcatSpreadable] = false;
arr.concat(arr2,5); //[[1,2],[3,4],5]
只要在數組中這個symbol屬性為false,concat操縱時,就不會去解構。那末是不是是意味着屬性設置為ture,沒有意義了?關於數組來講是的,由於它默許就是true,但是關於類數組對象,它另有一個小功用:
// (續)
arr.concat({length:2,0:3,1:4,[Symbol.isConcatSpreadable]:true}) //[1,2,3,4]
match & replace & split & search
一些字符串的操縱要領,一同都說了,也許都一個意義,就是接收一個對象,然後完成一個鈎子處置懲罰的函數,並返回其處置懲罰結果,它們都是能夠吸收正則的要領,在ES6之前,假如我們須要對字符串有比較複雜的操縱基礎上都是在要領外部的,必
class MyMatch {
[Symbol.match](string){return string.indexOf('world') }
}
'hello world'.match(new MyMatch()); //6
class MyReplace{
[Symbol.replace](string) {
return 'def'
}
}
'abcdef'.replace(new MyReplace(),'xxx'); //'abcxxx'
class mySplit {
[Symbol.split](val){
return val.split('-');
}
}
"123-123-123".split(new mySplit()); //['123','123','123']
class MySearch {
constructor(value) {
this.value = value;
}
[Symbol.search](string) {
return string.indexOf(this.value);
}
}
var fooSearch = 'foobar'.search(new MySearch('foo')); //0
var barSearch = 'foobar'.search(new MySearch('bar')); //3
var bazSearch = 'foobar'.search(new MySearch('baz')); //-1
practice
- 能夠經由過程Symbol完成以上的功用性要領,比方增加 Iterator 接口,讓對象隊友接口特徵,現實開闢中,我預計很少會用到,卻是以為
sanycIterator
是將來的遠景,現在還在草案階段 - 關於Symbol做為鍵值的作用,很為難,現實開闢中,這個我也沒運用過,現在為止,只須要記着它有
unique
性,比方我們想要在一個對象中增加兩個一樣的key名,這類需求很不罕見,
var firstPerson = Symbol("peter");
var secondPerson = Symbol("peter");
var persons = {[firstPerson]:"first", [secondPerson]:"pan"};
總結
Symbol
更多的是在運用和言語自身層面暴露更多的運用體式格局和特徵(on Object type),是的,它只以key的體式格局存在Object當中,在統統皆為對象中,它為 Next ECMScript Standard 供應了更多的可能性擴展性,這也是ES6中做的最大轉變方面之一,雖不經常使用但我們照樣要總結進修一下,以便在極度狀況下應變自若,假如有什麼文章中沒有涉及到的點,迎接補充! 注: 尤其是運用場景方面