Symbol(符号)是 ECMAScript 6 新增的数据类型。符号是原始值,且符号实例是唯一、不可变的。 符号的用途是确保对象属性使用唯一标识符,不会发生属性冲突的危险。
尽管听起来跟私有属性有点类似,但符号并不是为了提供私有属性的行为才增加的(尤其是因为 Object API 提供了方法,可以更方便地发现符号属性)。相反,符号就是用来创建唯一记号,进而用作非字符串形式的对象属性。
符号的基本用法
Symbol 值通过Symbol()
函数生成。
这就是说,对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是新增的 Symbol 类型。凡是属性名属于 Symbol 类型,就都是独一无二的,可以保证不会与其他属性名产生冲突。
因为符号本身是原始类型,所以 typeof 操作符对符号返回 symbol。
let sym = Symbol();
console.log(typeof sym); // symbol
符号没有字面量语法。
由于 Symbol 值不是对象,所以也不能添加属性。基本上,它是一种类似于字符串的数据类型。
Symbol()
函数不能与 new
关键字一起作为构造函数使用。
let mySymbol = new Symbol(); // TypeError: Symbol is not a constructor
调用 Symbol()函数时,也可以传入一个字符串参数作为对符号的描述(description),将来可以通过这个字符串来调试代码。但是,这个字符串参数与符号定义或标识完全无关:
let genericSymbol = Symbol();
let otherGenericSymbol = Symbol();
let fooSymbol = Symbol('foo');
let otherFooSymbol = Symbol('foo');
console.log(genericSymbol == otherGenericSymbol); // false
console.log(fooSymbol == otherFooSymbol); // false
使用 Symbol 作为属性
在对象的内部,使用 Symbol 值定义属性时,Symbol 值必须放在方括号之中。
let mySymbol = Symbol();
// 第一种写法
let a = {};
a[mySymbol] = 'Hello!';
// 第二种写法
let a = {
[mySymbol]: 'Hello!'
};
// 第三种写法
let a = {};
Object.defineProperty(a, mySymbol, { value: 'Hello!' });
// 以上写法都得到同样结果
a[mySymbol] // "Hello!"
通过方括号结构和Object.defineProperty()
方法,将对象的属性名指定为一个 Symbol 值。
注意,Symbol
值作为对象属性名时,不能用点运算符。(因为点运算符后面总是字符串,所以不会读取 mySymbol 作为标识名所指代的那个值,导致a
的属性名实际上是一个字符串,而不是一个 Symbol 值。)
返回对象实例的常规属性数组:Object.getOwnPropertyNames()
返回对象实例的符号属性数组:Object.getOwnProperty- Symbols()
返回同时包含常规和符号属性描述符的对象:Object.getOwnProperty- Descriptors()
返回两种类型的键:Reflect.ownKeys()
let s1 = Symbol('foo'),
s2 = Symbol('bar');
let o = {
[s1]: 'foo val',
[s2]: 'bar val',
baz: 'baz val',
qux: 'qux val'
};
console.log(Object.getOwnPropertySymbols(o));
// [Symbol(foo), Symbol(bar)]
console.log(Object.getOwnPropertyNames(o));
// ["baz", "qux"]
console.log(Object.getOwnPropertyDescriptors(o));
// {baz: {...}, qux: {...}, Symbol(foo): {...}, Symbol(bar): {...}}
console.log(Reflect.ownKeys(o));
// ["baz", "qux", Symbol(foo), Symbol(bar)]
Symbol 属性名的遍历
Symbol 值作为属性名,遍历对象的时候,该属性不会出现在for...in
、for...of
循环中,也不会被Object.keys()
、Object.getOwnPropertyNames()
、JSON.stringify()
返回。
Object.getOwnPropertySymbols()
方法,可以获取指定对象的所有 Symbol 属性名。该方法返回一个数组,成员是当前对象的所有用作属性名的 Symbol 值。
以 Symbol 值作为键名,不会被常规方法遍历得到。我们可以利用这个特性,为对象定义一些非私有的、但又希望只用于内部的方法。