Javascript Symbol原理及使用方法解析
Symbol是ES6中新引入的一種基本數(shù)據(jù)類型,在此之前JavaScript中已有幾種基本數(shù)據(jù)類型:
- Numberg
- String
- Boolean
- Null
- Undefined
- Object
不同于其他基本類型的通俗易懂,Symbol 是什么和有什么用一直有些讓人困惑。
什么是Symbol
JavaScript標(biāo)準(zhǔn)中規(guī)定對象的key只能是 String 或 Symbol 類型,區(qū)別在于 String 類型的key可以重復(fù)而 Symbol 類型的key是唯一的。Symbol 的本質(zhì)是表示一個(gè)唯一標(biāo)識。每次創(chuàng)建一個(gè)Symbol,它所代表的值都不可能重復(fù),該值的內(nèi)部實(shí)現(xiàn)可以視為一段數(shù)字(類似:3423498431987719455..)。所以理論上 Symbol 的存在只有一個(gè)意義:用于必須使用唯一值的場景。
創(chuàng)建Symbol
創(chuàng)建 Number、String等基本類型的實(shí)例有兩種方法:通過構(gòu)造函數(shù)(或者叫工廠函數(shù))和文字語法糖。比如:
// 構(gòu)造函數(shù)
const num = Number(3);
const str = String('hi');// 語法糖
const num = 3;
const str = 'hi';
顯然使用語法糖更加簡潔。但是 Symbol 只能通過構(gòu)造函數(shù) Symbol() 進(jìn)行創(chuàng)建:
const sym = Symbol();
或者,我們可以傳入一個(gè)字符串參數(shù)(descriptor)用于描述該Symbol:
const sym = Symbol('cat');
注意:傳入的參數(shù)對 Symbol 值的產(chǎn)生并無影響,因?yàn)榫退忝看蝹魅氲膮?shù)都一樣,生成的Symbol值也是不等的。該參數(shù)的作用僅用于描述被創(chuàng)建的Symbol,以便debug時(shí)可以識別出Symbol的含義。 所以,下列等式結(jié)果為 false:
Symbol('cat') === Symbol('cat') // false
Symbol.for(key)
和 Symbol() 類似,Symbol.for(key) 也可以創(chuàng)建一個(gè)Symbol,不一樣的是:創(chuàng)建的 Symbol 是全局的(在全局Symbol表中注冊),而如果全局已經(jīng)存在相同 key 的Symbol,則直接返回該Symbol。所以,下列等式結(jié)果為 true:
Symbol.for('cat') === Symbol.for('cat') // true
如何使用Symbol
其實(shí) Symbol 本身很簡單,但是如何把它用好、且用的恰到好處卻使人困惑,因?yàn)樵谄匠9ぷ髦胁]有多少非Symbol不用的場景。但是用對了Symbol會(huì)對你的代碼質(zhì)量有不少提升。來看下面幾種案例:
1. 用作對象的key,防止命名沖突
使用Symbol作為Object的key,可以保證和其他key都不重復(fù)。因此,Symbol非常適合用于對對象的屬性進(jìn)行拓展。
比如,當(dāng)使用 String 作為對象的key時(shí),一旦出現(xiàn)重復(fù)的key則后面的屬性會(huì)覆蓋前面的:
const persons = { 'bruce': 'wayne', 'bruce': 'banner' } console.log(persons.bruce); // 'wayne' 使用Symbol作為Key可以避免這種情況: const bruce1 = Symbol('bruce'); const bruce2 = Symbol('bruce'); const persons = { [bruce1]: 'wayne', [bruce2]: 'banner' } console.log(persons[bruce1]); // 'wayne' console.log(persons[bruce2]); // 'banner'
js很多內(nèi)建的方法都是通過 Symbol 進(jìn)行指定的,比如:Symobol.iterator 指定了一個(gè)iterable對象的迭代器方法;Symbol.replace 指定了對象字符串替換的方法,這類 Symbol 被稱為 Well-know Symbols,代表了js語言的內(nèi)部行為。
2. 使用Symbol定義枚舉
由于Javascript并不自帶枚舉類型,通常情況下我們會(huì)使用一個(gè)freezed的Object來模擬枚舉類型,比如定義一個(gè)日期的枚舉:
const DAYS = Object.freeze({
monday: 1,
tuesday: 2,
wednesday: 3
});
此時(shí)有一個(gè)方法,接收 DAYS 的枚舉值來返回當(dāng)天要做的事:
function getTodo(day) { switch (day) { case DAYS.monday: return "看電影"; case DAYS.tuesday: return "購物"; case DAYS.wednesday: return "健身"; default: return "日期錯(cuò)誤"; } }
我們希望代碼邏輯足夠嚴(yán)謹(jǐn),傳入的參數(shù)嚴(yán)格按照 DAYS.monday 的形式,否則就返回日期錯(cuò)誤,但是該枚舉類型的實(shí)現(xiàn)卻做不到。比如:getTodo(1) 依然能得到 “看電影” 這個(gè)結(jié)果。
但是使用Symbol卻可以解決這一問題,DAYS 枚舉類型可以重新定義為:
const DAYS = Object.freeze({
monday: Symbol('monday'),
tuesday: Symbol('tuesday'),
wednesday: Symbol('wednesday')
});
此時(shí) getTodo 方法必須接收 DAYS.monday 這樣的枚舉值作為參數(shù),否則就返回 “日期錯(cuò)誤”,因?yàn)槭澜缟显贈(zèng)]有任何一個(gè)值和 DAYS.monday 相等了。
這樣定義枚舉顯然更嚴(yán)謹(jǐn)了。
3. 使用Symbol存儲元數(shù)據(jù)
Key為Symbol類型的屬性是不能被枚舉的,這是 Symbol 除了唯一性外的第二大特性,因此使用for...in,Object.keys()、Object.hasOwnProperty()等方法不能識別Symbol屬性,簡而言之Symbol屬性對用戶是“隱藏”的(但并不是private的,因?yàn)橛衅渌緩娇梢垣@取Symbol屬性),例如:
因此Symbol作為“隱藏”屬性可以用來存儲對象的元數(shù)據(jù)。比如,有一個(gè) TodoList:
class TodoList { constructor() { // todo數(shù)量 this.count = 0; } // 增加todo add(id, content) { this[id] = content; this.count++; } } const list = new TodoList();
我們使用 add() 方法向其中增加幾個(gè)todo:
list.add('a', '看電影');
list.add('b', '購物');
list.add('c', '健身');
當(dāng)我們想使用 for...in 查看里面所有的todo時(shí),會(huì)把 count 屬性也帶出來:
為了隱藏count屬性,更方便的對todo進(jìn)行操作,我們可以使用Symbol來存儲它,TodoList 類修改為:
const count = Symbol('count');
class TodoList {
constructor() {
this[count] = 0;
}add(id, content) {
this[id] = content;
this[count]++;
}
}
當(dāng)我們再遍歷 TodoList 的時(shí)候,count就隱藏了:
當(dāng)我們想獲取存儲在Symbol中的原數(shù)據(jù)時(shí),可以使用 Object.getOwnPropertySymbols() 方法:
以上是我能想到的 Symbol 的用途,如果大家有其他心得體會(huì)歡迎補(bǔ)充。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
JavaScript之iterable_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要介紹了JavaScript之iterable,遍歷Array可以采用下標(biāo)循環(huán),遍歷Map和Set就無法使用下標(biāo)。為了統(tǒng)一集合類型,ES6標(biāo)準(zhǔn)引入了新的iterable類型,Array、Map和Set都屬于iterable類型2017-06-06js實(shí)現(xiàn)圖片360度旋轉(zhuǎn)
本文主要介紹了js實(shí)現(xiàn)圖片360度旋轉(zhuǎn)的思路與方法。具有很好的參考價(jià)值,下面跟著小編一起來看下吧2017-01-01JS解決?Array.fill()參數(shù)為對象指向同一個(gè)引用地址的問題
這篇文章主要介紹了JS解決?Array.fill()參數(shù)為對象指向同一個(gè)引用地址問題,解決方案使用map返回出不同的引用的地址,fill參數(shù)可隨意填寫(不為空),主要是map函數(shù)中返回的數(shù)據(jù),需要的朋友可以參考下2023-02-02JS實(shí)現(xiàn)多張圖片預(yù)覽同步上傳功能
這篇文章主要介紹了JS實(shí)現(xiàn)多張圖片預(yù)覽同步上傳功能的相關(guān)資料,需要的朋友可以參考下2017-06-06解決echarts的多個(gè)折現(xiàn)數(shù)據(jù)出現(xiàn)坐標(biāo)和值對不上的問題
這篇文章主要介紹了解決echarts的多個(gè)折現(xiàn)數(shù)據(jù)出現(xiàn)坐標(biāo)和值對不上的問題,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-12-12js技巧之十幾行的代碼實(shí)現(xiàn)vue.watch代碼
相信很多的用vue的人都知道vue雙向綁定的原理建立在,給屬性綁定了getter和setter,在屬性被改變的同時(shí)觸發(fā)視圖的再渲染。而本期也是借助這兩個(gè)內(nèi)置方法實(shí)現(xiàn)vue內(nèi)的watch2018-06-06