【ES6 筆記】Symbol和Symbol屬性

回憶一下JS中的原始類型:字符串型、數(shù)字型崩溪、布爾型、null和undefined。

ES6中引入了第6種原始類型:Symbol

創(chuàng)建Symbol

let firstName = Symbol();
let person = {};
person[firstName] = '歐陽不乖'
console.log(person[firstName]); //'歐陽不乖'

Symbol函數(shù)接受一個可選參數(shù)挺尾,可以添加一段文本描述即將創(chuàng)建的Symbol,這段屬描述不可用于屬性訪問站绪,但是建議每次創(chuàng)建Symbol時都添加一段描述遭铺,便于閱讀代碼和調(diào)試Symbol程序。

let firstName = Symbol('first name');
let person = {};
person[firstName] = '歐陽不乖';
console.log('first name' in person); //false
console.log(person[firstName]); // ''歐陽不乖
console.log(firstName); //Symbol('first name')

Symbol的描述被存儲在內(nèi)部的[[Description]]屬性中恢准,只有調(diào)用Symbol的toString()方法時才可以讀取這個屬性魂挂。在執(zhí)行console.log的時候隱式的調(diào)用了toString()方法。

  • Symbol的辨識方法
    Symbol是原始值馁筐,且ES6同時擴展了typeof操作符涂召,支持返回“Symbol”,所以可以用typeof來檢測變量是否為Symbol類型
let symbol = Symbol('test symbol');
 console.log(typeof symbol);  //'symnbol'      

Symbol的使用方法

所有使用可計算屬性名的地方敏沉,都可以使用Symbol果正。

let firstName = Symbol('first name');
let person = {
    //使用一個可計算對象字面量屬性
    [firstName] : '歐陽不乖'
}
//將屬性設(shè)置為只讀
Object.defineProperty( person, firstName, { writable : false});
console.log(person[firstName]); //'歐陽不乖'

Symbol共享體系

如果想創(chuàng)建一個可共享的Symbol,要使用Symbol.for()方法盟迟。它只接受一個參數(shù)秋泳,也就是即將創(chuàng)建的Symbol的字符串標(biāo)識符,這個參數(shù)同樣也被用作Symbol的描述:

let uid = Symbol.for('uid');
let object = {};
object[ uid ] = '12345';
console.log(object[uid]); //12345
console.log(uid); //Symbol(uid)

Symbol.for()方法首先在全局Symbol注冊表中搜索鍵為‘uid’的Symbol是否存在攒菠,如果存在迫皱,直接返回已有的Symbol;否則辖众,創(chuàng)建一個新的Symbol卓起,并使用這個鍵在Symbol全局注冊表中注冊,隨機返回新創(chuàng)建的Symbol凹炸。
后續(xù)如果再傳入同樣的鍵調(diào)用Symbol.for()會返回相同的Symbol:

let uid = Symbol.for('uid');
let uid2 = Symbol.for('uid');
let object = {
     [uid] : '12345' 
} ;
console.log(uid === uid2); //true
console.log(object[uid]);  //12345
console.log(object[uid2]);  //12345

還有一個與Symbol共享有關(guān)的特性:可以使用Symbol.keyFor()方法在Symbol全局注冊表中檢索與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

Symbol全局注冊表是一個類似全局作用域的共享環(huán)境戏阅,也就是說你不能假設(shè)目前環(huán)境中存在哪些鍵

Symbol與類型強制轉(zhuǎn)換

由于其他類型沒有與Symbol邏輯等價的值,所以不能將Symbol強制轉(zhuǎn)換為字符串或是數(shù)字類型还惠。
在使用console.log()方法來輸出Symbol的內(nèi)容時饲握,它會調(diào)用Symbol的String()方法并輸出有用的信息。也可以像下面這樣直接調(diào)用String()方法來獲取相同的內(nèi)容:

let uid = Symbol.for('uid'),
    desc = String(uid);
console.log(desc); //Symbol(uid)

String()函數(shù)調(diào)用了uid.toString()方法蚕键,返回字符串類型的Symbol描述內(nèi)容救欧,但是,如果將Symbol與一個字符串拼接會導(dǎo)致程序拋出錯誤:

let uidDesc = Symbol.for('uid') + ''; //報錯

Symbol不可以被轉(zhuǎn)為字符串锣光,同樣也不能轉(zhuǎn)為數(shù)字類型:

let uidSum = Symbol.for('uid') /1; //報錯

只有在使用邏輯操作符的時候笆怠,Symbol可以正常運行,因為Symbol與JS中的非空值類似誊爹,其等價布爾值為true

