Symbol
Symbol
是es6
引入的一個新的原始資料型別,是一個獨一無二
的值。
目前為止,js
的資料型別有以下幾種:
資料型別 | 說明 |
---|---|
undefined | undefined |
null | null |
boolean | 布林值 |
string | 字串 |
number | 數字 |
Bigint | 大整數 |
Object | 物件 |
Symbol | Symbol |
Symbol
透過Symbol()
函式生成。物件的屬性名現在除了可以使用字串以外,還可以使用新增的Symbol
型別。如果屬性名使用Symbol
,那麼它就是獨一無二的,不與其它屬性名產生衝突。
let s = Symbol()
console.log(typeof s); // symbol
注意:
Symbol()
函式前不能使用new
,否則報錯。因為生成的Symbol
是一個原始型別的值,而不是物件,所以不能使用new
來呼叫。而且,Symbol
值不是物件,不能給Symbol
新增屬性。可以這麼理解,Symbol
是一種類似於字串的資料型別。
Symbol
接收字串作為引數,表示對Symbol
的描述,新增描述可以用來區分多個Symbol
。
let s2 = Symbol('desc')
let s3 = Symbol('desc2')
console.log(s2); // Symbol(desc)
console.log(s3); // Symbol(desc2)
如果Symbol
的引數傳入的是物件,需要把物件轉為字串再生成Symbol
,否則會顯示[object Object]
。
let obj = {
name : '東方不敗'
}
let s4 = Symbol(JSON.stringify(obj))
console.log(s4); // Symbol({"name":"東方不敗"})
let s5 = Symbol(obj)
console.log(s5);// Symbol([object Object])
Symbol
傳入的引數只是一個描述,實際上Symbol
和Symbol
並不相等。
let sy = Symbol()
let sy2 = Symbol()
console.log(sy === s2); // false
let sy3 = Symbol('a')
let sy4 = Symbol('a')
console.log(sy3 === sy4); // false
每呼叫一次Symbol()
都會生成一個獨一無二的值,每個Symbol
都不相等。
Symbol
值不能參與其他型別值的運算,否則報錯。
let a = Symbol('hello')
console.log(a + 'world'); // 報錯 Cannot convert a Symbol value to a string
Symbol轉換
Symbol可以轉換為字串
let a2 = Symbol('hello')
console.log(String(a2)); // Symbol(hello)
如果需要返回Symbol
的描述需要使用es2019
提供的Symbol
例項屬性description
返回描述。
let a2 = Symbol('hello')
console.log(a2.description); // hello
Symbol可以轉換為布林值(boolean)
let a2 = Symbol('hello')
console.log(Boolean(a2)); // true
console.log(Boolean(!a2)); // false
Symbol屬性名
Symbol
作為屬性名
let n = Symbol()
// 方式一
let obj2 = {
[n] : '東方不敗'
}
console.log(obj2); // {Symbol(): '東方不敗'}
console.log(obj2[n]); // 東方不敗
// 方式二
obj2[n] = '東方求敗'
console.log(obj2[n]); // 東方求敗
// 方式三
let obj3 = {}
let back = Object.defineProperty(obj3,n,{value : '藝術概論'})
console.log(obj3[n]); // 藝術概論
Object.defineProperty
使用說明
第一個引數:要在其上定義屬性的物件
第二個引數:要定義或修改的屬性的名稱
第三個引數:將被定義或修改的屬性描述符
Symbol
值作為物件屬性名時,不能用點運算子
獲得Symbol
屬性,使用點運算子相當於是給物件新增了一個字串屬性名
,而不是獲取Symbol
。
let n2 = Symbol()
let obj4 = {}
console.log(obj4.n2 = '中國工藝美術史'); // 中國工藝美術史
console.log(obj4[n2]); // undefined
console.log(obj4); // {n2: '中國工藝美術史'}
屬性名遍歷
Symbol
是不可列舉的,Symbol
作為物件鍵名時,是不可被遍歷的,for...in
、Object.keys
等方法都得不到Symbol
鍵名,並且JSON.stringify()
也不會返回Symbol
。
let m = Symbol('a')
let f = {
[m]:'東方不敗',
name:'西方求敗',
name2: '光合作用'
}
// 西方求敗 、 光合作用
for(k in f){
console.log(f[k]);
}
console.log(Object.keys(f)); // ['name','name2']
console.log(JSON.stringify(f)); // {"name":"西方求敗","name2":"光合作用"}
Reflect.ownKeys()
可以返回常規鍵名和Symbol
鍵名
console.log(Reflect.ownKeys(f)); // ['name', 'name2', Symbol(a)]
Object.getOwnPropertySymbols()
只返回Symbol
屬性
console.log(Object.getOwnPropertySymbols(f)); // [Symbol(a)]
Symbol.for()、Symbol.keyFor()
Symbol.for()
Symbol
有一個特性就是Symbol
不等於Sombol
,但有時候我們需要同一個Symbol
值
let r = Symbol.for('a')
let r2 = Symbol.for('a')
console.log(r === r2); // true
Symbol.for()
和Symbol()
都會生成新的Symbol
,前者會被登記在全域性環境提供搜尋,後者不會。
Symbol.for()
每次呼叫都會先檢查引數key
是否存在,如果不存在才會新建一個值。
Symbol()
每次呼叫都會新建一個值。
Symbol.keyFor()
Symbol.keyFor()
返回已經登記的Symbol
值的key
let r3 = Symbol.for('b')
let r4 = Symbol('c')
console.log(Symbol.keyFor(r3)); // b
console.log(Symbol.keyFor(r4)); // undefined
Symbol內建值
Symbol.hasInstance
Symbol.hasInstance
用來判斷某個物件是否為某個構造器例項
class myClass {
static [Symbol.hasInstance](val){
return typeof val === 'number'
}
// static [Symbol.hasInstance](val){
// return typeof val === 'boolean'
// }
}
console.log(100 instanceof myClass); // true
console.log('100' instanceof myClass); // false
多個Symbol.hasInstance
會覆蓋,只保留最下面的那一個。
Symbol.isConcatSpreadable
Symbol.isConcatSpreadable
用於表示Array.prototype.concat()
是否可以展開,true、undefined
可以展開,false
不可展開。
let arr1 = [1,2]
let arr2 = [3,4]
console.log(arr1[Symbol.isConcatSpreadable]); // undefined
console.log(arr1.concat(arr2)); // [1,2,3,4]
console.log(arr1[Symbol.isConcatSpreadable] = false)
console.log(arr1.concat(arr2)); // [[1,2],3,4]
Symbol.species
物件的Symbol.species
屬性指向一個建構函式,建立衍生物件時會使用該屬性
// 這裡繼承了Array的原型
class MyArray extends Array { }
let a = new MyArray(1,2,3)
let b = a.map(el => el + 1)
console.log(b); // constructor : class MyArray
b
和c
呼叫的是陣列方法,那麼應該是Array
的例項,但實際上它們也是MyArray
的例項
class MyArray extends Array {
static get [Symbol.species]() { return Array }
}
let a = new MyArray(1,2,3)
let b = a.map(el => el + 1)
let c = a.filter(el => el == 2)
console.log(a,b,c); // 1,2,3 2,3,4 2
console.log(b instanceof MyArray); // false
console.log(b); // constructor : class MyArray
Symbol.species
可以在建立衍生物件時使用這個屬性返回的函式作為建構函式。
這裡return
了Array
,所以建立的衍生物件使用的Array
作為建構函式,而不是MyArray
。
如果這裡return
一個String
,那麼上面的map、filter
會報錯,因為衍生物件使用的是String
作為建構函式,String
是沒有陣列方法的。
Symbol.match
Symbol.match
指向一個函式,如果函式存在則會被呼叫,並返回該方法的返回值
class MyMatch {
[Symbol.match](val){
return 'hello world'.indexOf(val)
}
}
// match字串方法,可以在字串內檢索指定的值並返回
console.log('e'.match(new MyMatch())); // 1
案例原始碼:https://gitee.com/wang_fan_w/es6-science-institute
如果覺得這篇文章對你有幫助,歡迎點亮一下star喲