概述
ES6 引入新的原始数据类型 Symbol ,表示独一无二的值(所有 Symbol 类型的值都不相等)。Symbol 不是对象,所以不能使用 new 命令,也不能添加属性。
如果 Symbol 的参数是一个对象,先调用该对象的 toString 方法,将其转为字符串,然后才生成一个 Symbol 值。
Symbol 值不能和其他类型的值进行运算,否则会报错。
Symbol 值可以显示转为字符串。
Symbol 值可以转为布尔值,但是不能转为数值。
let sym = Symbol('hahaha');
"my Symbol is " + sym // TypeError:can't convert symbol to string
String(sym); // 'Symbol(hahaha)'
sym.toString(); // 'Symbol(hahaha)'
Boolean(sym); // true
!sym ; // false
Number(sym); // TypeError
sym + 2; // TypeError
作为属性名的 Symbol
Symbol 值作为对象属性名时不能使用点运算符。因为点运算符后面总是字符串,所以不会读取 Symbol 值作为标识名所指代的值。
let mySymbol = Symbol();
let anSymbol = Symbol();
let a = {};
a.mySymbol = 'hh';
a[anSymbol] = 'aa';
a.anSymbol // undefined
a[anSymbol] 'aa'
a[mySymbol] // undefined
a['mySymbol'] // 'hh'
同理,使用 Symbol 值定义属性时,也必须放在方括号中。
let s = Symbol();
let obj = {
[s](arg) {}
}
属性名的遍历
遍历 Symbol 值作为属性名的属性,只能通过 Object.getOwnPropertySymbols() 方法和 Reflect.ownKeys() 方法
Symbol.for()、Symbol.keyFor()
Symbol.for() 接收一个字符串作为参数,然后全局搜索有没有该参数作为名称的 Symbol 值。有的话就返回这个 Symbol 值,没有就新建并返回一个以该字符串为名称的 Symbol 值。这样就可以借助该方法重新使用同一个 Symbol 值。
Symbol.for() 和 Symbol() 的区别——Symbol.for() 有登记机制,Symbol() 没有
Symbol.keyFor() 方法返回一个已登记的 Symbol 类型值的 key。
Symbol.for('bar') === Symbol.for('bar'); // true
Symbol('bar') === Symbol('bar'); // false
let s1 = Symbol.for('foo');
let s2 = Symbol('foo');
Symbol.keyFor(s1); // 'foo'
Symbol.keyFor(s2); // undefined
内置的 Symbol 值
Symbol.hasInstance
用于判断某对象是否为某构造器的实例。使用 instanceof 操作符时实际上是调用该方法
class Even {
static [Symbol.hasInstance] (obj) {
return Number(obj) % 2 === 0;
}
}
1 instanceof Even; // false
2 instanceof Even; // true
123423 instanceof Even; // false
Symbol.isConcatSpreadable
该属性等于一个布尔值,表示对象使用 Array.prototype.concat() 时是否可以展开。true 表示可以展开,数组和类数组对象的默认值都是 undefined,数组默认可以展开,类数组对象(这里的类数组对象特指那些属性名为:0、1、2、3...这样的对象,属性名为字符串的类数组对象展开的话值都是 undefined)需要把该属性设置为 true 才可以展开。
let arr = ['c', 'd'];
arr[Symbol.isConcatSpreadable] // undefined
['a', 'b'].concat(arr,'e'); // ['a', 'b', 'c', 'd', 'e']
arr[Symbol.isConcatSpreadable] = false;
['a', 'b'].concat(arr,'e'); // ['a', 'b', ['c', 'd'], 'e']
let obj = { length: 2, 0: 'c', 1: 'd' };
obj[Symbol.isConcatSpreadable] // undefined
['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 属性要采用 get 读取器。
class MyArray extends Array {
static get [Symbol.species] () {
return Array;
}
}
let a = new MyArray(1, 2, 3);
let mapped = a.map(x => x * x);
mapped instanceof MyArray // false
mapped instanceof Array // true
Symbol.match
该属性指向一个函数,当执行 str.match(myObject) 时,如果该属性存在,会调用它返回该方法的返回值。
String.prototype.match(regexp);
// 等同于
regexp[Symbol.match] (this)
class MyMatcher {
[Symbol.match] (string) {
return 'hello world'.indexOf(string);
}
}
'e'.match(new MyMatcher()); // 1
Symbol.replace
该属性指向一个方法,当对象被 String.prototype.replace 方法调用时会返回该方法的返回值。
const x = {};
x[Symbol.replace] = (...s) => console.log(s);
'Hello'.replace(x, 'World'); // ['Hello', 'World']
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
该属性指向一个方法,当对象被 String.prototype.split 方法调用时会返回该方法的返回值。
class MySplitter {
constructor(value) {
this.value = value;
}
[Symbol.split](string) {
var 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']
'foobar'.split(new MySplitter('bar')); // ['foo', '']
'foobar'.split(new MySplitter('baz')) // 'foobar'
Symbol.iterator
该属性指向该对象的默认遍历器方法。
var myIterable = {};
myIterable[Symbol.iterator] = function* () {
yield 1;
yield 2;
yield 3;
};
[...myIterable] // [1, 2, 3]
Symbol.toPrimitive
该属性指向一个方法,对象被转换为原始类型的值时,会调用这个方法,返回该对象的原始类型值。
Symbol.toPrimitives 被调用时会接收一个字符串参数,表示当期运算的模式。一共有 3 种模式:
- Number:需要转换为数值
- String:需要转换为字符串
- Default:可以转成数值,也可以转成字符串
let obj = {
[Symbol.toPrimitive](hint) {
switch (hint) {
case 'number':
return 123;
case 'string':
return 'str';
case 'default':
return 'default';
default:
throw new Error();
}
}
}
2 * obj // 246
3 + obj // '3default'
obj == 'default' // true
String(obj) // 'str'
Symbol.toStringTag
该属性指向一个方法,在对象调用 Object.prototype.toString 方法时,如果该方法存在,其返回值会出现在 toString 方法返回的字符串中,表示对象的类型。即,这个属性可以用于定制 [object Object] 或 [object Array] 中 object 后面的字符串。
({[Symbol.toStringTag]: 'Foo'}.toString()) // '[object Foo]'
class Collection {
get [Symbol.toStringTag]() {
return 'xxx';
}
}
var x = new Collection();
Object.prototype.toString.call(x); // '[object xxx]'
Symbol.unscopables
该属性指向一个对象,指定使用 with 关键字时哪些属性会被 with 环境排除。
// 没有 unscopables 时
class MyClass {
foo() { return 1; }
}
var foo = function () { return 2; };
with (MyClass.prototype) {
foo(); // 1
}
//有 uncsopables 时
class MyClass {
foo() { return 1; }
get [Symbol.unscopables]() {
return { foo: true };
}
}
var foo = function () { return 2; };
with (MyClass.prototype) {
foo(); // 2
}