ES6之Symbol内置属性详解

1. Symbol.hasInstance

Symbol.hasInstance用于判断某对象是否为某构造器的实例。因此你可以用它自定义 instanceof 操作符在某个类上的行为。

class Array1 {
  static [Symbol.hasInstance](instance) {
    return Array.isArray(instance);
  }
}

console.log([] instanceof Array1);
Symbol.hasInstance 属性的属性特性:
writablefalse
enumerablefalse
configurablefalse

你可实现一个自定义的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.isConcatSpreadablefalse

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.csdn.net/qq_41694291/article/details/103322409

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值