以下内容摘自 ECMAScript6 入门 Symbol 篇
Symbol 的概念
Symbol
是 ES6 提供的一个新的基本数据类型,表示独一无二的值。
在实际开发过程中,我们可能使用到别人提供的对象,但又想要为这个对象添加新的属性,那么这个新的属性就有可能与现有属性冲突。如果有一种机制,可以从根本根本上防止属性名冲突就好了,这也就是 Symbol
出现的原因。
Symbol 的使用
Symbol 值通过 Symbol
函数生成。因为 Symbol
是 ES6 提供的一种新的 数据类型,因此使用 Symbol
时,前面不能加 new
,它基本上类似于字符串的数据类型。
const s1 = Symbol('foo');
console.log(s1); // Symbol(foo)
Symbol
函数的参数只是表示当前Symbol
值的描述,因此相同的Symbol
函数的返回值不相等。
const s1 = Symbol('foo');
const s2 = Symbol('foo');
console.log(s1 === s2); // false
Symbol
值不能与其它类型的值进行运算,会报错。Symbol
可以显示的转换字符串类型、布尔类型,但不能转为数值类型。
let s1 = Symbol(0);
console.log(s1.toString()); // 'Symbol(My symbol)'
console.log(Boolean(s1)); // true
Symbol 作为属性名
由于每一个 Symbol
的值都是不相等的,因此 Symbol
可以作为标识符。
const mySymbol = Symbol();
// 第一种写法
const a = {};
a[mySymbol] = 'Hello!';
// 第二种写法
const a = {
[mySymbol]: 'Hello!'
};
// 第三种写法
const a = {};
Object.defineProperty(a,mySymbol,'Hello!');
// 以上的写法都得到同样的结果
console.log(a[mySymbol]); // "Hello!"
Symbol
作为对象的属性名时,不能使用点运算符。
在对象内部,使用 Symbol
定义属性时, Symbol
值必须放在方括号之中。
const mySymbol = Symbol();
const obj = {
[mySymbol] : function(arg){
console.log(arg);
}
};
obj[mySymbol]('Hello'); // Hello
Symbol
作为对象属性时,该属性为公开属性,不是私有属性。
遍历属性名
Symbol
作为属性名时,该属性不会出现在 for...in
、for...of
循环中,也不会在 Object.keys()
、Object.getOwnPropertyNames()
和 Json.stringify()
返回。但可以通过下面的 API 获取 指定对象的所有 Symbol
属性名。
Object.getOwnPropertySymbols
方法返回一个数组,成员为当前对象的所有用作属性名的Symbol
值
const obj = {
[Symbol('name')]: 'hvkcoder',
[Symbol('age')]: 18
};
const keys = Object.getOwnPropertySymbols(obj);
console.log(keys); // [ Symbol(name), Symbol(age) ]
Reflect.ownKeys
方法可以返回所有类型的键名,包括常规键名和Symbol
键名
const obj = {
[Symbol('name')]: 'hvkcoder',
[Symbol('age')]: 18,
job: 'coder'
};
const keys = Reflect.ownKeys(obj);
console.log(keys); // [ 'job', Symbol(name), Symbol(age) ]
Symbol.for()、Symbol.keyFor()
- Symbol.for
有时我们可能需要重新使用同一个 Symbol
的值,Symbol.for
接受一个字符串作为参数,然后通过该参数去查找是否存在以该参数作为名称的 Symbol
值,如果存在则就返回这个 Symbol
值,否则就新建并返回一个以该字符串为名称的 Symbol
值。
const s1 = Symbol.for('foo');
const s2 = Symbol.for('foo');
console.log(s1 === s2); // true
Symbol.for()
会被登记在全局环境中供搜索,因此可以在 iframe
或 server worker
中获取到同一个值。
- Symbol.keyFor
Symbol.keyFor()
会返回已登记的 Symbol
类型值的key。
const s1 = Symbol('foo');
const key = Symbol.keyFor(s1);
console.log(key); // foo
内置的 Symbol 值
- Symbol.hasInstance
对象的 Symbol.hasInstance
属性,指向一个内部方法。当其他对象使用 instanceof
运算符,判断是否为该对象的实例时,会调用这个方法。
class MyClass {
[Symbol.hasInstance](foo) {
return foo instanceof Array;
}
}
console.log([1, 2, 3] instanceof new MyClass()); // true
- Symbol.isConcatSpreadable
对象的 Symbol.isConcatSpreadable
属性等于一个布尔值,表示该对象用于 Array.prototype.concat()
时,是否可以展开。
const obj = {length: 2, 0: 'c', 1: 'd'};
['a', 'b'].concat(obj, 'e') // ['a', 'b', obj, 'e']
obj[Symbol.isConcatSpreadable] = true;
['a', 'b'].concat(obj, 'e') // ['a', 'b', 'c', 'd', 'e']
- Symbol.species
对象的 Symbol.species
指向一个构造函数。创建衍生对象时,会使用该属性。
class Test extends Array {
constructor(args) {
super(args);
};
static get [Symbol.species]() { return Array; }
}
const t1 = new Test();
const t2 = t1.map(x => x);
console.log(t1 instanceof Test); // true
console.log(t2 instanceof Test); // false
Symbol.species
的作用在于,实例对象在运行过程中,需要再次调用自身的构造函数时,会调用该属性指定的构造函数。
- Symbol.match
对象的 Symbol.match
属性,指向一个函数。当执行 str.match(myObject)
时,如果该属性存在,会调用它,返回该方法的返回值。
class MyMatcher {
[Symbol.match](string) {
return 'hello world'.indexOf(string);
}
}
console.log('e'.indexOf(new MyMatcher())); // -1
- Symbol.replace
对象的 Symbol.replace
属性,指向一个方法,当该对象呗 String.prototype.replace
方法调用时,会返回该方法的返回值。
const x = {};
x[Symbol.replace] = (...arg) => console.log(arg);
'Hello'.replace(x,'World'); // ["Hello","World"]
Symbol.replace
方法会收到两个参数,第一个参数是 replace
方法正在作用的对象,第二个参数是替换后的值。
- Symbol.search
对象的 Symbol.search
属性,指向一个方法,当该对象呗 String.prototype.search
方法调用时,会返回该方法的返回值。
class MySearch{
constructor(value){
this.value = value;
}
[Symbol.search](string){
return string.indexOf(this.value);
}
}
'foobar'.search(new MySearch('foo')); // 0
- Symbol.split
对象的 Symbol.split
属性,指向一个方法,当该对象呗 String.prototype.split
方法调用时,会返回该方法的返回值。
class MySplitter{
constructor(value){
this.value = value;
}
[Symbol.split](string){
const index = string.indexOf(this.value);
if(index === -1){
return string;
}
return [
string.substr(0, index),
string.substr(index + this.value.length)
];
}
}
'foobar'.split(new MySplitter('foo')); // ['', 'bar']
- Symbol.iterator
对象的 Symbol.iterator
属性,指向该对象的默认遍历器方法
class Collection {
*[Symbol.iterator]() {
let i = 0;
while (this[i] !== undefined) {
yield this[i];
++i;
}
}
};
const myCollection = new Collection();
myCollection[0] = 1;
myCollection[1] = 2;
for(let value of myCollection){
console.log(value); // 1 2
}
- Symbol.toPrimitive
对象的 Symbol.toPrimitive
属性,指向一个方法。该对象被转为原始属性时,会调用这个方法,返回该对象对应的原始属性。
Symbol.toPrimitive
被调用时,会接受一个字符串参数,表示当前运算的模式,一共有三种模式:
1. Number:该场合需要转成数值
2. String:该场合需要转成字符串
3. Default:该场合可以转成数值,也可以转成字符串
- Symbol.toStringTag
对象的 Symbol.toStringTag
属性,指向一个方法。在该对象上面调用 Object.prototype.toString
方法时,如果这个属性存在,它的返回值会出现在 toString
方法返回的字符串之中,表示对象的类型。
- Symbol.unscopables
对象的 Symbol.unscopables
属性,指向一个对象。该对象指定了使用 with
关键字时,哪些属性会被 with
环境排除。