关于Symbol

178 阅读2分钟
  1. 不可使用 new ,避免创建符号包装对象

    查看使用 new 与不使用 new 创建的字符串的区别

    const string = "string";
    const stringObj = new String('string');
    
    console.log(typeof string); //string
    console.log(typeof stringObj); //object
    
    string.a = 123;
    console.log(string.a);//undefined
    
    stringObj.a = 123;
    console.log(stringObj.a);//123
    
  2. Symbol.for

    使用像 Symbol("create") 这样永远创建的是唯一标识。使用 Symbol.for 在全局注册表中搜索,如果不存在才会创建新的实例,否则返回存在的实例。

    console.log(Symbol('create') === Symbol('create')); //false
    console.log(Symbol.for('create') === Symbol.for('create')); //true
    

    也就是说,使用 Symbol.for 方法创建的实例,都会放在一个全局的表中去记录他的键值对。同时生成值的方法对于固定的key生成的value一定是一样的。而直接通过 Symbol('create') 创建的实例,生成的value不会记录在全局的表中,相当于一个私有变量。

  3. Symbol.keyFor

    全局表中寻找对应key 的实例。使用 Symbol('create') 声明的实例使用该方法找不到。

    let created = Symbol('create');
    
    console.log(Symbol.keyFor(created)); //undefined
    
    let createdFor = Symbol.for('create');
    
    console.log(Symbol.keyFor(createdFor)); //create
    
  4. 使用 getOwnPropertySymbols 查找对应实例

    Object.getOwnPropertyName功能类似,返回数组中以 Symbol 作为 key 的数组。 Object.getOwnPropertyNameObject.getOwnPropertySymbols 返回的数组互斥。

无标题-2021-10-13-0831.png

  1. Symbol.asyncIterator

  2. Symbol.hasInstance

    Symbol.hasInstance 可以判断某个实例的是否是某个函数的实例。该方法可以重写。

    class Father {}
    class Child extends Father {}
    let c = new Child();
    console.log(Child[Symbol.hasInstance](c)); //true
    console.log(Father[Symbol.hasInstance](c)) //true
    
    class Father {}
    class Child extends Father {
            static [Symbol.hasInstance] () {
                    return false;
            }
    }
    let c = new Child();
    console.log(Child[Symbol.hasInstance](c)); //false
    console.log(Father[Symbol.hasInstance](c)) //true
    
    
  3. Symbol.species

    作为派生对象的构造函数。比如说对于 Array 来说, map 方法会构造一个新的 Array 返回。设置该方法就相当于设置了新的 Array 的构造函数。如下:

    function Test() {
        console.log("我是构造函数");
    }
    class Baz extends Array {
        static get[Symbol.species]() {
            return Test;
        }
    }
    
    let baz = new Baz(5, 1, 2, 5, 78, 87, 8);
    
    let derivation = baz.map(v => v);
    
    console.log(baz); //Baz [ 5, 1, 2, 5, 78, 87, 8 ]
    console.log(baz instanceof Array); //true
    console.log(derivation instanceof Array); //false
    console.log(derivation instanceof Test); //true
    
    
  4. Symbol.toPrimitive

    对一个 class 重新设置该方法,可以改变该 class new 出来的对象在隐式或则显式转化为 NumberString的值。

    参考ECMAScript® 2022中的

    7.1.1.1 OrdinaryToPrimitive ( objhint )

    The abstract operation OrdinaryToPrimitive takes arguments obj (an Object) and hint (string or number). It performs the following steps when called:

    1.  If hint is string, then

      Let methodNames be « "toString", "valueOf".

    2.  Else,

      Let methodNames be « "valueOf", "toString".

    3.  For each element name of methodNames, do

      a. Let method be ? Get(objname).

      b. If IsCallable(method) is true,

      then

      i. Let result be ? Call(methodobj).

      ii. If Type(result) is not Object, return result. 4. Throw a TypeError exception.

    class Test {
        constructor() {
            this[Symbol.toPrimitive] = function(hint) {
                console.log(hint);
                switch (hint) {
                    case 'number':
                        return -1;
                    case 'string':
                        return 'haha';
                    case 'default':
                    default:
                        return 'o';
                }
            }
        }
    }
    
    let test = new Test();
    
    Number(test) //-1
    String(test) //haha
    
  5. Symbol.toStringTag

    用于创建对象的默认 字符串描述。由内置方法 Object.prototype.toString()使用

    class Test {
        constructor() {
            this[Symbol.toStringTag] = "HEHE"
        }
    }
    
    let test = new Test();
    
    console.log(test.toString());
    console.log(test[Symbol.toStringTag]);