这是我参与「掘金日新计划 · 8 月更文挑战」的第6天,点击查看活动详情
前言
根据规范,只有两种原始类型可以用作对象属性键:
- 字符串类型
- symbol 类型
否则,如果使用另一种类型,例如数字,它会被自动转换为字符串。所以 obj[1] 与 obj["1"] 相同,而 obj[true] 与 obj["true"] 相同。
这就会导致属性名冲突,因此ES6引入新的原始数据类型Symbol
symbol简介
“symbol” 值表示唯一的标识符。
可以使用 Symbol() 来创建这种类型的值:
let id = Symbol();
- 也可以在symbol内部放入一个字符串,在代码调试时会方便很多(内部的字符串至少一个标签,因此即使内部字符串一样创建的symbol变量仍然不相同)
let id1 = Symbol("id");
let id2 = Symbol("id");
let s2 = Symbol('bar');
alert(id1 == id2); // false
id1.toString() //'Symbol('id')'
id2.toString() //'Symbol('id')'
s2.toString() // "Symbol(bar)"
alert(id1.description); // id
- 这里可以看见,里面的字符串是一个区分的作用,
toString()查看我们调用的是什么(这里是转化成了字符串)或者获取symbol.description属性,只显示描述(description):
注意事项
无法和其他类型处理
let sym = Symbol('My symbol');
"your symbol is " + sym
// TypeError: can't convert symbol to string
`your symbol is ${sym}`
// TypeError: can't convert symbol to string
- 无法直接转化成字符串和布尔值,不能转化成数字类型
- 转化成字符串
String(sym)或者sym.toString() - 转化成布尔值
Boolean(sym)或者!!sym
作为属性名称
let mySymbol = Symbol();
// 第一种写法
let a = {};
a[mySymbol] = 'Hello!';
// 第二种写法
let a = {
[mySymbol]: 'Hello!'
};
// 第三种写法
let a = {};
Object.defineProperty(a, mySymbol, { value: 'Hello!' });
// 以上写法都得到同样结果
a[mySymbol] // "Hello!"
这是Symbol作为属性的使用场景
- 下面这种事特殊的一种情况
a.mySymbol = 'Hello!';
a[mySymbol] // undefined
console.log(a['mySymbol']) // "Hello!"
- 这里我们就知道,实际上a. 并不是Symbol值而是一个字符串。因此,Symbol类型不能使用点运算符(点运算符后面总是字符串)。Symbol定义属性需放在
[]中,这杨才不会是字符串
隐藏属性
- symbol 允许我们创建对象的“隐藏”属性,代码的任何其他部分都不能意外访问或重写这些属性。
let id = Symbol("id");
let user = { name: "John", age: 30, [id]: 123 };
for (let key in user)
alert(key); // name, age(没有 symbol)
alert("Direct: " + user[id]); // 使用 symbol 直接访问 Direct: 123
Object.keys(user) 也会忽略它们。这是一般“隐藏符号属性”原则的一部分。如果另一个脚本或库遍历我们的对象,它不会意外地访问到符号属性。
相反,Object.assign 会同时复制字符串和 symbol 属性:
let id = Symbol("id");
let user = {
[id]: 123
};
let clone = Object.assign({}, user);
alert( clone[id] ); // 123
这里并不矛盾,就是这样设计的。这里的想法是当我们克隆或者合并一个 object 时,通常希望 所有 属性被复制(包括像 id 这样的 symbol)。
// 通过 name 获取 symbol
let sym = Symbol.for("name");
let sym2 = Symbol.for("id");
let globalSymbol = Symbol.for("name");
let localSymbol = Symbol("name");
alert( Symbol.keyFor(globalSymbol) ); // name,全局 symbol
alert( Symbol.keyFor(localSymbol) ); // undefined,非全局
// 通过 symbol 获取 name
alert( Symbol.keyFor(sym) ); // name
alert( Symbol.keyFor(sym2) ); // id
- 对于全局 symbol,
Symbol.for(key)按名字返回一个 symbol。相反,通过全局 symbol 返回一个名字,我们可以使用Symbol.keyFor(sym)