JS中Symbol類型的介紹與基本用法
1、 引入Symbol類型的背景
ES5 的對(duì)象屬性名都是字符串,這容易造成屬性名沖突的問題
舉例: 使用別人的模塊/對(duì)象, 又想為之添加新的屬性,這就容易使得新屬性名與原有屬性名沖突
2、Symbol類型簡介
symbol是一種原始數(shù)據(jù)類型
- 其余原始類型: 未定義(undefined) 、 空值(null)、布爾值(boolean)、字符串(string)、數(shù)值(number)、對(duì)象(object)
- symbol表示獨(dú)一無二的值
- symbol類型的"真實(shí)值"無法獲取,也就是說Symbol類型沒有對(duì)應(yīng)的字面量
- symbol類型的意義在于區(qū)分彼此和不重復(fù),不在于真實(shí)值
Symbol()是一種原生函數(shù)
- 常見的原生函數(shù)有String()、Number()、Boolean()、Array()、Object()、Function()、RegExp()、Date()、Error()、Symbol()
3、基本用法
符號(hào)需要使用Symbol()函數(shù)初始化。
let sym = Symbol(); // 因?yàn)榉?hào)本身是原始類型,所以typeof操作符對(duì)符號(hào)返回symbol console.log(typeof sym); // symbol
調(diào)用Symbol()函數(shù)時(shí),也可以傳入一個(gè)字符串參數(shù)作為對(duì)符號(hào)的描述,將來可以通過這個(gè)字符串來調(diào)試代碼。但是,這個(gè)字符串參數(shù)與符號(hào)定義或標(biāo)識(shí)完全無關(guān):
// Symbol的值是唯一的,不會(huì)出現(xiàn)相同值的常量
let genericSymbol = Symbol();
let otherGenericSymbol = Symbol();
console.log(genericSymbol == otherGenericSymbol); // false
// 可以傳入一個(gè)字符串參數(shù)作為對(duì)符號(hào)的描述
let fooSymbol = Symbol('foo');
let otherFooSymbol = Symbol('foo');
console.log(fooSymbol == otherFooSymbol); // false
符號(hào)沒有字面量語法。 按照規(guī)范,只要?jiǎng)?chuàng)建Symbol()實(shí)例并將其用作對(duì)象的新屬性,就可以保證它不會(huì)覆蓋已有的對(duì)象屬性,無論是符號(hào)屬性還是字符串屬性。
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)建符號(hào)包裝對(duì)象,像使用Boolean、String或Number那樣,它們都支持構(gòu)造函數(shù)且可用于初始化包含原始值的包裝對(duì)象。
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(); // 報(bào)錯(cuò),TypeError console.log(mySymbol);
如果想使用符號(hào)包裝對(duì)象,可以借用Object()函數(shù):
let mySymbol = Symbol(); let myWarppedSymbol = Object(mySymbol); console.log(typeof myWarppedSymbol); // "object"
4、 使用全局符號(hào)注冊(cè)表
如果運(yùn)行時(shí)的不同部分需要共享和重用符號(hào)實(shí)例,那么可以用一個(gè)字符串作為鍵,在全局符號(hào)注冊(cè)表中創(chuàng)建并重用符號(hào)。
Symbol.for()方法:
let fooGlobalSymbol = Symbol.for('foo');
console.log(typeof fooGlobalSymbol); // symbol
Symbol.for()對(duì)每個(gè)字符串鍵都執(zhí)行冪等操作。
第一次使用某個(gè)字符串調(diào)用時(shí),它會(huì)檢查全局運(yùn)行時(shí)注冊(cè)表,發(fā)現(xiàn)不存在對(duì)應(yīng)的符號(hào),于是就會(huì)生成一個(gè)新符號(hào)實(shí)例并添加到注冊(cè)表中。
后續(xù)使用相同字符串的調(diào)用同樣會(huì)檢查注冊(cè)表,發(fā)現(xiàn)存在與該字符串對(duì)應(yīng)的符號(hào),然后就會(huì)返回該符號(hào)實(shí)例。
// 創(chuàng)建新符號(hào)
let fooGlobalSymbol = Symbol.for('foo');
// 重用已有符號(hào)
let otherFooGlobalSymbol = Symbol.for('foo');
console.log(fooGlobalSymbol === otherFooGlobalSymbol); // true
采用相同符號(hào),在全局注冊(cè)表中定義的符號(hào)跟使用Symbol()定義的符號(hào)也不等同:
// 使用Symbol()定義
let localSymbol = Symbol('foo');
// 使用全局注冊(cè)表定義
let globalSymbol = Symbol.for('foo');
console.log(localSymbol === globalSymbol); // false
全局注冊(cè)表中的符號(hào)必須使用字符串鍵來創(chuàng)建,因此作為參數(shù)傳給Symbol.for()的任何值都會(huì)被轉(zhuǎn)換為字符串。
注冊(cè)表中使用的鍵同時(shí)也會(huì)被用作符號(hào)描述。
let emptyGlobalSymbol = Symbol.for(); console.log(emptyGlobalSymbol); // Symbol(undefined)
使用Symbol.keyFor()來查詢?nèi)肿?cè)表,這個(gè)方法接收符號(hào),返回該全局符號(hào)對(duì)應(yīng)的字符串鍵。如果查詢的不是全局符號(hào),則返回undefined。
// 創(chuàng)建全局符號(hào)
let s = Symbol.for('foo');
console.log(Symbol.keyFor(s)); // foo
// 創(chuàng)建普通符號(hào)
let s2 = Symbol('bar');
console.log(Symbol.keyFor(s2)); // undefined
如果傳給Symbol.keyFor()的不是符號(hào),則該方法拋出TypeError。
Symbol.keyFor(123); // TypeError: 123 is not a symbol
5、 使用符號(hào)作為屬性
凡是可以使用字符串或數(shù)值作為屬性的地方,都可以使用符號(hào)。
包括對(duì)象字面量屬性和 Object.defineProperty(obj, prop, descriptor) / Object.defineProperties() 定義的屬性。
對(duì)象字面量只能在計(jì)算屬性語法中使用符號(hào)作為屬性。
let s1 = Symbol('foo'),
s2 = Symbol('bar'),
s3 = Symbol('baz'),
s4 = Symbol('qux');
let o = {
// [屬性],會(huì)對(duì)屬性進(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()返回對(duì)象實(shí)例的符號(hào)屬性數(shù)組
console.log(Object.getOwnPropertySymbols(o)); // [ Symbol(foo), Symbol(bar) ]
// Object.getOwnPropertyNames()返回對(duì)象實(shí)例的常規(guī)屬性數(shù)組
console.log(Object.getOwnPropertyNames(o)); // [ 'baz', 'qux' ]
// Object.getOwnPropertyDescriptors()會(huì)返回同時(shí)包含常規(guī)和符號(hào)屬性描述符的對(duì)象
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()會(huì)返回兩種類型的鍵
console.log(Reflect.ownKeys(o)); // [ 'baz', 'qux', Symbol(foo), Symbol(bar) ]
注意:Object.getOwnPropertyNames()和Object.getOwnProperty-Symbols()兩個(gè)方法的返回值彼此互斥。
因?yàn)榉?hào)屬性是對(duì)內(nèi)存中符號(hào)的一個(gè)引用,所以直接創(chuàng)建并用作屬性的符號(hào)不會(huì)丟失。
但是,如果沒有顯式地保存對(duì)這些屬性的引用,那么必須遍歷對(duì)象的所有符號(hào)屬性才能找到相應(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 | 符號(hào)指定了一個(gè)對(duì)象的默認(rèn)異步迭代器。如果一個(gè)對(duì)象設(shè)置了這個(gè)屬性,它就是異步可迭代對(duì)象,可用于for await…of循環(huán)。 |
| Symbol.prototype.description | description 是一個(gè)只讀屬性,它會(huì)返回 Symbol 對(duì)象的可選描述的字符串。 |
| Symbol.hasInstance | 用于判斷某對(duì)象是否為某構(gòu)造器的實(shí)例。因此你可以用它自定義 instanceof 操作符在某個(gè)類上的行為。 |
| Symbol.isConcatSpreadable | 用于配置某對(duì)象作為Array.prototype.concat()方法的參數(shù)時(shí)是否展開其數(shù)組元素。 |
| Symbol.iterator | 為每一個(gè)對(duì)象定義了默認(rèn)的迭代器。該迭代器可以被 for…of 循環(huán)使用。 |
| Symbol.match | 指定了匹配的是正則表達(dá)式而不是字符串。String.prototype.match() 方法會(huì)調(diào)用此函數(shù) |
| Symbol.matchAll | 內(nèi)置通用(well-known)符號(hào)指定方法返回一個(gè)迭代器,該迭代器根據(jù)字符串生成正則表達(dá)式的匹配項(xiàng)。此函數(shù)可以被 String.prototype.matchAll() 方法調(diào)用。 |
| Symbol.replace | 這個(gè)屬性指定了當(dāng)一個(gè)字符串替換所匹配字符串時(shí)所調(diào)用的方法。 |
| Symbol.search | 指定了一個(gè)搜索方法,這個(gè)方法接受用戶輸入的正則表達(dá)式,返回該正則表達(dá)式在字符串中匹配到的下標(biāo),這個(gè)方法由以下的方法來調(diào)用 String.prototype.search()。 |
| Symbol.species | 知名的 Symbol.species 是個(gè)函數(shù)值屬性,其被構(gòu)造函數(shù)用以創(chuàng)建派生對(duì)象。 |
| Symbol.split | 指向 一個(gè)正則表達(dá)式的索引處分割字符串的方法。這個(gè)方法通過 String.prototype.split() 調(diào)用。 |
| Symbol.toPrimitive | 是內(nèi)置的 symbol 屬性,其指定了一種接受首選類型并返回對(duì)象原始值的表示的方法。它被所有的強(qiáng)類型轉(zhuǎn)換制算法優(yōu)先調(diào)用。 |
| Symbol.toStringTag | 內(nèi)置通用(well-known)symbol 是一個(gè)字符串值屬性,用于創(chuàng)建對(duì)象的默認(rèn)字符串描述。它由 Object.prototype.toString() 方法內(nèi)部訪問。 |
| Symbol.unscopables | 指用于指定對(duì)象值,其對(duì)象自身和繼承的從關(guān)聯(lián)對(duì)象的 with 環(huán)境綁定中排除的屬性名稱。 |
附:Symbol的使用場(chǎng)景
1、作為對(duì)象屬性 當(dāng)一個(gè)復(fù)雜對(duì)象中含有多個(gè)屬性的時(shí)候,很容易將某個(gè)屬性名覆蓋掉,利用 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]())因?yàn)槭褂谜邿o法在外部創(chuàng)建出一個(gè)相同的 speak,所以就無法調(diào)用該方法
總結(jié)
到此這篇關(guān)于JS中Symbol類型的介紹與基本用法的文章就介紹到這了,更多相關(guān)JS Symbol類型用法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
擴(kuò)展js對(duì)象數(shù)組的OrderByAsc和OrderByDesc方法實(shí)現(xiàn)思路
js的擴(kuò)展方法是基于原型的,如Array.prototype.XXXX就是給Array擴(kuò)展XXX方法,然后數(shù)組都能使用這個(gè)方法了,在對(duì)象數(shù)組里面經(jīng)常有根據(jù)屬性來進(jìn)行排序的,升序,降序的,下面與大家分享自己寫的一個(gè)2013-05-05
JavaScript保留兩位小數(shù)的2個(gè)自定義函數(shù)
這篇文章主要介紹了JavaScript保留兩位小數(shù)的2個(gè)自定義函數(shù),需要的朋友可以參考下2014-05-05
基于JS實(shí)現(xiàn)導(dǎo)航條之調(diào)用網(wǎng)頁助手小精靈的方法
在網(wǎng)站中加入網(wǎng)頁助手小精靈,當(dāng)用戶訪問網(wǎng)站時(shí),向用戶問好,或是傳遞一些網(wǎng)站的重要信息,給用戶帶來極好的體驗(yàn)感,那么基于js代碼是如何調(diào)用網(wǎng)頁助手小精靈的呢?下面跟著腳本之家小編一起學(xué)習(xí)吧2016-06-06
javascript 另一種圖片滾動(dòng)切換效果思路
把圖片們用ul之類的包起來,并設(shè)置float。然后設(shè)置這個(gè)ul本身為absolute定位,其父標(biāo)簽用relative定位。通過設(shè)置ul的left或top值,實(shí)現(xiàn)圖片隊(duì)列的滾動(dòng)效果2012-04-04
javascript中導(dǎo)出與導(dǎo)入實(shí)現(xiàn)模塊化管理教程
這篇文章主要給大家介紹了關(guān)于javascript中導(dǎo)出與導(dǎo)入實(shí)現(xiàn)模塊化管理的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12
localStorage過期時(shí)間設(shè)置的幾種方法
聊到localStorage想必熟悉前端的朋友都不會(huì)陌生,在實(shí)際的應(yīng)用場(chǎng)景中,我們往往需要讓localStorage設(shè)置的某個(gè)key能在指定時(shí)間內(nèi)自動(dòng)失效,所以基于這種場(chǎng)景,我們?nèi)绾稳ソ鉀Q呢,本文就詳細(xì)的介紹一下2021-12-12

