【ECMAScript 学习笔记】4.Symbol 的使用

以下内容摘自 ECMAScript6 入门 Symbol 篇

Symbol 的概念

  Symbol 是 ES6 提供的一个新的基本数据类型,表示独一无二的值。

  在实际开发过程中,我们可能使用到别人提供的对象,但又想要为这个对象添加新的属性,那么这个新的属性就有可能与现有属性冲突。如果有一种机制,可以从根本根本上防止属性名冲突就好了,这也就是 Symbol 出现的原因。

Symbol 的使用

  Symbol 值通过 Symbol 函数生成。因为 Symbol 是 ES6 提供的一种新的 数据类型,因此使用 Symbol 时,前面不能加 new,它基本上类似于字符串的数据类型。

    const s1 = Symbol('foo');
    console.log(s1); // Symbol(foo)
  • Symbol 函数的参数只是表示当前 Symbol 值的描述,因此相同的 Symbol 函数的返回值不相等。
    const s1 = Symbol('foo');
    const s2 = Symbol('foo');
    console.log(s1 === s2); // false
  • Symbol 值不能与其它类型的值进行运算,会报错。
  • Symbol 可以显示的转换字符串类型、布尔类型,但不能转为数值类型。
    let s1 = Symbol(0);
    console.log(s1.toString()); // 'Symbol(My symbol)'
    console.log(Boolean(s1)); // true

Symbol 作为属性名

  由于每一个 Symbol 的值都是不相等的,因此 Symbol 可以作为标识符。

    const mySymbol = Symbol();

    // 第一种写法
    const a = {};
    a[mySymbol] = 'Hello!';

    // 第二种写法
    const a = {
        [mySymbol]: 'Hello!'
    };

    // 第三种写法
    const a = {};
    Object.defineProperty(a,mySymbol,'Hello!');

    // 以上的写法都得到同样的结果
    console.log(a[mySymbol]); // "Hello!"

Symbol 作为对象的属性名时,不能使用点运算符。

在对象内部,使用 Symbol 定义属性时, Symbol 值必须放在方括号之中。

    const mySymbol = Symbol();

    const obj = {
        [mySymbol] : function(arg){
            console.log(arg);
        }
    };

    obj[mySymbol]('Hello'); // Hello

Symbol 作为对象属性时,该属性为公开属性,不是私有属性。

遍历属性名

  Symbol 作为属性名时,该属性不会出现在 for...infor...of 循环中,也不会在 Object.keys()Object.getOwnPropertyNames()Json.stringify()返回。但可以通过下面的 API 获取 指定对象的所有 Symbol 属性名。

  • Object.getOwnPropertySymbols 方法返回一个数组,成员为当前对象的所有用作属性名的 Symbol
    const obj = {
        [Symbol('name')]: 'hvkcoder',
        [Symbol('age')]: 18
    };
    const keys = Object.getOwnPropertySymbols(obj);
    console.log(keys); // [ Symbol(name), Symbol(age) ]
  • Reflect.ownKeys 方法可以返回所有类型的键名,包括常规键名和 Symbol 键名
    const obj = {
        [Symbol('name')]: 'hvkcoder',
        [Symbol('age')]: 18,
        job: 'coder'
    };
    const keys = Reflect.ownKeys(obj);
    console.log(keys); // [ 'job', Symbol(name), Symbol(age) ]

Symbol.for()、Symbol.keyFor()

  • Symbol.for

  有时我们可能需要重新使用同一个 Symbol 的值,Symbol.for 接受一个字符串作为参数,然后通过该参数去查找是否存在以该参数作为名称的 Symbol 值,如果存在则就返回这个 Symbol 值,否则就新建并返回一个以该字符串为名称的 Symbol 值。

    const s1 = Symbol.for('foo');
    const s2 = Symbol.for('foo');

    console.log(s1 === s2); // true