Symbol屬性檢索

Object.keys()和Object.getOwnPropertyNames()方法可以檢索對象中所有的屬性名:前一個方法返回所有的可枚舉屬性名蹬刷;后一個方法不考慮屬性的可枚舉性一律返回瓢捉。在ES6中新增一個Object.getOwnPropertySymbols()方法來檢索對象中的Symbol屬性。
Object.getOwnPropertySymbols()方法的返回值是一個包含所有Symbol自有屬性的數(shù)組:

let uid = Symbol.for('uid');
let object = {
     [uid]:12345
}
let symbols = Object.getOwnPropertySymbols(object);
console.log(symbols.length); //1
console.log(symbols[0]);  //Symbol('uid')
console.log(object[symbols[0]]);   //12345

通過well-known Symbol暴露內(nèi)部操作

  • Symbol.hasInstance:一個在執(zhí)行instanceof時調(diào)用的內(nèi)部方法办成,用來檢測對象的繼承信息泡态。
    Symbol.hasInstance方法只接受一個參數(shù),即要檢查的值迂卢。如果傳入的值是函數(shù)的實例某弦,則返回true:
let arr = []
console.log(arr instanceof Array);  //true
// 等價于
console.log(Array[Symbol.hasInstance](arr));  //true

本質(zhì)上,ES6只是將instanceof操作符重新定義為此方法的簡寫語法而克,現(xiàn)在引入方法調(diào)用以后靶壮,就可以隨便改變instanceof的運行方式了:

function MyObject(){
    // empty
}
Object.defineProperty(MyObject, Symbol.hasInstance,{
    value : function(v){
          //console.log(v);   //MyObject {}
          return false;
    }
})
let obj = new MyObject();
console.log(obj instanceof MyObject );  // false
/*
* obj實際上是MyObject的實例,但是我們將Symbol.hasInstance的返回值硬編碼為false以后
* 即使使用instanceof運算符也只是返回false
*/

我們可以按照自己的喜好任意重構(gòu)Symbol.hasInstance员萍,但是改寫源碼會造成不可預(yù)期的后果腾降,所以請在必要的情況下只改寫自己聲明的函數(shù)Symbol.hasInstance屬性

  • Symbol.isConcatSpreadable:一個布爾值,用于表示當(dāng)傳遞一個集合作為Array.prototype.concat()方法的參數(shù)時碎绎,是否應(yīng)該將合集內(nèi)的元素規(guī)整到同一層級螃壤。
    JS數(shù)組的concat方法被設(shè)計用于拼接兩個數(shù)組,不但接受數(shù)組參數(shù)筋帖,也可以接收非數(shù)組參數(shù):
let colors1 = ['red'];
let colors2 = ['green'];
let color3 = 'black';
console.log(colors1.concat(colors2, color3)); //["red", "green", "black"]

JS規(guī)范聲明映穗,凡是傳入了數(shù)組的參數(shù),就會自動將他們分解為獨立元素幕随。
Symbol.isConcatSpreadable屬性是一個布爾值,如果該屬性值為true宿接,則表示對象有l(wèi)ength屬性和數(shù)字鍵赘淮,故它的數(shù)值型屬性值應(yīng)該被獨立添加到concat調(diào)用的結(jié)果中。這個屬性默認情況下不會出現(xiàn)在標(biāo)準(zhǔn)對象中睦霎,它只是可選屬性梢卸,用于增強作用于特定對象類型的concat方法的功能,有效簡化其默認特性:

let collection = {
      0:'Hello',
      1:'World',
      length:2,
      [Symbol.isConcatSpreadable]:true
}
let message = ['Hi'].concat(collection);
console.log(message.length); //3
console.log(message); //["Hi", "Hello", "World"]

