ES6 Symbol探究

2020-10-07  本文已影响0人  弱冠而不立

Symbol 是什么

Symbol ===> 象征;符号;标志
在 ES6 中是一种新的数据类型,它的目的是为了给数据创建一个唯一的标识,让数据不和其他数据冲突。

Symbol 怎么用

  1. Symbol()

参数:description [number | string],用来对symbol的描述,可用于调试但不能访问symbol本身。
返回值:一个Symbol类型的值,返回的Symbol类型的值有一个自带的 description 属性,可以获取到对于这个Symbol的描述。但是不能给 Symbol 类型的值添加自定义属性。

<script>
    let symbol_test = Symbol();
    let symbol_1 = Symbol(1);
    let symbol_str1 = Symbol("1");
    let symbol_1_bak = Symbol(1);
    console.log(symbol_test);  //===> Symbol()
    console.log(symbol_1);  //===> Symbol(1)
    console.log(symbol_str1);  //===> Symbol(1)
    console.log(symbol_1_bak);  //===> Symbol(1)
    console.log(symbol_1 == symbol_str1);  //===> false    
    console.log(symbol_1 == symbol_1_bak);  //===> false    
    console.log(symbol_test.description);  //===> undefined
    console.log(symbol_1.description, typeof symbol_1.description);  //===> 1 string
    console.log(symbol_str1.description, typeof symbol_str1.description);  //===> 1 string
    console.log(symbol_1_bak.description); //===> 1
</script>
  1. Symbol.for(key)

参数: key[string],作为 symbol 注册表中与某 symbol 关联的键(同时也会作为该 symbol 的描述)。
返回值:返回由给定的 key 找到的 symbol,否则就是返回新创建的 symbol

Symbol() 的区别Symbol.for(key)方法会根据给定的键 key,来从运行时的 symbol 注册表中找到对应的 symbol,如果找到了,则返回它,否则,新建一个与该键关联的 symbol,并放入全局 symbol 注册表中。

<script>
    let symbol_undefined = Symbol.for();
    let symbol_undefined_bak = Symbol.for();
    let symbol_1 = Symbol.for(1);
    let symbol_str1 = Symbol.for("1");
    let symbol_1_bak = Symbol.for(1);
    console.log(symbol_undefined);  //===> Symbol(undefined)
    console.log(symbol_1);  //===>Symbol(1)
    console.log(symbol_str1);  //===>Symbol(1)
    console.log(symbol_1_bak);  //===>Symbol(1)
    console.log(symbol_undefined == symbol_undefined_bak);  //===> true
    console.log(symbol_1 == symbol_str1); //===> true
    console.log(symbol_1 == symbol_1_bak); //===> true
    console.log(symbol_undefined.description); //===> undefined
    console.log(symbol_1.description, typeof symbol_1.description); //===> 1 string
    console.log(symbol_str1.description, typeof symbol_str1.description); //===> 1 string
    console.log(symbol_1_bak.description); //===> 1
</script>
  1. Symbol.keyFor(sym)

用途:Symbol.keyFor(sym) 方法用来获取 symbol 注册表中与某个 symbol 关联的键。
参数:必选参数,存储在 symbol 注册表中的某个 symbol
返回值:如果全局注册表中查找到该 symbol,则返回该symbolkey值,形式为string。如果symbol未在注册表中,返回undefined

<script>
    let symbol_undefined = Symbol.for();
    let symbol_1 = Symbol.for(1);
    let symbol_str1 = Symbol.for("1");
    console.log(Symbol.keyFor(symbol_undefined));  //===> undefined
    console.log(Symbol.keyFor(symbol_1));  //===> 1
    console.log(Symbol.keyFor(symbol_str1));  //===> 1
</script>

内置的Symbol值

除了定义自己使用的 Symbol 值以外,ES6 还提供了一些内置的 Symbol 值,指向语言内部使用的方法。

  1. Symbol.hasInstance

对象的Symbol.hasInstance属性,指向一个内部方法。当其他对象使用instanceof运算符,判断是否为该对象的实例时,会调用这个方法。比如,foo instanceof Foo在语言内部,实际调用的是Foo[Symbol.hasInstance](foo)

<script>
    function MyFunc(num) {
        this.num = num
    }

    MyFunc.prototype[Symbol.hasInstance] = (num) => {
        // instanceof new MyFunc() 也会调用这一行
        console.log("MyFunc.prototype[Symbol.hasInstance]---num",num);
        return Number(num) % 2 === 0
    }

    console.log(2 instanceof new MyFunc()); // true
    console.log(3 instanceof new MyFunc()); // false
    console.log(MyFunc.prototype[Symbol.hasInstance](2)); // true
    console.log(MyFunc.prototype[Symbol.hasInstance](3)); // false

</script>

<script>
    class MyClass {
        [Symbol.hasInstance](num) {
            // instanceof new MyClass() 也是会调用这一行
            console.log("MyClass.prototype[Symbol.hasInstance]---num",num);
            return Number(num) % 2 === 0
        }
    }

    console.log(2 instanceof new MyClass()); //true
    console.log(3 instanceof new MyClass()); // false
    console.log(MyFunc.prototype[Symbol.hasInstance](2)); // true
    console.log(MyFunc.prototype[Symbol.hasInstance](3)); // false
