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()方法。
注意事项
- 可以使用typeof来检测变量是否为Symbol类型
- Symbol 函数前不能使用 new 命令,否则会报错。这是因为生成的 Symbol 是一个原始类型的值,不是对象。
- instanceof 的结果为 false
- 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