ES6 Symbol探究
Symbol 是什么
Symbol
===> 象征;符号;标志
在 ES6 中是一种新的数据类型,它的目的是为了给数据创建一个唯一的标识,让数据不和其他数据冲突。
Symbol 怎么用
- Symbol()
参数:
description [number | string]
,用来对symbol的描述,可用于调试但不能访问symbol本身。
返回值:一个Symbol
类型的值,返回的Symbol
类型的值有一个自带的description
属性,可以获取到对于这个Symbol
的描述。但是不能给 Symbol 类型的值添加自定义属性。
<script>
let symbol_test = Symbol();
let symbol_1 = Symbol(1);
let symbol_str1 = Symbol("1");
let symbol_1_bak = Symbol(1);
console.log(symbol_test); //===> Symbol()
console.log(symbol_1); //===> Symbol(1)
console.log(symbol_str1); //===> Symbol(1)
console.log(symbol_1_bak); //===> Symbol(1)
console.log(symbol_1 == symbol_str1); //===> false
console.log(symbol_1 == symbol_1_bak); //===> false
console.log(symbol_test.description); //===> undefined
console.log(symbol_1.description, typeof symbol_1.description); //===> 1 string
console.log(symbol_str1.description, typeof symbol_str1.description); //===> 1 string
console.log(symbol_1_bak.description); //===> 1
</script>
- Symbol.for(key)
参数:
key[string]
,作为 symbol 注册表中与某 symbol 关联的键(同时也会作为该 symbol 的描述)。
返回值:返回由给定的key
找到的symbol
,否则就是返回新创建的symbol
。
和Symbol()
的区别:Symbol.for(key)
方法会根据给定的键key
,来从运行时的 symbol 注册表中找到对应的symbol
,如果找到了,则返回它,否则,新建一个与该键关联的symbol
,并放入全局symbol
注册表中。
<script>
let symbol_undefined = Symbol.for();
let symbol_undefined_bak = Symbol.for();
let symbol_1 = Symbol.for(1);
let symbol_str1 = Symbol.for("1");
let symbol_1_bak = Symbol.for(1);
console.log(symbol_undefined); //===> Symbol(undefined)
console.log(symbol_1); //===>Symbol(1)
console.log(symbol_str1); //===>Symbol(1)
console.log(symbol_1_bak); //===>Symbol(1)
console.log(symbol_undefined == symbol_undefined_bak); //===> true
console.log(symbol_1 == symbol_str1); //===> true
console.log(symbol_1 == symbol_1_bak); //===> true
console.log(symbol_undefined.description); //===> undefined
console.log(symbol_1.description, typeof symbol_1.description); //===> 1 string
console.log(symbol_str1.description, typeof symbol_str1.description); //===> 1 string
console.log(symbol_1_bak.description); //===> 1
</script>
- Symbol.keyFor(sym)
用途:
Symbol.keyFor(sym)
方法用来获取symbol
注册表中与某个symbol
关联的键。
参数:必选参数,存储在symbol
注册表中的某个symbol
返回值:如果全局注册表中查找到该symbol
,则返回该symbol
的key
值,形式为string
。如果symbol
未在注册表中,返回undefined
<script>
let symbol_undefined = Symbol.for();
let symbol_1 = Symbol.for(1);
let symbol_str1 = Symbol.for("1");
console.log(Symbol.keyFor(symbol_undefined)); //===> undefined
console.log(Symbol.keyFor(symbol_1)); //===> 1
console.log(Symbol.keyFor(symbol_str1)); //===> 1
</script>
内置的Symbol值
除了定义自己使用的 Symbol 值以外,ES6 还提供了一些内置的 Symbol 值,指向语言内部使用的方法。
- Symbol.hasInstance
对象的
Symbol.hasInstance
属性,指向一个内部方法。当其他对象使用instanceof
运算符,判断是否为该对象的实例时,会调用这个方法。比如,foo instanceof Foo在语言内部,实际调用的是Foo[Symbol.hasInstance](foo)
。
<script>
function MyFunc(num) {
this.num = num
}
MyFunc.prototype[Symbol.hasInstance] = (num) => {
// instanceof new MyFunc() 也会调用这一行
console.log("MyFunc.prototype[Symbol.hasInstance]---num",num);
return Number(num) % 2 === 0
}
console.log(2 instanceof new MyFunc()); // true
console.log(3 instanceof new MyFunc()); // false
console.log(MyFunc.prototype[Symbol.hasInstance](2)); // true
console.log(MyFunc.prototype[Symbol.hasInstance](3)); // false
</script>
<script>
class MyClass {
[Symbol.hasInstance](num) {
// instanceof new MyClass() 也是会调用这一行
console.log("MyClass.prototype[Symbol.hasInstance]---num",num);
return Number(num) % 2 === 0
}
}
console.log(2 instanceof new MyClass()); //true
console.log(3 instanceof new MyClass()); // false
console.log(MyFunc.prototype[Symbol.hasInstance](2)); // true
console.log(MyFunc.prototype[Symbol.hasInstance](3)); // false
</script>
- Symbol.isConcatSpreadable
对象的
Symbol.isConcatSpreadable
属性等于一个布尔值,表示该对象用于Array.prototype.concat()
时,是否可以展开。
<script>
let arr1 = [3, 4, 5];
console.log([1, 2].concat(arr1, 6)); // [1, 2, 3, 4, 5, 6]
console.log(arr1[Symbol.isConcatSpreadable]); // undefined
</script>
<script>
//数组的默认行为是可以展开,Symbol.isConcatSpreadable默认等于undefined。该属性等于true时,也有展开的效果。
let arr2 = [3, 4, 5];
arr2[Symbol.isConcatSpreadable] = false
console.log([1, 2].concat(arr2, 6)); //[1, 2, [3, 4, 5], 6]
arr2[Symbol.isConcatSpreadable] = true
console.log([1, 2].concat(arr2, 6)); //[1, 2, 3, 4, 5, 6]
</script>
- Symbol.species
对象的
Symbol.species
属性,指向一个构造函数。创建衍生对象时,会使用该属性。例如
<script>
/**
* 子类MyArray继承了父类Array,a是MyArray的实例,b和c是a的衍生对象。
* 你可能会认为,b和c都是调用数组方法生成的,所以应该是数组(Array的实例),
* 但实际上它们也是MyArray的实例。
**/
class MyArray extends Array {
}
const a = new MyArray(1, 2, 3);
const b = a.map(x => x);
console.log(b instanceof MyArray); // true
</script>
<script>
/**
* 由于定义了Symbol.species属性,创建衍生对象时就会使用这个属性返回的函数,作为构造函数。
* 这个例子也说明,定义Symbol.species属性要采用get取值器。
* a.map(x => x)生成的衍生对象,就不是MyArray的实例,而直接就是Array的实例
**/
class MyArray extends Array {
static get [Symbol.species]() { return Array; }
}
const a = new MyArray();
const b = a.map(x => x);
console.log(a instanceof Array); //true
console.log(a instanceof MyArray); //true
b instanceof MyArray // false
b instanceof Array // true
</script>
- Symbol.match
对象的
Symbol.match
属性,指向一个函数。当执行str.match(myObject)
时,如果该属性存在,会调用它,返回该方法的返回值。
<script>
class MyMatch {
[Symbol.match](str) {
return str.length > 2
}
}
console.log("a".match(new MyMatch)); // true
console.log("hello".match(new MyMatch)); //false
</script>
类似的还有:
- Symbol.replace
对象的Symbol.replace属性,指向一个方法,当该对象被String.prototype.replace方法调用时,会返回该方法的返回值。
String.prototype.replace(searchValue, replaceValue)
// 等同于
searchValue[Symbol.replace](this, replaceValue)
const x = {};
x[Symbol.replace] = (...s) => console.log(s);
'Hello'.replace(x, 'World') // ["Hello", "World"]
- Symbol.search
对象的Symbol.search属性,指向一个方法,当该对象被String.prototype.search方法调用时,会返回该方法的返回值。
- Symbol.split
对象的Symbol.split属性,指向一个方法,当该对象被String.prototype.split方法调用时,会返回该方法的返回值。
- Symbol.iterator
对象的Symbol.iterator属性,指向该对象的默认遍历器方法。
const myIterable = {};
myIterable[Symbol.iterator] = function* () {
yield 1;
yield 2;
yield 3;
};
[...myIterable] // [1, 2, 3]
- Symbol.toPrimitive
对象的Symbol.toPrimitive属性,指向一个方法。该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值。Symbol.toPrimitive被调用时,会接受一个字符串参数,表示当前运算的模式,一共有三种模式。
Number:该场合需要转成数值
String:该场合需要转成字符串
Default:该场合可以转成数值,也可以转成字符串
<script>
let obj = {
[Symbol.toPrimitive](hint) {
switch (hint) {
case 'number':
return 0;
case 'string':
return 'str';
case 'default':
return 'default';
default:
throw new Error();
}
}
};
console.log(2 * obj); // 0
console.log(3 + obj); // '3default'
console.log(obj == 'default'); // true
console.log(String(obj)); // 'str'
</script>
- Symbol.toStringTag
对象的
Symbol.toStringTag
属性,指向一个方法。在该对象上面调用Object.prototype.toString
方法时,如果这个属性存在,它的返回值会出现在toString
方法返回的字符串之中,表示对象的类型。也就是说,这个属性可以用来定制[object Object]
或[object Array]
中object
后面的那个字符串。例如:
Object.prototype.toString.call([])
->"[object Array]"
<script>
class MyClass {
get [Symbol.toStringTag]() {
return 'MyClass';
}
}
console.log(Object.prototype.toString.call(new MyClass())); //"[object MyClass]"
</script>