symbol
- 新的原始数据类型,表示独一无二的值,可以理解为类似字符串的数据类型
- 不可使用 new,可接收一个字符串作为参数,该字符串作为实例的描述
description 属性
ES2019 提供了一个实例属性 description,直接返回 Symbol 的描述
let s = Symbol('sss');
s.toString(); // 'Symbol(sss)'
s.description; // sss
如果参数是一个对象,则会调用该对象的 toString 方法,将其转为字符串
let obj = {
toString() {
return 'abc';
},
};
let s2 = Symbol(obj);
s2; // 'Symbol(abc)'
不能与其他类型的值进行运算
let s3 = Symbol('s3');
'aaa' + s3; // 错误
可显式转为字符串
String(s3);
s3.toString();
可转为布尔值,但是不能转为数值
let s4 = Symbol('s4');
Boolean(s4); // 正确
Number(s4); // 错误
作为对象的属性名时,需加中括号,取值时不可通过点运算符来访问,当通过 Object.defineProperty 设置时,无需加中括号
let obj2 = {
[s]: 'hello',
s: 'world',
};
obj2[s];
obj2.s;
当 Symbol 作为属性名时,该属性不会出现在 for...in、for...of 循环中,也不会被 Object.keys()、Object.getOwnPropertyNames()、JSON.stringify() 返回
Object.getOwnPropertySymbols() 可获取指定对象的所有 Symbol 属性名,返回该键的 Symbol 值
Object.getOwnPropertySymbols(obj2); // ['Symbol(sss)']
Reflect.ownKeys() 可获取所有类型的键
Symbol.for()
重复使用同一个 Symbol 值
接受一个字符串作为参数,然后搜索是否有该参数作为名称的 Symbol 值,有就返回该值,没有就新建一个,并注册到全局。Symbol() 不会被登记到全局环境,因此就算使用同一个字符串作参数,返回的实例也不相等
let s5 = Symbol.for('s5');
let s6 = Symbol.for('s5');
s5 === s6; // true
Symbol.keyFor()
返回一个已登记的 Symbol 类型值的 key,若未登记则返回 undefined
let s7 = Symbol.for('s7');
let s8 = Symbol('s8');
Symbol.keyFor(s7); // s7
Symbol.keyFor(s8);
内置的 Symbol 值
Symbol.hasInstance
该属性指向一个内部方法,当其他对象使用 instanceof 运算符,判断是否为该对象的实例时,就会调用该方法。如:foo instanceof Foo,在语言内部,实际调用的时 Foo[Symbol.hanInstance](foo)
Symbol.isConcatSpreadable
该属性等于一个布尔值,表示该对象用于 Array.prorotype.concat() 时,是否可以展开,默认值为 undefined,当该值为默认值或者 true 时,表示可展开
let arr1 = ['c', 'd'];
['a', 'b'].concat(arr1, 'e'); // ['a', 'b', 'c', 'd', 'e']
arr1[Symbol.isConcatSpreadable]; // undefined
let arr2 = ['c', 'd'];
arr2[Symbol.isConcatSpreadable] = false;
['a', 'b'].concat(arr2, 'e'); // ['a', 'b', Array(2), 'e']
Symbol.species
该属性指向一个构造函数,创建衍生对象时,会使用该属性
class MyArray extends Array {}
let a = new MyArray(1, 2, 3);
let b = a.map(x => x);
let c = a.filter(x => x > 1);
b instanceof MyArray; // true
c instanceof MyArray; // true
上面示例中,b、c 虽然调用数组方法生成,但是它们同时也是 MyArray 的实例
Symbol.species 就是用来解决该问题的,定义 Symbol.species 时需要采用 get 取值器
以下示例更改了 MyArray2 的 Symbol.species,使其返回 Object
class MyArray2 extends Array {
static get [Symbol.species]() {
return Object;
}
}
let a2 = new MyArray2();
let b2 = a.map(x => x);
b instanceof MyArray2; // false
b instanceof Object; // true
Symbol.replace
该属性指向一个方法,当对象被 String.prototype.replace 方法调用时,返回该方法的返回值
如:String.prototype.replace(searchValue, replaceValue),等同于:searchValue[Symbol.replace](this, replaceValue)
接受两个参数,第一个是 replace 方法正在作用的对象(hello),第二个是替换后的值(world)
let x = {};
x[Symbol.replace] = (s, b) => console.log(s, b);
'hello'.replace(x, 'world');
Symbol.search
该属性指向一个方法,String.prototype.search
如:String.prototype.search(regexp),等同于:regexp[Symbol.search](this)
Symbol.split
该属性指向一个方法,String.prototype.split
如:String.prototype.split(separator, limit),等同于:separator[Symbol.split](this, limit)
Symbol.iterator
该属性指向该对象的默认遍历器方法
对象进行 for...of 循环时,会调用 Symbol.iterator 方法,返回该对象的默认遍历器
let myIterator = {};
myIterator[Symbol.iterator] = function () {
return {
next() {
if (this._first) {
this._first = false;
return { value: 'ok', done: false };
} else {
return { done: true };
}
},
_first: true,
};
};
console.log([...myIterator]);
Symbol.toPrimitive
该属性指向一个方法,当对象被转为原始类型值时,会调用该方法
返回该对象对应的原始类型值
接受一个字符串参数,表示当前运算的模式:
Number:需转成数值String:需转成字符串Defalut:可以转成数值,也可转成字符串
let obj3 = {
[Symbol.toPrimitive](hint) {
switch (hint) {
case 'number':
return 123;
case 'string':
return 'str';
case 'default':
return 'default';
default:
throw new Error();
}
},
};
console.log(2 * obj3);
console.log(3 + obj3);
console.log(obj3 == 'default');
console.log(String(obj3));
Symbol.toStringTag
该属性指向一个方法
在该对象上调用 Object.prototype.toString 方法时,如果这个属性存在,它的返回值会出现在 toString 方法返回的字符串之中,表示对象的类型
也就是说,该属性可以用来定制 [object Object] 或 [object Array] 中 object 后面那个字符串
{ [Symbol.toStringTag]: 'Foo' }.toString() // [object Foo]
class Collection {
get [Symbol.toStringTag]() {
return 'xxx';
}
}
let cc = new Collection();
Object.prototype.toString.call(cc) // [object xxx]
ES6 新增内置对象的 Symbol.toStringTag 属性值如下:
JSON[Symbol.toStringTag]:'JSON'Math[Symbol.toStringTag]:'Math'Module对象M[Symbol.toStringTag]:'Module'ArrayBuffer.prototype[Symbol.toStringTag]:'ArrayBuffer'DataView.prototype[Symbol.toStringTag]:'DataView'Map.prototype[Symbol.toStringTag]:'Map'Promise.prototype[Symbol.toStringTag]:'Promise'Set.prototype[Symbol.toStringTag]:'Set'%TypedArray%.prototype[Symbol.toStringTag]:'Uint8Array'等WeakMap.prototype[Symbol.toStringTag]:'WeakMap'WeakSet.prototype[Symbol.toStringTag]:'WeakSet'%MapIteratorPrototype%[Symbol.toStringTag]:'Map Iterator'%SetIteratorPrototype%[Symbol.toStringTag]:'Set Iterator'%StringIteratorPrototype%[Symbol.toStringTag]:'String Iterator'Symbol.prototype[Symbol.toStringTag]:'Symbol'Generator.prototype[Symbol.toStringTag]:'Generator'GeneratorFunction.prototype[Symbol.toStringTag]:'GeneratorFunction'
Symbol.unscopables
该属性指向一个对象,当对象使用了 with 关键字时,哪些属性会被 with 环境排除
此处输出的结果表明数组的这些属性会被 with 命令排除
Array.prototype[Symbol.unscopables];
console.log(Object.keys(Array.prototype[Symbol.unscopables]));
没有 unscopables
class MyClass {
foo() {
return 1;
}
}
var foo = function () {
return 2;
};
with (MyClass.prototype) {
console.log(foo());
}
有 unscopables
class MyClass2 {
foo2() {
return 1;
}
get [Symbol.unscopables]() {
return {
foo2: true,
};
}
}
var foo2 = function () {
return 2;
};
with (MyClass2.prototype) {
console.log(foo2());
}
上面代码通过指定 Symbol.unscopables 属性,使得 with 语法块不会在当前作用域寻找 foo 属性,即 foo 将指向外层作用域的变量