// 假設(shè)將 [Symbol.isConcatSpreadable]:false改為這樣副女,那么運行結(jié)果就變?yōu)榱耍?/ * 
* console.log(message);
* ['Hi', {
*       0:'Hello',
*       1:'World',
*       length:2,
*       [Symbol.isConcatSpreadable]:true
* }]
* /
  • Symbol.match:一個在調(diào)用String.prototype.match()方法時調(diào)用的方法蛤高,用于比較字符串。接受一個字符串類型的參數(shù)碑幅,如果匹配成功則返回匹配元素的數(shù)組戴陡,否則返回null。
  • Symbol.replace:一個在調(diào)用String.prototype.replace()方法時調(diào)用的方法沟涨,用于替換字符串的子串恤批。接受一個字符串類型的參數(shù)和一個替換用的字符串,最終依然返回一個字符串裹赴。
  • Symbol.search:一個在調(diào)用String.prototype.search()方法時調(diào)用的方法喜庞,用于在字符串中定位子串诀浪。接受一個字符串類型的參數(shù),如果匹配到則返回數(shù)字索引延都,否則返回 -1 雷猪。
  • Symbol.split:一個在調(diào)用String.prototype.split()方法時調(diào)用的方法,用于分割字符串晰房。接受一個字符串參數(shù)求摇,根據(jù)匹配內(nèi)容將字符串分解,并返回一個包含分解后片段的數(shù)組嫉你。
    在JS中字符串與正則表達式經(jīng)常一起使用月帝,尤其是字符串類型的幾個方法,可以接受正則表達式作為參數(shù):
    1. match(regex) 確定給定字符串是否匹配正則表達式regex
    2. replace(regex幽污,replacement) 將字符串中匹配正則表達式的regex部分替換為replacement
    3. search(regex) 在字符串中定位匹配正則表達式regex的位置索引
    4. split(regex) 按照匹配正則表達式regex的元素將字符串分切嚷辅,并將結(jié)果存入數(shù)組
    在ES6之前,以上4個方法無法使用開發(fā)者自定義的對象來替代正則表達式進行字符串匹配距误。而在ES6中定義了與上邊4個方法相對應(yīng)的4個Symbol簸搞,將語言內(nèi)建的RegExp對象的原生特性完全暴露出來。
// 實際上等價于 /^.{10}$/
let hasLengthOf10 = {
    [Symbol.match]:function(value){
        return value.length ===10 ? [ value ] : null ;
    },
    [Symbol.replace]:function(value, replacement){
        return value.length ===10 ? replacement : value ;
    },
    [Symbol.search]:function(value){
        return value.length ===10 ? 0 : -1 ;
    },
    [Symbol.split]:function(value){
        return value.length ===10 ? [ ,  ] : [ value ] ;
    },  
};
let message1 = 'Hello world'; //11個字符
let message2 = 'Hello 1234'; //10個字符

console.log( message1.match(hasLengthOf10) ); //null
console.log( message2.match(hasLengthOf10) ); //["Hello 1234"]

console.log( message1.replace(hasLengthOf10,'歐陽不乖') ); //Hello world
console.log( message2.replace(hasLengthOf10,'歐陽不乖') ); //歐陽不乖

console.log( message1.search(hasLengthOf10) ); // -1
console.log( message2.search(hasLengthOf10) ); // 0

console.log( message1.split(hasLengthOf10) ); //["Hello world"]
console.log( message2.split(hasLengthOf10) ); // [ ,  ] 注意這里原書寫的['','']個人認為二者有區(qū)別
  • Symbol.toPrimitive:該方法被定義在每一個標(biāo)準(zhǔn)類型的原型上准潭,并且規(guī)定了當(dāng)對象被轉(zhuǎn)換為原始值時應(yīng)該執(zhí)行的操作趁俊。
    該方法接受一個值作為參數(shù),該值在規(guī)范中被稱為“類型提示(hint)”,分別是:number刑然、string或default寺擂,對應(yīng)的返回分別是數(shù)字、字符串活無類型偏好的值泼掠。
    對于大多數(shù)標(biāo)準(zhǔn)對象怔软,數(shù)字模式有以下的特性,根據(jù)優(yōu)先級的順序排列如下:
    1.調(diào)用valueOf()方法择镇,結(jié)果為原始值挡逼,則返回;
    2.否則調(diào)用toString()方法腻豌,結(jié)果為原始值家坎,則返回;
    3.如果再無可選值吝梅,則拋出錯誤虱疏。
    對于大多數(shù)標(biāo)準(zhǔn)對象,字符串模式有以下優(yōu)先級排序:
    1.調(diào)用toString()方法憔涉,結(jié)果為原始值订框,則返回;
    2.否則調(diào)用valueOf()方法兜叨,結(jié)果為原始值穿扳,則返回衩侥;
    3.如果再無可選值,則拋出錯誤矛物。
    在大多數(shù)情況下茫死,標(biāo)準(zhǔn)對象會將默認模式按數(shù)字模式處理(除了Date對象,在這種情況下履羞,會將默認模式按字符串模式處理)
function Temperature(degrees){
    this.degrees = degrees;
}
Temperature.prototype[Symbol.toPrimitive] = function(hint){
    switch (hint){
        case 'string' : 
                return this.degrees + '\u00b0' ; //degrees symbol
        case 'number':
                return this.degrees;
        case 'default':
                return this.degrees + '度'
    }
}
var freezing = new Temperature(32);

