内置Symbol属性
- Symbol是ES6引入的内置(built-in)全局函数,它自带一些内置好的属性。下面依次介绍一下。
Symbol.hasInstance
// 示例
class Array1 {
static [Symbol.hasInstance](instance) {
return Array.isArray(instance);
}
}
console.log([] instanceof Array1); // true
- 即使不是构造对象,普通对象也能定义它的Symbol.hasInstance。这样甚至可以用
instanceof
来进行值的校验,比如判断一个字符串是否是手机号码格式,就可以改写成这样:
const Ismoblie = {
[Symbol.hasInstance]: function(text) {
var pattern = /^1[3-9]\d{9}$/
return pattern.test(text)
}
}
'abc' instanceof Ismoblie // false
'18312345678' instanceof Ismoblie // true
Symbol.isConcatSpreadable
const alpha = ['a', 'b', 'c'];
const numeric = [1, 2, 3];
let alphaNumeric = alpha.concat(numeric);
console.log(alphaNumeric);
// expected output: Array ["a", "b", "c", 1, 2, 3]
numeric[Symbol.isConcatSpreadable] = false;
alphaNumeric = alpha.concat(numeric);
console.log(alphaNumeric);
// expected output: Array ["a", "b", "c", Array [1, 2, 3]]
- Symbol.isConcatSpreadable属性只能规定在调用concat函数时不能展开,使用
...
时仍然还是能够展开然后拼接的。
var arr1 = [1,2]
var arr2 = [3,4]
arr1[Symbol.isConcatSpreadable] = false
arr1[Symbol.isConcatSpreadable] = false
console.log([...arr1, ...arr2])
// expected output: Array [1,2,3,4]
Symbol.iterator
- Symbol.iterator用来声明一个对象的默认遍历器(iterator),主要结合
for...of
使用,for...of
会遍历那些可遍历(Iterable)的对象,执行对象的Symbol.iterator所对应的generator
函数。普通对象是没有遍历器接口的,为它指定了Symbol.iterator之后,就可以用for...of
遍历它了,注意Symbol.iterator对应的一定是一个generator函数。
const iterable1 = new Object();
iterable1[Symbol.iterator] = function* () {
yield 1;
yield 2;
yield 3;
};
console.log([...iterable1]);
// expected output: Array [1, 2, 3]
Symbol.match
- 定义了Symbol.match属性的对象,在执行
*.match(obj)
时,就会调用Symbol.match对应的函数,如果不是函数,则会报错。(这个Symbol感觉没什么用)
let str = new String('123')
str[Symbol.match] = function (arg) {
console.log(arg);
return false
}
'123'.match(str)
// console输出123,返回值为false
Symbol.replace
- 与Symbol.match类似,定义了Symbol.replace属性的对象,在执行
string.replace(obj)
时,就会调用Symbol.match对应的函数,如果不是函数,则会报错。(这个Symbol感觉没什么用)
class Replace1 {
constructor(value) {
this.value = value;
}
[Symbol.replace](string) {
return `s/${string}/${this.value}/g`;
}
}
console.log('foo'.replace(new Replace1('bar')));
// expected output: "s/foo/bar/g"
Symbol.search
- 同上,定义了Symbol.search属性的对象,再执行String.prototype.search方法时,会调用Symbol.search指向的函数。
class Search1 {
constructor(value) {
this.value = value;
}
[Symbol.search](string) {
return string.indexOf(this.value);
}
}
console.log('foobar'.search(new Search1('bar')));
// expected output: 3
Symbol.species
-
Symbol.species
指向一个构造函数。创建衍生对象时,会使用该函数返回的属性。正常情况下,例如一个类Array1继承自Array,那么Array1的实例对象产生的衍生对象,既是Array1的实例,又是Array的实例。如果像下面这样定义Symbol.species之后则会这样:
class Array1 extends Array {
static get [Symbol.species]() { return Array; }
}
const a = new Array1(1, 2, 3);
const mapped = a.map(x => x * x);
console.log(mapped instanceof Array1);
// expected output: false
console.log(mapped instanceof Array);
// expected output: true
- 所谓产生衍生对象,对于数组对象来说,包括了
filter
、map
、slice
这些函数生成的对象。而对于promise
对象,then
、catch
函数返回的都是一个新的promise
对象,这也是衍生对象。
class T1 extends Promise {
}
class T2 extends Promise {
static get [Symbol.species]() {
return Promise;
}
}
new T1(r => r()).then(v => v) instanceof T1 // true
new T2(r => r()).then(v => v) instanceof T2 // false
- 至此Symbol.species的主要作用就在于,子类的实例生成衍生对象时,可以通过修改Symbol.species,让衍生对象通过父类来创造,不通过子类。
Symbol.split
- 对象的Symbol.split属性,指向一个方法,当该对象被String.prototype.split方法调用时,会返回该方法的返回值。
class Split1 {
constructor(value) {
this.value = value;
}
[Symbol.split](string) {
var index = string.indexOf(this.value);
return this.value + string.substr(0, index) + "/"
+ string.substr(index + this.value.length);
}
}
console.log('foobar'.split(new Split1('foo')));
// expected output: "foo/bar"
Symbol.toPrimitive
- Symbol.toPrimitive用于声明一个值为函数的属性,当一个对象要转换为一个相应的原始(primitive)值时,会调用该函数,该函数接收一个字符串参数,根据场景有不同的值:
- Number:该场合需要转成数值
- String:该场合需要转成字符串
- Default:该场合可以转成数值,也可以转成字符串
let a = {
valueOf() {
return 0;
},
toString() {
return '1';
},
[Symbol.toPrimitive](hint) {
switch (hint) {
case 'number':
return 123;
case 'string':
return 'str';
case 'default':
return 'default';
default:
throw new Error();
}
}
}
2 * a // 246
3 + a // '3default'
a == 'default' // true
String(a) // 'str'
- 注意上面代码中,我同时还为a对象定义了
valueOf
和toString
函数,但实际发生toPrimitive类型转换时,实际执行的是Symbol.toPrimitive对应的函数,也就是说Symbol.toPrimitive的优先级更高。
Symbol.toStringTag
- 对象的Symbol.toStringTag属性,指向一个方法。在该对象上面调用Object.prototype.toString方法时,如果这个属性存在,它的返回值会出现在toString方法返回的字符串之中,表示对象的类型。也就是说,这个属性可以用来定制[object Object]或[object Array]中object后面的那个字符串。
class ValidatorClass {
get [Symbol.toStringTag]() {
return 'Validator';
}
}
console.log(Object.prototype.toString.call(new ValidatorClass()));
// expected output: "[object Validator]"
let c = {}
c[Symbol.toStringTag] = 'nike sb'
c.toString()
// expected output: "[object nike sb]"