美文网首页
ES6 之 Symbol

ES6 之 Symbol

作者: isnan | 来源:发表于2019-11-26 23:00 被阅读0次

    1. 基本用法

    • Symbol 是ES6引入的一种新的原始数据类型,表示独一无二的值。
    • 前六种基础数据类型是 undefined null Boolean String Number Object
    • Symbol 值通过Symbol函数生成 可以接受一个字符串作为参数,主要为了区分不同Symbol值
    • 参数若是一个对象,则会调用该对象的 toString 方法转成字符串 传递给Symbol
      ***** 参数只做为一个描述存在,同样的参数生成的Symbol也不相等
      ***** 不能用 new 创建

    围绕原始数据类型创建一个显式包装器对象从 ECMAScript 6 开始不再被支持。然而,现有的原始包装器对象,如 new Boolean、new String以及new Number,因为遗留原因仍可被创建。

    let s1 = Symbol('foo');
    let s2 = Symbol('bar');
    let s3 = Symbol('bar');
    console.log(s1);  // Symbol(foo)
    console.log(typeof s1); // symbol
    console.log(String(s2)); // "Symbol(bar)"  不能用new String
    console.log(Boolean(s2)); // true
    console.log(Number(s2)); // 报错 TypeError: Cannot convert a Symbol value to a number
    console.log(s2 === s3); //false
    console.log(s3.description); //"bar"  (ES2019)
    
    let obj1 = {
      a: 12
    };
    let obj2 = {
      a: 12,
      toString() {}
    };
    let obj3 = {
      a: 12,
      toString() {
        return this.a;
      }
    };
    let s4 = Symbol(obj1);
    let s5 = Symbol(obj2);
    let s6 = Symbol(obj3);
    console.log(s4);  //Symbol([object Object])
    console.log(s5);  //Symbol(undefined)
    console.log(s6);  //Symbol(12)
    

    2. 使用场景

    • 基本应用场景有两种,一是作为key ,一是作为value
      *****作为key时 只能用 [] 定义 , 不能用 .
    let s7 = Symbol();
    let s8 = Symbol();
    let s9 = Symbol();
    let jo = {
      name: 'nan',
      age: 18,
      [s7]: 'Hi'
    };
    jo[s8] = 'hook';
    Object.defineProperty(jo, s9, {
      value: 'jay',
      enumerable: true
    })
    console.log(jo); //{ name: 'nan', age: 18, [Symbol()]: 'Hi', [Symbol()]: 'hook', [Symbol()]: 'jay' }
    console.log(jo[s8]);  //hook
    // 是无法通过正常的遍历方法取到Symbol 属性名的 
    // for...in / for...of / Object.keys() / Object.getOwnPropertyNames() / JSON.stringify()
    // 可以通过 Object.getOwnPropertySymbols() / Reflect.ownKeys()
    // 可以利用这个特点来定义一些不暴露出去的属性或方法
    console.log(Object.keys(jo)); //[ 'name', 'age' ]
    console.log(Object.getOwnPropertyNames(jo)); //[ 'name', 'age' ]
    console.log(Object.getOwnPropertySymbols(jo));  //[ Symbol(), Symbol(), Symbol() ]
    console.log(Reflect.ownKeys(jo)); //[ 'name', 'age', Symbol(), Symbol(), Symbol() ]
    

    *****作为value , 可以定义一组常量 , 并且保证这些常量的值不想等
    当只想定义一组常量 , 并不关心值的时候 ,就可以使用Symbol来定义

    const PENDING = Symbol();
    const SUCCESS = Symbol();
    const FAIL = Symbol();
    class Promise {
      constructor (executor) {
          this.state = PENDING;
          let resolve = value => {
              if (this.state === PENDING) {
                  this.state = SUCCESS;
                  // ...
              }
          }
          let reject = reason => {
              if (this.state === PENDING) {
                  this.state = FAIL;
                  // ...
              }
          }
          executor(resolve, reject);
      }
    }
    

    3. Symbol.for() & Symbol.keyFor()

    • Symbol.for() 接收一个字符串,然后搜索有没有以该参数作为名称的 Symbol 值, 有则直接返回,没有创建一个新的Symbol 且注册到全局 (可以应用到iframe中)
    • Symbol.keyFor 返回一个Symbol.for登记过值的 key
    let s10 = Symbol.for('foo');
    let s11 = Symbol('foo');
    console.log(Symbol.for('foo') === s10); //true
    console.log(s11 === s10); //false
    console.log(Symbol.keyFor(s10));  //"foo"
    console.log(Symbol.keyFor(s11));  //undefined
    

    4. 属性

    1. Symbol.hasInstance 用于判断某对象是否为某构造器的实例。因此你可以用它自定义 instanceof 操作符在某个类上的行为。
    class Counts {
      static [Symbol.hasInstance](instance) {
        return !(instance % 2); //是否被 2 整除
      }
    }
    console.log(1 instanceof Counts); //false
    console.log(4 instanceof Counts); // true
    
    1. Symbol.isConcatSpreadable 符号用于配置某对象作为Array.prototype.concat()方法的参数时是否展开其数组元素。
    let arr1 = [1, 2, 3];
    let arr2 = ['a', 'b'];
    console.log(arr1.concat(arr2)); //[ 1, 2, 3, 'a', 'b' ]
    arr2[Symbol.isConcatSpreadable] = false;
    console.log(arr1.concat(arr2)); //[ 1, 2, 3, ['a', 'b'] ]
    
    1. Symbol.species 是个函数值属性,其被构造函数用以创建派生对象。

    你可能想在扩展数组类 MyArray 上返回 Array 对象。 例如,当使用例如 map() 这样的方法返回默认的构造函数时,你希望这些方法能够返回父级的 Array 对象,以取代 MyArray 对象。Symbol.species 允许你这么做:

    class MyArray extends Array {
      // 覆盖 species 到父级的 Array 构造函数上
      static get [Symbol.species]() { return Array; }
    }
    let a = new MyArray(1,2,3);
    let mapped = a.map(x => x * x);
    console.log(mapped instanceof MyArray); // false
    console.log(mapped instanceof Array);   // true
    
    1. Symbol.iterator 为每一个对象定义了默认的迭代器。该迭代器可以被 for...of 循环使用。
    let myIterable = {}
    myIterable[Symbol.iterator] = function* () {
        let i = 0;
        while (i < 5) {
          yield i;
          i++;
        }
    };
    console.log([...myIterable]); //[ 0, 1, 2, 3, 4 ]
    function getOdd () {
      let arr = [];
      for (let i of myIterable) {
        !(i%2) && arr.push(i);
      }
      return arr;
    }
    console.log(getOdd()); //[ 0, 2, 4 ]
    
    1. Symbol.asyncIterator 符号指定了一个对象的默认异步迭代器。如果一个对象设置了这个属性,它就是异步可迭代对象,可用于for await...of循环。
    let asyncIterable = {
      [Symbol.asyncIterator]() {
        return {
          i: 0,
          next() {
            if (this.i < 3) {
              return Promise.resolve({ value: this.i++, done: false });
            }
    
            return Promise.resolve({ done: true });
          }
        };
      }
    };
    
    (async function() {
       for await (num of asyncIterable) {
         console.log(num); // 0 1 2
       }
    })();
    

    ***** 其他属性和方法可以参考 MDN 文档


    参考文献

    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol
    http://es6.ruanyifeng.com/#docs/symbol

    相关文章

      网友评论

          本文标题:ES6 之 Symbol

          本文链接:https://www.haomeiwen.com/subject/igquwctx.html