console.log( freezing/2 ) //16
console.log(String(freezing) ) //32°
console.log( freezing + '!' );  //32度!
  • Symbol.toStringTag:一個在調(diào)用Object.prototype.toString()方法時使用的字符串峦萎,用于創(chuàng)建對象描述。
  • Symbol.unscopables:一個定義了一些不可被with語句引用的對象屬性名稱的對象集合忆首。

Symbol 的一些小擴展

let firstName = Symbol('歐陽不乖');
let lastName  ='Hello';
let person = {
    [firstName]:'愛誰誰',
    [lastName]:'World'
}

console.log( person.firstName ); // undefined
console.log( person[Symbol('歐陽不乖')] ) //undefined
console.log( person[firstName] ); // 愛誰誰

console.log( person.last ); // undefined
console.log( person[lastName] ); // World

console.log( firstName ); // Symbol('歐陽不乖')
console.log( Symbol('歐陽不乖') ); // Symbol('歐陽不乖')
console.log( firstName==Symbol('歐陽不乖') ); // false

console.log( String(Symbol('歐陽不乖'))+'yes' ); //Symbol(歐陽不乖)yes

console.log( Symbol('歐陽不乖')=== Symbol('歐陽不乖')  ); //false
console.log( Symbol('歐陽不乖')== Symbol('歐陽不乖')  ); //false

console.log( Object.is( Symbol('歐陽不乖'), Symbol('歐陽不乖')) );  //false

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末爱榔,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子糙及,更是在濱河造成了極大的恐慌详幽,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件浸锨,死亡現(xiàn)場離奇詭異唇聘,居然都是意外死亡,警方通過查閱死者的電腦和手機柱搜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進店門迟郎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人聪蘸,你說我怎么就攤上這事宪肖。” “怎么了健爬?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵匈庭,是天一觀的道長。 經(jīng)常有香客問我浑劳,道長,這世上最難降的妖魔是什么所灸? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任台汇,我火速辦了婚禮三圆,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蒜绽。我一直安慰自己,他們只是感情好桶现,可當(dāng)我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布躲雅。 她就那樣靜靜地躺著,像睡著了一般骡和。 火紅的嫁衣襯著肌膚如雪相赁。 梳的紋絲不亂的頭發(fā)上相寇,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天,我揣著相機與錄音钮科,去河邊找鬼唤衫。 笑死,一個胖子當(dāng)著我的面吹牛绵脯,可吹牛的內(nèi)容都是我干的佳励。 我是一名探鬼主播,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蛆挫,長吁一口氣:“原來是場噩夢啊……” “哼赃承!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起悴侵,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤瞧剖,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后畜挨,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體筒繁,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年巴元,在試婚紗的時候發(fā)現(xiàn)自己被綠了毡咏。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡逮刨,死狀恐怖呕缭,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情修己,我是刑警寧澤恢总,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站睬愤,受9級特大地震影響片仿,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜尤辱,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一砂豌、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧光督,春花似錦阳距、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春咖熟,著一層夾襖步出監(jiān)牢的瞬間圃酵,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工球恤, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留辜昵,地道東北人。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓咽斧,卻偏偏與公主長得像堪置,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子张惹,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,700評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 第2章 基本語法 2.1 概述 基本句法和變量 語句 JavaScript程序的執(zhí)行單位為行(line)舀锨,也就是一...
    悟名先生閱讀 4,148評論 0 13
  • 概述 ES5的對象屬性名都是字符串,這容易造成屬性名的沖突宛逗。比如坎匿,你使用了一個他人提供的對象,但又想為這個對象添加...
    oWSQo閱讀 516評論 1 3
  • 第5章 引用類型(返回首頁) 本章內(nèi)容 使用對象 創(chuàng)建并操作數(shù)組 理解基本的JavaScript類型 使用基本類型...
    大學(xué)一百閱讀 3,233評論 0 4
  • 一雷激、高手的暗箱:利用規(guī)律替蔬,放大努力 沒有一個人是僅憑借努力、天賦屎暇、機遇而獲得巨大 成功的承桥,躍遷式的成功都是利用了更...
    造塔人閱讀 383評論 0 0
  • 我為什么而活 為了山澗潺潺流動的清泉 為了漫山姹紫嫣紅的花朵 為了林間婉轉(zhuǎn)悅耳的天籟 我為什么而活 為了體驗和感知...
    林清萓閱讀 355評論 6 9