那些小而美的JS基础知识 - Symbol

640 阅读3分钟

最近在恶补JS基础,正好看到了Symbol这块,所以决定记录一下。

阅读了阮一峰老师的es6教程,现总结如下:

一、它是用来干什么的?

我们都知道对象的属性名都是字符串,这就很容易造成属性名字的冲突,导致原有的固有方法被重写(这也叫做mixin混入模式)。

这个时候就需要一种机制,来保证每个属性名字都是独一无二的。Symbol就是这种机制。

二、用法

let a = symbol(); 
let b = symbol("foo"); 
let c = symbol("bar");

我们可以看到,symbol函数里面,参数是可有可无的。参数的作用仅仅是对当前symbol值的描述。

三、特点

  • symbol可以转化为字符串、布尔值;转化为其他类型均报错。
  • symbol不能参与计算。
  • 因为symbol值是独一无二的,所以,即使描述符相同的2个symbol值是不相等的。
  • symbol值作为属性名字时,不能使用点运算符来得到该属性,而是使用方括号的形式来get or set。因为点运算符后面总是跟着字符串。
  • symbol类型的对象属性名字,不能被 Object.keys、Object.getOwnPropertyNames、for...in...、for...of... 遍历到。

代码实践

let b1 = 'b1';

let obj1 = {
    a: 11,
    b: 22,
    [Symbol('sty')]: 55,
    [b1]: 66
}

obj1.__proto__ = {
    c: 33
}

Object.keys(obj1);                  // 【“a”,“b”,“b1”】

Object.getOwnPropertyNames(obj1);   // 【“a”,“b”,“c1”】

Object.getOwnPropertySymbols(obj1); // 【Symbol(sty)】

四、内置的symbol类型的属性名

  • Symbol.hasInstance。
  • Symbol.isConcatSpreadable。
  • Symbol.iterator。
  • Symbol.toPrimitive。
  • Symbol.toStringTag。

4.1、Symbol.hasInstance

指向了 instanceOf 的秘密。当我们使用 instanceOf 来检测某某实例时,实际上就是调用的这个方法。

foo instanceOf Foo;   // 等价于下面的写法

Foo[Symbol.hasInstance](foo);

4.2、Symbol.isConcatSpreadable

表示该对象是否可被array.concat方法展开。当此属性值为true时,表示此对象可以被展开。

let arr1 = [1, 2];
arr1[Symbol.isConcatSpreadable] = false;
[].concat(arr1);   // 返回:【 【1, 2】 】

let arr2 = { length: 3, 0: 'a', 1: 'b', 2: 'c' };
arr2[Symbol.isConcatSpreadable] = true;
[].concat(arr2);   // 返回: [ 'a', 'b', 'c' ]

4.3、Symbol.iterator

当一个数据结构拥有了Symbol.iterator属性,那么它就可以与for ... of ... 一起使用。反之亦然。

4.4、Symbol.toPrimitive

这个方法是干啥的呢?

在这个内置的Symbol属性(Symbol.toPrimitive)的作用下,对象可以转换为一个原始值。

当发生强制类型转换的时候,会自动调用这个方法,这个方法接受一个字符串参数,并且值只能是“string”、“number”、“default”。

let obj = { 
    a: 22,
    [Symbol.toPrimitive](hint){
        switch(hint){
           case 'number':
                return 100;
           case 'string':
                return '200';
           case 'default':
                return 'default';
           default:
                throw new Error('something is wrong');
        }
    }
};

obj1 == 'default';      // true

(2 * obj1) === 200;     // true

4.5、Symbol.toStringTag

我们知道,JS中提供了多种数据类型检测的方法,其中Object.prototype.toString.call这个方法屡试不爽。

原因很简单,相对来说,它是万能油。

typeof 不能检测数组跟null类型;Array.isArray只能用来检测数组;instanceOf只能用来检测引用数据类型。

那么Object.prototype.toString.call方法是如何实现的呢?

答案就是这个内置的Symbol属性。

let obj = {
    [Symbol.toStringTag]: "这是一个陌生的属性"
}

Object.prototype.toString.call(obj);     // 返回“这是一个陌生的属性”