ES6 語法 - Symbol 類型

1、Symbol類型

ES5包含五種原始數據類型:字符串、數字、布爾值、nullundefinedES6引入了第6中原始類型Symbol

在之前的對象屬性名都是字符串,很容易造成屬性名衝突,比如使用他人提供的對象,想爲這個對象添加新的方法,新方法的名字很有可能與現有的方法產生衝突,Symbol就可以保證每個屬性的名字都是獨一無二的,這樣就從根本上防止了屬性衝突

1.1、基本使用

Symbol通過函數生成,Symbol不是構造函數,它是一個原始類型的值,不是對象,不能通過new關鍵字創建

let name = Symbol();

let person = {
    [name]: 'ithanmang'
}
console.log(name);//Symbol()
console.log(person);//{Symbol(): "ithanmang"}

Symbol可以接受一個可選參數,可以添加一段文本來描述即將創建的Symbol,這段描述不可用於屬性訪問,但是建議每次創建Symbol時都添加這樣一段描述,以便於閱讀代碼和調試

let name = Symbol('name property');

let person = {
    [name]: 'ithanmang'
}

console.log(name);//Symbol(name property)
console.log(person);//{Symbol(name property): "ithanmang"}

Symbol的描述符被存儲在內部的[[Description]]屬性中,通過調用SymboltoString方法可以讀取到這個屬性

let des = name.toString();
console.log(des);//Symbol(name property)

1.2、唯一性

每一個Sysbol值都是不相等的,和描述符無關,因此Symbol可以作爲標識符,用於對象的屬性名,就能保證不會出現同名的屬性,這對於一個對象由多個模塊構成的情況非常有用,能防止某一個鍵被不小心改寫或者覆蓋

let name1 = Symbol('name property');
let name2 = Symbol('name property');
console.log(name1);//Symbol(name property)
console.log(name2);//Symbol(name property)
console.log(Object.is(name1, name2));//false

上面兩個Symbol雖然打印出來沒有區別但是二者時不相等的

let name1 = Symbol('name1 property');

let person = {
    [name1]: 'ithanmang'
}

Object.defineProperty(person, name1, {
    writable: false
})

let name2 = Symbol('name2 property');

Object.defineProperties(person, {
    [name2]: {
        value: 'dxy',
        writable: false
    }
})

console.log(person[name1]);//ithanmang
console.log(person[name2]);//dxy

需要注意的是,Symbol值作爲對象屬性時,不能用點運算符

let sym1 = Symbol();
let person = {};

person.sym1 = 'test';

console.log(person[sym1]);//undefined
console.log(person['sym1']);//test

由上面代碼可以得知,person.sym1的屬性名是一個字符串'sym1'

1.3、共享體系

有時候希望在不同的代碼中共享一個Symbol,因此ES6提供了一個可以隨時訪問的全局Symbol註冊表

1. Symbol.for()

通過Symbol.for()可以創建一個共享的Symbol,它接受一個參數,也就是即將創建的Symbol的字符串標識符,這個參數同樣被用作Symbol的描述

let uuid = Symbol.for('uuid');
let person = {};

person[uuid] = '550e8400-e29b-41d4-a716-446655440000';

console.log(person[uuid]);//550e8400-e29b-41d4-a716-446655440000
console.log(uuid);//Symbol(uuid)

Symbol.for()方法首先在全局Symbol註冊表中搜索鍵位uuidSymbol是否存在,如果存在則直接返回已有的Symbol,否則,創建一個新的Symbol並使用這個鍵在Symbol全局註冊表中註冊,隨即返回新創建的Symbol

如果後續再傳入同樣的鍵調用Symbol.for()會返回相同的Symbol

let uuid = Symbol.for('uuid');
let person = {};

person[uuid] = '550e8400-e29b-41d4-a716-446655440000';

console.log(person[uuid]);//550e8400-e29b-41d4-a716-446655440000
console.log(uuid);//Symbol(uuid)

let uuid2 = Symbol.for('uuid');
person[uuid2] = '33a6c1ed-387a-46ee-903c-f88a52256893';

console.log(uuid === uuid2);//true
console.log(person);//{Symbol(uuid): "33a6c1ed-387a-46ee-903c-f88a52256893"}

上面代碼中第二次調用Symbol.for()創建的Symbol裏面的標識一樣,因此會直接返回之前創建的Symbol,所以uuid1 === uuid2true,並且給person賦值的時候會覆蓋之前的值

2. Symbol.keyFor()

Symbol.keyFor()可以檢索在全局註冊表中與Symbol有關的鍵,參數是一個Symbol類的值

let uuid = Symbol.for("uuid");
console.log(Symbol.keyFor(uuid));//uuid

let uuid2 = Symbol.for('uuid');
console.log(Symbol.keyFor(uuid2));//uuid

console.log(Symbol.keyFor(uuid3));//undefined

uuiduuid2都返回了uuid這個鍵,而在Symbol全局註冊表中,不存在uuid3這個Symbol,也就是不存在與之有關的鍵,因此返回undefined

需要注意的是Symbol.for()Symbol值等級的名字,是全局環境的,可以在不同的iframe中取到同一個值

let iframe  = document.createElement('iframe');

iframe.src = String(window.location);

document.body.appendChild(iframe);

console.log(iframe.contentWindow,Symbol.for('uuid') === Symbol.for('uuid'));//true

1.4、類型轉換

類型轉換是javascript得一個重要屬性,然而其他類型沒有與Symbol邏輯等價等價得值,因此Symbol使用起來不是很靈活

使用console.log()方法輸出得Symbol輸出的內容,它會調用SymbolString()方法並輸出有用的信息,也可以直接調用string()方法來獲得相同的內容

let uuid = Symbol.for('uuid');
let desc = String(uuid);
console.log(desc);//Symbol(uuid)

String()函數調用了uuid.toString()方法,返回Symbol描述的內容,但是嘗試將Symbol與一個字符串拼接,會導致程序拋出異常

let uuid = Symbol.for('uuid');
let desc = uuid + '';//TypeError: Cannot convert a Symbol value to a string

同樣Symbol也不能被強制轉換成數字類型,將Symbol與每一個數學運算符混合使用都會導致程序拋出異常

let uuid = Symbol.for('uuid');
let desc = uuid + 1;//TypeError: Cannot convert a Symbol value to a number

1.5、屬性檢索

Symbol作爲屬性,改屬性不會出現在for...in、for...of循環中,也不會被Object.getOwnPropertyNames()、Object.keys()、JSON、stringfy()返回,因此ES6添加了一個新的方法Object.getOwnPropertySymbols()來檢索對象中的Symbol屬性

Object.getOwnPropertySymbols()返回的是一個包含所有自由屬性的數組

let name = Symbol.for('name value');
let job = Symbol('job value');
let address = Symbol.for('address value');

let person = {
    id: 1001,
    [name]: 'ithanmang',
    [job]: 'coder',
}

Object.defineProperties(person, {
    [address]: {
        value: 'beijing',
        writable: false
    }
})

let symbols = Object.getOwnPropertySymbols(person);

//[Symbol(name value), Symbol(job value), Symbol(address value)]
console.log(symbols);

還可以通過一個新的API-Reflect.ownKeys(),可以獲取所有類型的鍵名

let keys = Reflect.ownKeys(person);

//["id", Symbol(name value), Symbol(job value), Symbol(address value)]
console.log(keys);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章