深入理解ES6 笔记六:Symbol简介

196 阅读3分钟

ES6 引入了一种新的原始数据类型 Symbol,表示独一无二的值。

创建Symbol

通过全局的Symbol函数创建,Sybmol函数接受一个可选参数用于描述即将创建的Symbol,这段描述不可用于属性访问,目的是便于阅读代码和调式Symbol程序

let firstName = Symbol();
let lastName = Symbol('last Name');
let person = {};

person[firstName] = 'apple';
person[lastName] = 'banana';
console.log(person(firstName)); // apple
console.log(person(lastName)); // banana
console.log(firstName); // Stmbol()
console.log(lastName); // Symbol(last Name) 
console.log('last Name' in person); // false

Symbol的描述被存储在内部的[[Description]]属性中,只有当Symbol的toString()方法调用时才可以读取这个属性。在执行console.log()时隐式调用了Symbol的toString()方法。

注意事项

  1. 可以使用typeof来检测变量是否为Symbol类型
  2. Symbol 函数前不能使用 new 命令,否则会报错。这是因为生成的 Symbol 是一个原始类型的值,不是对象。
  3. instanceof 的结果为 false
  4. Symbol 函数的参数只是表示对当前 Symbol 值的描述,相同参数的 Symbol 函数的返回值是不相等的。
let symbol = Symbol('test Symbol');
console.log(typeof symbol); // Symbol
console.log(symbol instanceof Symbol); // false

let s1 = Symbol();
let s2 = Symbol();
console.log(s1 === s2); // false

let s1 = Symbol('foo');
let s2 = Symbol('foo');
console.log(s1 === s2); // false

使用方法

所有使用可计算属性名的地方,都可以使用Symbol。Symbol也可以用于可计算对象字面量属性名、Object.defimeProperty()方法和Object.definePropreties()方法的调用过程中。

let firstName = Symbol('first name');

// 使用一个可计算对象字面量属性
let person = {
    [firstName] = 'apple';
}
// 等同于 person[firstName] = 'apple';

// 将属性设置为只读
Object.defineProperty(person, firstName, {writable: false});

let lastName = Symbol('last Name');
Object.defineProperties(person, {
    [lastName]: {
        value: 'banana',
        writable: false
    }
});

console.log(person[firstName]); // apple
console.log(person[lastName]); // banana

Symbol共享体系

一般而言,在很大的代码库或跨文件追踪Symbol非常困难而且容易出错,ES6提供了一个可以随时访问的全局Symbol注册表。通过Symbol.for()方法。它只接受一个参数,也就是即将创建的Symbol的字符串标识符,这个参数也同样被用作Symbol的描述。

let uid = Symbol.for('uid');
let obj = {};
obj[uid] = '12345';
console.log(obj[uid]);  // 12345
console.log(uid);       // Symbol(uid)

Symbol.for()方法首先会在全局Symbol注册表中搜索键为‘uid’的Symbol是否存在,如果存在,直接返回已有的Symbal;否则,创建一个新的Symbol,并使用这个键在Symbol全局注册表中注册,随即返回新创建的Symbol。

let uid = Symbol.for('uid');
let obj = {
    [uid]: '12345';
};
console.log(obj[uid]);  // 12345
console.log(uid);       // Symbol(uid)

let uid2 = Symbol.for('uid');
console.log(uid === uid2);  // true
console.log(obj[uid2]);     // 12345
console.log(uid2);           // Symbol(uid)

可以使用Symbol.keyFor()方法在Symbol全局注册表中检索与Symbol有关的值

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

let uid2 = Symbol.for('uid');
console.log(Symbol.keyFor(uid2));   // uid

let uid3 = Symbol('uid');
console.log(Symbol.keyFor(uid3));   //  undefined

Symbol与类型强制转换

其他类型没有与Symbol逻辑等价的值,可以将Symbol显示转换为字符串和布尔值,不能将Symbol强制转换为字符串和数字类型。

//直接通过String()来调用Symbol.toString()获取Symbol内容 
let uid = Symbol.for('uid'),
    desc = String(uid);
console.log(desc);      // Symbol(uid)
console.log(uid.toString());      // Symbol(uid)

// 转为布尔值
Boolean(sym) // true
!sym  // false

// 不能强转字符串
let uid = Symbol.for('uid'),
    desc = uid + '';        // 报错!

// 不能强转数字类型
let uid = Symbol.for('uid'),
    desc = uid + 1;        // 报错!

Symbol属性检索

Object.keys()方法和Object.getOwnPropertyNames()方法可以检索对象中所有的属性名:前一个方法返回所有可枚举的属性名;后一个方法不考虑属性的可枚举性一律返回。但这两个方法都不支持Symbol属性(for...in、for...of也不支持),ES6引入Object.getOwnPropertySymbols()方法来检错对象中的Symbol属性。 Object.getOwnPropertySymbols()方法的返回值是一个包含所有Symbol自由属性的数组

let uid = Symbol.for('uid');
let obj = {
    [uid]: '12345';
};

let symbols = Object.getOwnPropertySymbols(object);

console.log(symbols.length);        // 1
console.log(symbols[0]);            // Symbol(uid)
console.log(obj[symbols[0]]);       // 12345