JS中Symbol類型的介紹與基本用法
1、 引入Symbol類型的背景
ES5 的對象屬性名都是字符串,這容易造成屬性名沖突的問題
舉例: 使用別人的模塊/對象, 又想為之添加新的屬性,這就容易使得新屬性名與原有屬性名沖突
2、Symbol類型簡介
symbol是一種原始數據類型
- 其余原始類型: 未定義(undefined) 、 空值(null)、布爾值(boolean)、字符串(string)、數值(number)、對象(object)
- symbol表示獨一無二的值
- symbol類型的"真實值"無法獲取,也就是說Symbol類型沒有對應的字面量
- symbol類型的意義在于區(qū)分彼此和不重復,不在于真實值
Symbol()是一種原生函數
- 常見的原生函數有String()、Number()、Boolean()、Array()、Object()、Function()、RegExp()、Date()、Error()、Symbol()
3、基本用法
符號需要使用Symbol()函數初始化。
let sym = Symbol(); // 因為符號本身是原始類型,所以typeof操作符對符號返回symbol console.log(typeof sym); // symbol
調用Symbol()函數時,也可以傳入一個字符串參數作為對符號的描述,將來可以通過這個字符串來調試代碼。但是,這個字符串參數與符號定義或標識完全無關:
// Symbol的值是唯一的,不會出現相同值的常量
let genericSymbol = Symbol();
let otherGenericSymbol = Symbol();
console.log(genericSymbol == otherGenericSymbol); // false
// 可以傳入一個字符串參數作為對符號的描述
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()函數不能與new關鍵字一起作為構造函數使用。
這樣做是為了避免創(chuàng)建符號包裝對象,像使用Boolean、String或Number那樣,它們都支持構造函數且可用于初始化包含原始值的包裝對象。
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()函數:
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í)行冪等操作。
第一次使用某個字符串調用時,它會檢查全局運行時注冊表,發(fā)現不存在對應的符號,于是就會生成一個新符號實例并添加到注冊表中。
后續(xù)使用相同字符串的調用同樣會檢查注冊表,發(fā)現存在與該字符串對應的符號,然后就會返回該符號實例。
// 創(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)建,因此作為參數傳給Symbol.for()的任何值都會被轉換為字符串。
注冊表中使用的鍵同時也會被用作符號描述。
let emptyGlobalSymbol = Symbol.for(); console.log(emptyGlobalSymbol); // Symbol(undefined)
使用Symbol.keyFor()來查詢全局注冊表,這個方法接收符號,返回該全局符號對應的字符串鍵。如果查詢的不是全局符號,則返回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、 使用符號作為屬性
凡是可以使用字符串或數值作為屬性的地方,都可以使用符號。
包括對象字面量屬性和 Object.defineProperty(obj, prop, descriptor) / Object.defineProperties() 定義的屬性。
對象字面量只能在計算屬性語法中使用符號作為屬性。
let s1 = Symbol('foo'),
s2 = Symbol('bar'),
s3 = Symbol('baz'),
s4 = Symbol('qux');
let o = {
// [屬性],會對屬性進行讀取,并且轉換成字符串。[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()返回對象實例的符號屬性數組
console.log(Object.getOwnPropertySymbols(o)); // [ Symbol(foo), Symbol(bar) ]
// Object.getOwnPropertyNames()返回對象實例的常規(guī)屬性數組
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()兩個方法的返回值彼此互斥。
因為符號屬性是對內存中符號的一個引用,所以直接創(chuà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 | 符號指定了一個對象的默認異步迭代器。如果一個對象設置了這個屬性,它就是異步可迭代對象,可用于for await…of循環(huán)。 |
| Symbol.prototype.description | description 是一個只讀屬性,它會返回 Symbol 對象的可選描述的字符串。 |
| Symbol.hasInstance | 用于判斷某對象是否為某構造器的實例。因此你可以用它自定義 instanceof 操作符在某個類上的行為。 |
| Symbol.isConcatSpreadable | 用于配置某對象作為Array.prototype.concat()方法的參數時是否展開其數組元素。 |
| Symbol.iterator | 為每一個對象定義了默認的迭代器。該迭代器可以被 for…of 循環(huán)使用。 |
| Symbol.match | 指定了匹配的是正則表達式而不是字符串。String.prototype.match() 方法會調用此函數 |
| Symbol.matchAll | 內置通用(well-known)符號指定方法返回一個迭代器,該迭代器根據字符串生成正則表達式的匹配項。此函數可以被 String.prototype.matchAll() 方法調用。 |
| Symbol.replace | 這個屬性指定了當一個字符串替換所匹配字符串時所調用的方法。 |
| Symbol.search | 指定了一個搜索方法,這個方法接受用戶輸入的正則表達式,返回該正則表達式在字符串中匹配到的下標,這個方法由以下的方法來調用 String.prototype.search()。 |
| Symbol.species | 知名的 Symbol.species 是個函數值屬性,其被構造函數用以創(chuàng)建派生對象。 |
| Symbol.split | 指向 一個正則表達式的索引處分割字符串的方法。這個方法通過 String.prototype.split() 調用。 |
| Symbol.toPrimitive | 是內置的 symbol 屬性,其指定了一種接受首選類型并返回對象原始值的表示的方法。它被所有的強類型轉換制算法優(yōu)先調用。 |
| Symbol.toStringTag | 內置通用(well-known)symbol 是一個字符串值屬性,用于創(chuàng)建對象的默認字符串描述。它由 Object.prototype.toString() 方法內部訪問。 |
| Symbol.unscopables | 指用于指定對象值,其對象自身和繼承的從關聯(lián)對象的 with 環(huán)境綁定中排除的屬性名稱。 |
附:Symbol的使用場景
1、作為對象屬性 當一個復雜對象中含有多個屬性的時候,很容易將某個屬性名覆蓋掉,利用 Symbol 值作為屬性名可以很好的避免這一現象
const name = Symbol('name');
const obj = {
[name]: 'ClickPaas',
}2、ES6 中的類是沒有 private 關鍵字來聲明類的私有方法和私有變量的,但是我們可以利用 Symbol 的唯一性來模擬
const speak = Symbol();
class Person {
[speak]() {
console.log(123)
}
}
let person = new Person()
console.log(person[speak]())因為使用者無法在外部創(chuàng)建出一個相同的 speak,所以就無法調用該方法
總結
到此這篇關于JS中Symbol類型的介紹與基本用法的文章就介紹到這了,更多相關JS Symbol類型用法內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
擴展js對象數組的OrderByAsc和OrderByDesc方法實現思路
js的擴展方法是基于原型的,如Array.prototype.XXXX就是給Array擴展XXX方法,然后數組都能使用這個方法了,在對象數組里面經常有根據屬性來進行排序的,升序,降序的,下面與大家分享自己寫的一個2013-05-05

