Symbol
ES6 引入了一种新的原始数据类型 Symbol,表示独一无二的值。
Symbol 值通过 Symbol 函数生成,也就是说,对象的属性名现在可以有两种类型:一种是字符串,另一种就是 Symbol 类型。只要属性名属于 Symbol 类型,就是独一无二的,可以保证不会与其他属性名冲突。
1 | let s = Symbol() |
Symbol 函数可以接受一个字符串作为参数,表示对 Symbol 实例的描述,主要是为了在 console 显示。
1 | let s = Symbol("str") |
如果 Symbol 的参数是一个对象,就会调用该对象的 toString 方法,再生成 Symbol 值
1 | const obj = { |
Symbol 函数只表示对当前 Symbol 值的描述,因此相同的 Symbol 函数的返回值是不相等的
1 | let s1 = Symbol() |
作为属性名的 Symbol
Symbol 值可以作为标识符用于对象的属性名,保证不会出现同名的属性,还能防止某一个键被不小心覆盖。
1 | let symbol = Symbol() |
实例:消除代码中的字符串
1 | function getArea(shape, options) { |
属性名的遍历
Symbol 属性不会出现在 for…in、for…of 循环中,也不会被 Object.keys()、Object.getOwnPropertyName()返回。有一个 Object.getOwnPropertySymbols 方法可以获取指定对象的所有 Symbol 属性。
1 | let obj = {} |
Symbol.for()、Symbol.keyFor()
Symbol.for 接受一个字符串作为参数,然后搜索有没有以该参数作为名称的 Symbol 值,如果有,就返回这个 Symbol 值,否则就新建一个以该字符串为名称的 Symbol 值。
1 | let str1 = Symbol.for("test") |
Symbol.keyFor 方法返回一个已登记的 Symbol 类型值的 key:
1 | let s1 = Symbol.for("foo") |
Symbol.for 为 Symbol 登记的名字是全局环境的,可以在不同的 iframe 或 serviceWorker 中取到同一个值
内置的 Symbol 值
Symbol.hasInstance
foo instanceof Foo 实际在内部调用的是 Foo[Symbol.hasInstance](foo):1
2
3
4
5
6
7
8
9
10
11
12
13
14class MyClass {
[Symbol.hasInstance](foo) {
return foo instanceof Array
}
}
;[1, 2, 3] instanceof new MyClass() //true
class Even {
static [Symbol.hasInstance](obj) {
return Number(obj) % 2 === 0
}
}
1 instanceof Even //false
2 instanceof Even //trueSymbol.isConcatSpreadable
等于一个布尔值,表示该对象使用 Array.prototype.concat()时是否可以展开:1
2
3
4
5
6
7let arr1 = ["a", "b"]
;[1, 2].concat(arr1, "c") //[1, 2, 'a', 'b', 'c']
arr1[Symbol.isConcatSpreadable] //undefined
let arr2 = ["a", "b"]
arr2[Symbol.isConcatSpreadable] = false
;[1, 2].concat(arr2, "c") //[1, 2, ['a', 'b'], 'c']
类似数组的对象也可以展开,默认值为 false,必须手动打开:
1 | let obj = { length: 2, 0: "c", 1: "d" } |
对一个类而言,Symbol.isConcatSprealable 属性必须写成实例属性:
1 | class myClass extends Array { |
Symbol.species
对象的 Symbol.species 属性指向当前对象的构造函数,创造实例时会默认调用这个方法。1
2
3
4
5
6
7
8
9
10
11
12
13
14class myClass extends Array {
static get [Symbol.species]() {
return Array
}
}
let a = new myClass(1, 2, 3)
let mapped = a.map((x) => x * x)
a instanceof Array //true
mapped instanceof Array //true
a instanceof myClass //true
mapped instanceof myClass //false
//默认值等同于下面的写法
// static get [Symbol.species]() { return this; }Symbol.match
对象的 Symbol.match 属性指向一个函数,当执行 str.match(myObject)时,如果该属性存在,会调用它的返回值:1
2
3
4
5
6
7
8
9
10String.prototype.match(regexp)
//等同于
regexp[Symbol.match](this)
class myMatcher {
[Symbol.match](string) {
return "hello world".indexOf(string)
}
}
e.match(new myMatcher()) //1Symbol.replace
对象的 Symbol.replace 属性指向一个方法,当对象被 String.prototype.replace 方法调用时会返回该方法的返回值:1
2
3String.prototype.replace(searchValue, replaceValue)
//等同于
searchValue[Symbol.replace](this, replaceValue)Symbol.replace 方法会收到两个参数,一个是 replace 方法正在作用的对象,第二个是替换后的值:
1
2
3const x = {}
x[Symbol.replace] = (...s) => console.log(s)
"hello".replace(x, "world") //['hello', 'world']Symbol.search
对象的 Symbol.search 属性指向一个方法,当对象被 String.prototype.search 方法调用时会返回该方法的返回值:1
2
3
4
5
6
7
8
9
10
11
12
13String.prototype.search(regexp)
//等同于
regexp[Symbol.search](this)
class mySearch {
constructor(value) {
this.value = value
}
[Symbol.search](string) {
return string.indexOf(this.value)
}
}
"foobar".search(new mySearch("foo")) //3Symbol.split
对象的 Symbol.split 属性指向一个方法,当对象被 String.prototype.split 方法调用时会返回该方法的返回值:1
2
3String.prototype.split(separator, limit)
//等同于
separator[Symbol.split](this, limit)重定义 split 方法的行为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16class mySplit {
constructor(value) {
this.value = value;
}
[Symbol.split](string) {
let index = string.indexOf(this.value);
if (index === -1) return string;
return [
string.substr(0, index),
string.substr(index + this.value.length);
];
}
}
'foobar'.split(new mySplit('foo')); //['', 'bar']
'foobar'.split(new mySplit('bar')); //['foo', '']
'foobar'.split(new mySplit('1')); //'foobar'Symbol.iterator
对象的 Symbol.iterator 属性指向该对象的默认遍历器方法:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24let myIterable = {}
myIterable[Symbol.iterator] = function* () {
yield 1
yield 2
yield 3
}
;[...myIterable] //[1, 2, 3]
class Collection {
*[Symbol.iterator]() {
let i = 0
while (this[i] !== undefined) {
yield this[i]
i++
}
}
}
let myCollection = new Collection()
myCollection[0] = 1
myCollection[1] = 2
for (let value of myCollection) {
console.log(value) //1, 2
}Symbol.toPrimitive
Symbol.toStringTag
Symbol.unscopables