</script>
  1. Symbol.isConcatSpreadable

对象的Symbol.isConcatSpreadable属性等于一个布尔值,表示该对象用于Array.prototype.concat()时,是否可以展开。

<script>
    let arr1 = [3, 4, 5];
    console.log([1, 2].concat(arr1, 6));  // [1, 2, 3, 4, 5, 6]
    console.log(arr1[Symbol.isConcatSpreadable]);  // undefined
</script>

<script>
    //数组的默认行为是可以展开,Symbol.isConcatSpreadable默认等于undefined。该属性等于true时,也有展开的效果。
    let arr2 = [3, 4, 5];
    arr2[Symbol.isConcatSpreadable] = false
    console.log([1, 2].concat(arr2, 6)); //[1, 2, [3, 4, 5], 6]

    arr2[Symbol.isConcatSpreadable] = true
    console.log([1, 2].concat(arr2, 6)); //[1, 2, 3, 4, 5, 6]
</script>
  1. Symbol.species

对象的Symbol.species属性,指向一个构造函数。创建衍生对象时,会使用该属性。例如

<script>
    /**
     * 子类MyArray继承了父类Array,a是MyArray的实例,b和c是a的衍生对象。
     * 你可能会认为,b和c都是调用数组方法生成的,所以应该是数组(Array的实例),
     * 但实际上它们也是MyArray的实例。
     **/
    class MyArray extends Array {
    }

    const a = new MyArray(1, 2, 3);
    const b = a.map(x => x);

    console.log(b instanceof MyArray); // true
</script>
<script>
    /**
     * 由于定义了Symbol.species属性,创建衍生对象时就会使用这个属性返回的函数,作为构造函数。
     * 这个例子也说明,定义Symbol.species属性要采用get取值器。
     * a.map(x => x)生成的衍生对象,就不是MyArray的实例,而直接就是Array的实例
     **/
    class MyArray extends Array {
        static get [Symbol.species]() { return Array; }
    }

    const a = new MyArray();
    const b = a.map(x => x);

    console.log(a instanceof Array);    //true
    console.log(a instanceof MyArray); //true
    b instanceof MyArray // false
    b instanceof Array // true
</script>
  1. Symbol.match

对象的Symbol.match属性,指向一个函数。当执行str.match(myObject)时,如果该属性存在,会调用它,返回该方法的返回值。

<script>
    class MyMatch {
        [Symbol.match](str) {
            return str.length > 2
        }
    }

    console.log("a".match(new MyMatch)); // true
    console.log("hello".match(new MyMatch)); //false
</script>

类似的还有:

  1. Symbol.replace

对象的Symbol.replace属性,指向一个方法,当该对象被String.prototype.replace方法调用时,会返回该方法的返回值。

String.prototype.replace(searchValue, replaceValue)
// 等同于
searchValue[Symbol.replace](this, replaceValue)
const x = {};
x[Symbol.replace] = (...s) => console.log(s);

'Hello'.replace(x, 'World') // ["Hello", "World"]
  1. Symbol.search

对象的Symbol.search属性,指向一个方法,当该对象被String.prototype.search方法调用时,会返回该方法的返回值。

  1. Symbol.split

对象的Symbol.split属性,指向一个方法,当该对象被String.prototype.split方法调用时,会返回该方法的返回值。

  1. Symbol.iterator

对象的Symbol.iterator属性,指向该对象的默认遍历器方法。

const myIterable = {};
myIterable[Symbol.iterator] = function* () {
  yield 1;
  yield 2;
  yield 3;
};

[...myIterable] // [1, 2, 3]
  1. Symbol.toPrimitive

对象的Symbol.toPrimitive属性,指向一个方法。该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值。Symbol.toPrimitive被调用时,会接受一个字符串参数,表示当前运算的模式,一共有三种模式。
Number:该场合需要转成数值
String:该场合需要转成字符串
Default:该场合可以转成数值,也可以转成字符串

<script>
    let obj = {
        [Symbol.toPrimitive](hint) {
            switch (hint) {
                case 'number':
                    return 0;
                case 'string':
                    return 'str';
                case 'default':
                    return 'default';
                default:
                    throw new Error();
            }
        }
    };

    console.log(2 * obj); // 0
    console.log(3 + obj); // '3default'
    console.log(obj == 'default'); // true
    console.log(String(obj)); // 'str'
</script>
  1. Symbol.toStringTag

对象的Symbol.toStringTag属性,指向一个方法。在该对象上面调用Object.prototype.toString方法时,如果这个属性存在,它的返回值会出现在toString方法返回的字符串之中,表示对象的类型。也就是说,这个属性可以用来定制[object Object][object Array]object后面的那个字符串。例如:
Object.prototype.toString.call([]) -> "[object Array]"

<script>
    class MyClass {
        get [Symbol.toStringTag]() {
            return 'MyClass';
        }
    }

    console.log(Object.prototype.toString.call(new MyClass()));  //"[object MyClass]"
</script>
上一篇 下一篇

猜你喜欢

热点阅读