JS中Symbol類型的介紹與基本用法
1、 引入Symbol類型的背景
ES5 的對象屬性名都是字符串,這容易造成屬性名沖突的問題
舉例: 使用別人的模塊/對象, 又想為之添加新的屬性,這就容易使得新屬性名與原有屬性名沖突
2、Symbol類型簡介
symbol是一種原始數(shù)據(jù)類型
- 其余原始類型: 未定義(undefined) 、 空值(null)、布爾值(boolean)、字符串(string)、數(shù)值(number)、對象(object)
- symbol表示獨一無二的值
- symbol類型的"真實值"無法獲取,也就是說Symbol類型沒有對應(yīng)的字面量
- symbol類型的意義在于區(qū)分彼此和不重復(fù),不在于真實值
Symbol()是一種原生函數(shù)
- 常見的原生函數(shù)有String()、Number()、Boolean()、Array()、Object()、Function()、RegExp()、Date()、Error()、Symbol()
3、基本用法
符號需要使用Symbol()函數(shù)初始化。
let sym = Symbol(); // 因為符號本身是原始類型,所以typeof操作符對符號返回symbol console.log(typeof sym); // symbol
調(diào)用Symbol()函數(shù)時,也可以傳入一個字符串參數(shù)作為對符號的描述,將來可以通過這個字符串來調(diào)試代碼。但是,這個字符串參數(shù)與符號定義或標(biāo)識完全無關(guān):
// Symbol的值是唯一的,不會出現(xiàn)相同值的常量 let genericSymbol = Symbol(); let otherGenericSymbol = Symbol(); console.log(genericSymbol == otherGenericSymbol); // false // 可以傳入一個字符串參數(shù)作為對符號的描述 let fooSymbol = Symbol('foo'); let otherFooSymbol = Symbol('foo'); console.log(fooSymbol == otherFooSymbol); // false
符號沒有字面量語法。 按照規(guī)范,只要創(chuàng)建Symbol()實例并將其用作對象的新屬性,就可以保證它不會覆蓋已有的對象屬性,無論是符號屬性還是字符串屬性。
let genericSymbol = Symbol(); console.log(genericSymbol); // Symbol() let fooSymbol = Symbol('foo'); console.log(fooSymbol); // Symbol(foo)
Symbol()函數(shù)不能與new關(guān)鍵字一起作為構(gòu)造函數(shù)使用。
這樣做是為了避免創(chuàng)建符號包裝對象,像使用Boolean、String或Number那樣,它們都支持構(gòu)造函數(shù)且可用于初始化包含原始值的包裝對象。
let myBoolean = new Boolean(); console.log(typeof myBoolean); // "object" let myString = new String(); console.log(typeof myString); // "object" let myNumber = new Number(); console.log(typeof myNumber); // "object" let mySymbol = new Symbol(); // 報錯,TypeError console.log(mySymbol);
如果想使用符號包裝對象,可以借用Object()函數(shù):
let mySymbol = Symbol(); let myWarppedSymbol = Object(mySymbol); console.log(typeof myWarppedSymbol); // "object"
4、 使用全局符號注冊表
如果運行時的不同部分需要共享和重用符號實例,那么可以用一個字符串作為鍵,在全局符號注冊表中創(chuàng)建并重用符號。
Symbol.for()方法:
let fooGlobalSymbol = Symbol.for('foo'); console.log(typeof fooGlobalSymbol); // symbol
Symbol.for()對每個字符串鍵都執(zhí)行冪等操作。
第一次使用某個字符串調(diào)用時,它會檢查全局運行時注冊表,發(fā)現(xiàn)不存在對應(yīng)的符號,于是就會生成一個新符號實例并添加到注冊表中。
后續(xù)使用相同字符串的調(diào)用同樣會檢查注冊表,發(fā)現(xiàn)存在與該字符串對應(yīng)的符號,然后就會返回該符號實例。
// 創(chuàng)建新符號 let fooGlobalSymbol = Symbol.for('foo'); // 重用已有符號 let otherFooGlobalSymbol = Symbol.for('foo'); console.log(fooGlobalSymbol === otherFooGlobalSymbol); // true
采用相同符號,在全局注冊表中定義的符號跟使用Symbol()定義的符號也不等同:
// 使用Symbol()定義 let localSymbol = Symbol('foo'); // 使用全局注冊表定義 let globalSymbol = Symbol.for('foo'); console.log(localSymbol === globalSymbol); // false
全局注冊表中的符號必須使用字符串鍵來創(chuàng)建,因此作為參數(shù)傳給Symbol.for()的任何值都會被轉(zhuǎn)換為字符串。
注冊表中使用的鍵同時也會被用作符號描述。
let emptyGlobalSymbol = Symbol.for(); console.log(emptyGlobalSymbol); // Symbol(undefined)
使用Symbol.keyFor()來查詢?nèi)肿员?,這個方法接收符號,返回該全局符號對應(yīng)的字符串鍵。如果查詢的不是全局符號,則返回undefined。
// 創(chuàng)建全局符號 let s = Symbol.for('foo'); console.log(Symbol.keyFor(s)); // foo // 創(chuàng)建普通符號 let s2 = Symbol('bar'); console.log(Symbol.keyFor(s2)); // undefined
如果傳給Symbol.keyFor()的不是符號,則該方法拋出TypeError。
Symbol.keyFor(123); // TypeError: 123 is not a symbol
5、 使用符號作為屬性
凡是可以使用字符串或數(shù)值作為屬性的地方,都可以使用符號。
包括對象字面量屬性和 Object.defineProperty(obj, prop, descriptor) / Object.defineProperties() 定義的屬性。
對象字面量只能在計算屬性語法中使用符號作為屬性。
let s1 = Symbol('foo'), s2 = Symbol('bar'), s3 = Symbol('baz'), s4 = Symbol('qux'); let o = { // [屬性],會對屬性進(jìn)行讀取,并且轉(zhuǎn)換成字符串。[s1]是讀取了Symbol的字符串鍵'foo' [s1]: 'foo val' }; // 或 o[s1] = 'foo val'; console.log(o); // { [Symbol(foo)]: 'foo val' } Object.defineProperty(o, s2, { value: 'bar val' }); console.log(o); // {Symbol(foo): foo val, Symbol(bar): bar val} Object.defineProperties(o, { [s3]: { value: 'baz val' }, [s4]: { value: 'qux val' } }); console.log(o); // {Symbol(foo): foo val, Symbol(bar): baz val, // Symbol(foo): foo val, Symbol(bar): qux val}
let s1 = Symbol('foo'), s2 = Symbol('bar'); let o = { [s1]: 'foo val', [s2]: 'bar val', baz: 'baz val', qux: 'qux val' }; // Object.getOwnPropertySymbols()返回對象實例的符號屬性數(shù)組 console.log(Object.getOwnPropertySymbols(o)); // [ Symbol(foo), Symbol(bar) ] // Object.getOwnPropertyNames()返回對象實例的常規(guī)屬性數(shù)組 console.log(Object.getOwnPropertyNames(o)); // [ 'baz', 'qux' ] // Object.getOwnPropertyDescriptors()會返回同時包含常規(guī)和符號屬性描述符的對象 console.log(Object.getOwnPropertyDescriptors(o)); // { // baz: { // value: 'baz val', // writable: true, // enumerable: true, // configurable: true // }, // qux: { // value: 'qux val', // writable: true, // enumerable: true, // configurable: true // }, // [Symbol(foo)]: { // value: 'foo val', // writable: true, // enumerable: true, // configurable: true // }, // [Symbol(bar)]: { // value: 'bar val', // writable: true, // enumerable: true, // configurable: true // } // } // Reflect.ownKeys()會返回兩種類型的鍵 console.log(Reflect.ownKeys(o)); // [ 'baz', 'qux', Symbol(foo), Symbol(bar) ]
注意:Object.getOwnPropertyNames()和Object.getOwnProperty-Symbols()兩個方法的返回值彼此互斥。
因為符號屬性是對內(nèi)存中符號的一個引用,所以直接創(chuàng)建并用作屬性的符號不會丟失。
但是,如果沒有顯式地保存對這些屬性的引用,那么必須遍歷對象的所有符號屬性才能找到相應(yīng)的屬性鍵。
let o = { [Symbol('foo')]: 'foo val', [Symbol('bar')]: 'bar val' }; console.log(o); // { [Symbol(foo)]: 'foo val', [Symbol(bar)]: 'bar val' } let barSymbol = Object.getOwnPropertySymbols(o).find((Symbol) => Symbol.toString().match(/bar/)); console.log(barSymbol); // Symbol(bar)
6、 所有屬性
屬性 | 含義 |
---|---|
Symbol.asyncIterator | 符號指定了一個對象的默認(rèn)異步迭代器。如果一個對象設(shè)置了這個屬性,它就是異步可迭代對象,可用于for await…of循環(huán)。 |
Symbol.prototype.description | description 是一個只讀屬性,它會返回 Symbol 對象的可選描述的字符串。 |
Symbol.hasInstance | 用于判斷某對象是否為某構(gòu)造器的實例。因此你可以用它自定義 instanceof 操作符在某個類上的行為。 |
Symbol.isConcatSpreadable | 用于配置某對象作為Array.prototype.concat()方法的參數(shù)時是否展開其數(shù)組元素。 |
Symbol.iterator | 為每一個對象定義了默認(rèn)的迭代器。該迭代器可以被 for…of 循環(huán)使用。 |
Symbol.match | 指定了匹配的是正則表達(dá)式而不是字符串。String.prototype.match() 方法會調(diào)用此函數(shù) |
Symbol.matchAll | 內(nèi)置通用(well-known)符號指定方法返回一個迭代器,該迭代器根據(jù)字符串生成正則表達(dá)式的匹配項。此函數(shù)可以被 String.prototype.matchAll() 方法調(diào)用。 |
Symbol.replace | 這個屬性指定了當(dāng)一個字符串替換所匹配字符串時所調(diào)用的方法。 |
Symbol.search | 指定了一個搜索方法,這個方法接受用戶輸入的正則表達(dá)式,返回該正則表達(dá)式在字符串中匹配到的下標(biāo),這個方法由以下的方法來調(diào)用 String.prototype.search()。 |
Symbol.species | 知名的 Symbol.species 是個函數(shù)值屬性,其被構(gòu)造函數(shù)用以創(chuàng)建派生對象。 |
Symbol.split | 指向 一個正則表達(dá)式的索引處分割字符串的方法。這個方法通過 String.prototype.split() 調(diào)用。 |
Symbol.toPrimitive | 是內(nèi)置的 symbol 屬性,其指定了一種接受首選類型并返回對象原始值的表示的方法。它被所有的強類型轉(zhuǎn)換制算法優(yōu)先調(diào)用。 |
Symbol.toStringTag | 內(nèi)置通用(well-known)symbol 是一個字符串值屬性,用于創(chuàng)建對象的默認(rèn)字符串描述。它由 Object.prototype.toString() 方法內(nèi)部訪問。 |
Symbol.unscopables | 指用于指定對象值,其對象自身和繼承的從關(guān)聯(lián)對象的 with 環(huán)境綁定中排除的屬性名稱。 |
附:Symbol的使用場景
1、作為對象屬性 當(dāng)一個復(fù)雜對象中含有多個屬性的時候,很容易將某個屬性名覆蓋掉,利用 Symbol 值作為屬性名可以很好的避免這一現(xiàn)象
const name = Symbol('name'); const obj = { [name]: 'ClickPaas', }
2、ES6 中的類是沒有 private 關(guān)鍵字來聲明類的私有方法和私有變量的,但是我們可以利用 Symbol 的唯一性來模擬
const speak = Symbol(); class Person { [speak]() { console.log(123) } } let person = new Person() console.log(person[speak]())
因為使用者無法在外部創(chuàng)建出一個相同的 speak,所以就無法調(diào)用該方法
總結(jié)
到此這篇關(guān)于JS中Symbol類型的介紹與基本用法的文章就介紹到這了,更多相關(guān)JS Symbol類型用法內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Bootstrap3使用typeahead插件實現(xiàn)自動補全功能
這篇文章主要介紹了Bootstrap3使用typeahead插件實現(xiàn)自動補全功能的相關(guān)資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2016-07-07淺析如何在Bash中調(diào)用Node運行JS文件進(jìn)行數(shù)據(jù)通信
這篇文章主要來和大家探討在 Bash 中調(diào)用 Node 運行 JS 文件時如何進(jìn)行數(shù)據(jù)通信,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-03-03Java?@Schema和@ApiModel等注解的聯(lián)系淺析
這篇文章主要給大家介紹了關(guān)于Java?@Schema和@ApiModel等注解的聯(lián)系的相關(guān)資料,我在看公司之前的文檔,發(fā)現(xiàn)了@schema注解,不太了解,所以查詢了一些資料,把我的見解記錄下,需要的朋友可以參考下2023-08-08微信小程序如何修改本地緩存key中單個數(shù)據(jù)的詳解
這篇文章主要介紹了微信小程序如何修改本地緩存key中單個數(shù)據(jù),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04JavaScript中使用正則匹配多條,且獲取每條中的分組數(shù)據(jù)
該問題在使用Ajax遠(yuǎn)程獲取某網(wǎng)頁數(shù)據(jù)時經(jīng)常遇見 如果目標(biāo)頁面是XML,就好辦了,實用XMLDOM可以很輕松完成任務(wù)。2010-11-11