1. Symbol.hasInstance
Symbol.hasInstance
用于判断某对象是否为某构造器的实例。因此你可以用它自定义 instanceof 操作符在某个类上的行为。
class Array1 {
static [Symbol.hasInstance](instance) {
return Array.isArray(instance);
}
}
console.log([] instanceof Array1);
Symbol.hasInstance 属性的属性特性: | |
---|---|
writable | false |
enumerable | false |
configurable | false |
你可实现一个自定义的instanceof
行为,例如:
class MyArray {
static [Symbol.hasInstance](instance) {
return Array.isArray(instance);
}
}
console.log([] instanceof MyArray); // true
2. Symbol.isConcatSpreadable
内置的Symbol.isConcatSpreadable
符号用于配置某对象作为
Array.prototype.concat()方法的参数时是否展开其数组元素。
var alpha = ['a', 'b', 'c'],
numeric = [1, 2, 3];
var alphaNumeric = alpha.concat(numeric);
console.log(alphaNumeric); // 结果: ['a', 'b', 'c', 1, 2, 3]
设置Symbol.isConcatSpreadable
为false
:
var alpha = ['a', 'b', 'c'],
numeric = [1, 2, 3];
numeric[Symbol.isConcatSpreadable] = false;
var alphaNumeric = alpha.concat(numeric);
console.log(alphaNumeric); // 结果: ['a', 'b', 'c', [1, 2, 3] ]
3. Symbol.species
知名的 Symbol.species
是个函数值属性,其被构造函数用以创建派生对象。
class MyArray extends Array {
// 覆盖 species 到父级的 Array 构造函数上
static get [Symbol.species]() { return Array; }
}
var a = new MyArray(1,2,3);
var mapped = a.map(x => x * x);
console.log(mapped instanceof MyArray); // false
console.log(mapped instanceof Array); // true
4. Symbol.match/replace/search/split
这四个方法允许我们以对象的方式自定义String的match、replace、search、split方法。以match为例,我们通常这样调用它:
var s = "hello";
s.match(RegExp); //匹配一个正则表达式
假如我们需要为当前的字符串s定制一个自己的match方法,但是又不希望修改String原型上的match方法(因为这样会影响到其他的字符串调用match方法)。Symbol.match就为我们提供了这种能力。
对于上面的例子,如果传入的对象具有[Symbol.match]方法,那么js引擎就会修改match方法默认的行为,去调用定义的[Symbol.match]方法。如:
var a = {
[Symbol.match](){
return true;
}
}
"hello".match(a); //true
当调用字符串的match方法并传入具有[Symbol.match]属性的对象时,js引擎就会调用对象的这个方法。
上面的写法等同于下面的写法:
a[Symbol.match]("hello"); //true
replace、search和split也是相同的原理。下面分别给一个简单的例子:
replace:
const x = {};
x[Symbol.replace] = (...s) => console.log(s);
'Hello'.replace(x, 'World') // ["Hello", "World"]
由于replace的第一个参数有[Symbol.replace]方法,因此js引擎会调用这个方法,并把调用者‘Hello’和第二个参数‘World’作为参数传递给该方法。这样,上面的写法就等同于:
x[Symbol.replace]("Hello", "world");
1
search:
var a = {
[Symbol.match](){
return true;
}
}
"hello".search(a); //true
原理同match。
split:
var a = {
sep: ",",
[Symbol.match](t){
return t.split(this.sep);
}
}
"hello,world".split(a); //["hello", "world"]
原理也与match相同。
5. Symbol.iterator
定义一个对象的遍历器方法。凡是具有[Symbol.iterator]方法的对象都是可遍历的,可以使用for … of循环依次输出对象的每个属性。数组和类数组,以及ES6新增的Map、Set等都原生部署了该方法,因此它们都可遍历。如:
for(var item of [1,2,3]){
console.log(item); //依次输出1,2,3
}
任何一个数组都具备这个原生的遍历器方法:
> [][Symbol.iterator]
< ƒ values() { [native code] } //C++实现
普通对象默认不具有该遍历器方法,因此无法用for … of循环遍历出对象所有的属性值。如果你希望让普通对象可遍历,可以手动为该对象定义遍历器方法,如:
var a = {
name: "夕山雨",
age: 24,
[Symbol.iterator]: function* (){
yield this.name;
yield this.age;
}
}
这里为了简单,使用了ES6的Generator函数,它定义该遍历器先输出name属性,再输出age属性。因此当你用for … of来输出a的属性值时,就可以得到结果:
for(var item of a){
console.log(item); //依次输出:"夕山雨" 24
}
6. Symbol.toPrimitive
该方法定义了一个对象如何被转化为一个基本数据类型。通常对象是不能直接与基本数据类型的变量进行运算的,但是如果你为它定义了[Symbol.toPrimitive]方法,它就可以按照你所指定的规则转化为基本数据类型。它接收一个字符串,表示需要转换成的数据类型:
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'
这里表示,如果对象需要转化为数字,就返回123;如果需要转化为字符串,就转化为’str’;如果没有指定要转化的类型,那就返回字符串’Default’。
由于乘法运算*只能对数值操作,因此js引擎会调用[Symbol.toPrimitive]并传入"number",将obj转化为数字。而加法既可以对数值生效,也可以对字符串生效,因此js引擎传入了"default"。该方法默认只接受number、string和default这三个值。
7. Symbol.toStringTag
可以自定义对象的toString()方法。通常对象的toString方法会返回一个类似[object Object]的字符串,表示该对象的类型,如:
var a = {};
a.toString(); //"[object Object]"
但是如果你修改了对象的Symbol.toStringTag方法,返回值就会发生变化:
a[Symbol.toStringTag] = function(){
return "xxx";
}
a.toString(); //"[object xxx]"
可以看到,我们定义的返回值覆盖了之前的字符串中的后半部分“Object”,因此该方法可以用于定制对象的toString()的返回值。
8. Symbol.unscopables
该方法用于with语句。它指定在使用with语句时,哪些属性不属于with环境。举个例子:
var author = {
name: "夕山雨",
age: 24,
stature: "179",
weight: 65
}
var name = "张三";
var age = "28";
with(author){
console.log(name); //“夕山雨”
console.log(age); //24
console.log(stature); //"179"
console.log(weight); //65
}
默认情况下,对于with语句内引用的变量,js引擎会优先去with的作用对象上查找对应的属性,如果找不到,才认为是外部变量。但是你可以人为指定哪些属性不应该去作用对象上查找,如:
var author = {
name: "夕山雨",
age: 24,
stature: "179",
weight: 65,
get [Symbol.unscopables](){
return { name: true, age: true }
}
}
var name = "张三";
var age = "28";
var stature = "153";
var weight = 80;
with(author){
console.log(name); //“张三”
console.log(age); //28
console.log(stature); //"179"
console.log(weight); //65
}
可以看到,由于我们认为指定了name和age两个属性不作用域with环境,因此这里的name和age输出的是外部的变量,而stature和weight输出的仍然是author的属性值。
总结
Symbol作为一种新的数据类型,有着与String相似的特性,与String不同的是它是独一无二的,因此适合作为对象属性的键值,防止该属性被覆盖。除了自定义的Symbol值外,灵活掌握内置的Symbol,对ES6的学习有带来极大帮助,特别是Symbol.iterator,它是ES6中的一个非常重要的概念,之后会继续探讨。
原文链接:https://blog.youkuaiyun.com/qq_41694291/article/details/103322409