Symbol.for() 会被登记在全局环境中供搜索,因此可以在 iframeserver worker 中获取到同一个值。

  • Symbol.keyFor

  Symbol.keyFor() 会返回已登记的 Symbol 类型值的key。

    const s1 = Symbol('foo');
    const key = Symbol.keyFor(s1);
    console.log(key); // foo

内置的 Symbol 值

  • Symbol.hasInstance

  对象的 Symbol.hasInstance 属性,指向一个内部方法。当其他对象使用 instanceof 运算符,判断是否为该对象的实例时,会调用这个方法。

    class MyClass {
        [Symbol.hasInstance](foo) {
            return foo instanceof Array;
        }
    }
    console.log([1, 2, 3] instanceof new MyClass()); // true
  • Symbol.isConcatSpreadable

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

    const obj = {length: 2, 0: 'c', 1: 'd'};
    ['a', 'b'].concat(obj, 'e') // ['a', 'b', obj, 'e']

    obj[Symbol.isConcatSpreadable] = true;
    ['a', 'b'].concat(obj, 'e') // ['a', 'b', 'c', 'd', 'e']
  • Symbol.species

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

    class Test extends Array {
        constructor(args) {
            super(args);
        };
        static get [Symbol.species]() { return Array; }
    }
    const t1 = new Test();
    const t2 = t1.map(x => x);
    console.log(t1 instanceof Test); // true
    console.log(t2 instanceof Test); // false

  Symbol.species 的作用在于,实例对象在运行过程中,需要再次调用自身的构造函数时,会调用该属性指定的构造函数。

  • Symbol.match

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

    class MyMatcher {
        [Symbol.match](string) {
            return 'hello world'.indexOf(string);
        }
    }
    console.log('e'.indexOf(new MyMatcher())); // -1
  • Symbol.replace

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

    const x = {};
    x[Symbol.replace] = (...arg) => console.log(arg);

    'Hello'.replace(x,'World'); // ["Hello","World"]

   Symbol.replace 方法会收到两个参数,第一个参数是 replace 方法正在作用的对象,第二个参数是替换后的值。

  • Symbol.search

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

    class MySearch{
        constructor(value){
            this.value = value;
        }
        [Symbol.search](string){
            return string.indexOf(this.value);
        }
    }
    'foobar'.search(new MySearch('foo')); // 0
  • Symbol.split

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

    class MySplitter{
        constructor(value){
            this.value = value;
        }
        [Symbol.split](string){
            const index = string.indexOf(this.value);
            if(index === -1){
                return string;
            }
            return [
                string.substr(0, index),
                string.substr(index + this.value.length)
            ];
        }
    }

    'foobar'.split(new MySplitter('foo')); // ['', 'bar']
  • Symbol.iterator

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

    class Collection {
        *[Symbol.iterator]() {
            let i = 0;
            while (this[i] !== undefined) {
                yield this[i];
                ++i;
            }
        }
    };

    const myCollection = new Collection();
    myCollection[0] = 1;
    myCollection[1] = 2;

    for(let value of myCollection){
        console.log(value); // 1 2
    }
  • Symbol.toPrimitive

  对象的 Symbol.toPrimitive 属性,指向一个方法。该对象被转为原始属性时,会调用这个方法,返回该对象对应的原始属性。

Symbol.toPrimitive 被调用时,会接受一个字符串参数,表示当前运算的模式,一共有三种模式:
1. Number:该场合需要转成数值
2. String:该场合需要转成字符串
3. Default:该场合可以转成数值,也可以转成字符串

  • Symbol.toStringTag

  对象的 Symbol.toStringTag 属性,指向一个方法。在该对象上面调用 Object.prototype.toString 方法时,如果这个属性存在,它的返回值会出现在 toString 方法返回的字符串之中,表示对象的类型。

  • Symbol.unscopables

  对象的 Symbol.unscopables 属性,指向一个对象。该对象指定了使用 with 关键字时,哪些属性会被 with 环境排除。

猜你喜欢

转载自blog.csdn.net/hvkcoder/article/details/79780417
今日推荐