zoukankan      html  css  js  c++  java
  • ES6 (4):Symbol 美丽的标记

    ES6 引入了一种新的原始数据类型Symbol,表示独一无二的值。它是 JavaScript 语言的第七种数据类型,前六种是:undefinednull、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。

    Symbol 值通过Symbol函数生成。这就是说,对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是新增的 Symbol 类型。凡是属性名属于 Symbol 类型,就都是独一无二的,可以保证不会与其他属性名产生冲突

    Symbol函数可以接受一个字符串作为参数,表示对 Symbol 实例的描述,主要是为了在控制台显示,或者转为字符串时,比较容易区分。

    let s1 = Symbol('foo');
    let s2 = Symbol('bar');
    
    s1 // Symbol(foo)
    s2 // Symbol(bar)
    
    s1.toString() // "Symbol(foo)"
    s2.toString() // "Symbol(bar)"
    

     如果 Symbol 的参数是一个对象,就会调用该对象的toString方法,将其转为字符串,然后才生成一个 Symbol 值。

    const obj = {
      toString() {
        return 'abc';
      }
    };
    const sym = Symbol(obj);
    sym // Symbol(abc)
    

     Symbol 值不能与其他类型的值进行运算。

    Symbol 可以显示的转为字符串、布尔值但是不能转为数值:

    let sym = Symbol('My symbol');
    
    String(sym) // 'Symbol(My symbol)'
    sym.toString() // 'Symbol(My symbol)'
    
    let sym = Symbol();
    Boolean(sym) // true
    !sym  // false
    
    if (sym) {
      // ...
    }
    
    Number(sym) // TypeError
    sym + 2 // TypeError
    

    Symbol.prototype.description:

    const sym = Symbol('foo');
    
    String(sym) // "Symbol(foo)"
    sym.toString() // "Symbol(foo)"
    

     ES2019 提供的实例属性。

    属性名(不能实用点运算符,否则会当作普通属性名对待,所以必须放在方括号内使用):

    let mySymbol = Symbol();
    
    // 第一种写法
    let a = {};
    a[mySymbol] = 'Hello!';
    
    // 第二种写法
    let a = {
      [mySymbol]: 'Hello!'
    };
    
    // 第三种写法
    let a = {};
    Object.defineProperty(a, mySymbol, { value: 'Hello!' });
    
    // 以上写法都得到同样结果
    a[mySymbol] // "Hello!"
    

     简洁写法:

    let obj = {
      [s](arg) { ... }
    };
    

     

    定义常量使用:

    const COLOR_RED    = Symbol();
    const COLOR_GREEN  = Symbol();
    
    function getComplement(color) {
      switch (color) {
        case COLOR_RED:
          return COLOR_GREEN;
        case COLOR_GREEN:
          return COLOR_RED;
        default:
          throw new Error('Undefined color');
        }
    }
    

    消除魔术字符串:魔术字符串指的是,在代码之中多次出现、与代码形成强耦合的某一个具体的字符串或者数值。风格良好的代码,应该尽量消除魔术字符串,改由含义清晰的变量代替。

    function getArea(shape, options) {
      let area = 0;
    
      switch (shape) {
        case 'Triangle': // 魔术字符串
          area = .5 * options.width * options.height;
          break;
        /* ... more code ... */
      }
    
      return area;
    }
    
    getArea('Triangle', {  100, height: 100 }); // 魔术字符串
    

     使用Symbol:

    const shapeType = {
      triangle: Symbol()
    };
    
    function getArea(shape, options) {
      let area = 0;
      switch (shape) {
        case shapeType.triangle:
          area = .5 * options.width * options.height;
          break;
      }
      return area;
    }
    
    getArea(shapeType.triangle, {  100, height: 100 });
    

    Symbol 作为属性名,该属性不会出现在for...infor...of循环中,也不会被Object.keys()Object.getOwnPropertyNames()JSON.stringify()返回。但是,它也不是私有属性,有一个Object.getOwnPropertySymbols方法,可以获取指定对象的所有 Symbol 属性名

     Reflect.ownKeys方法:可以返回所有类型的键名,包括常规键名和 Symbol 键名

     

     Symbol.for()/Symbol.keyFor():它接受一个字符串作为参数,然后搜索有没有以该参数作为名称的 Symbol 值。如果有,就返回这个 Symbol 值,否则就新建并返回一个以该字符串为名称的 Symbol 值。(需要注意的是Symbol 是同一个Symbol,是复用)

    let s1 = Symbol.for('foo');
    let s2 = Symbol.for('foo');
    
    s1 === s2 // true
    

     Symbol.keyFor方法返回一个已登记的 Symbol 类型值的key。(参数是Symbol,不是字符串,注意)

     

     单例模式:

    // mod.js
    function A() {
      this.foo = 'hello';
    }
    
    if (!global._foo) {
      global._foo = new A();
    }
    
    module.exports = global._foo;
    

     使用Symbol 避免其他文件修改

    // mod.js
    const FOO_KEY = Symbol.for('foo');
    
    function A() {
      this.foo = 'hello';
    }
    
    if (!global[FOO_KEY]) {
      global[FOO_KEY] = new A();
    }
    
    module.exports = global[FOO_KEY];
    

     如果键名使用Symbol方法生成,那么外部将无法引用这个值,当然也就无法改写

    // mod.js
    const FOO_KEY = Symbol('foo');
    

    内置的Symbol:

    1.hasinstance:判断是否为某对象实例

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

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

    let arr1 = ['c', 'd'];
    ['a', 'b'].concat(arr1, 'e') // ['a', 'b', 'c', 'd', 'e']
    arr1[Symbol.isConcatSpreadable] // undefined
    
    let arr2 = ['c', 'd'];
    arr2[Symbol.isConcatSpreadable] = false;
    ['a', 'b'].concat(arr2, 'e') // ['a', 'b', ['c','d'], 'e']
    

     数组的默认行为是可以展开,Symbol.isConcatSpreadable默认等于undefined。该属性等于true时,也有展开的效果。

    3.species:执行构造函数

    Symbol.species的作用在于,实例对象在运行过程中,需要再次调用自身的构造函数时,会调用该属性指定的构造函数。它主要的用途是,有些类库是在基类的基础上修改的,那么子类使用继承的方法时,作者可能希望返回基类的实例,而不是子类的实例。

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

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

    const x = {};
    x[Symbol.replace] = (...s) => console.log(s);
    
    'Hello'.replace(x, 'World') // ["Hello", "World"]
    

     此处返回值就是打印的值,扩展修饰符转换为数组对象,所以最终返回的值也就是打印的值["Hello", "World"]。

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

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

    String.prototype.split(separator, limit)
    // 等同于
    separator[Symbol.split](this, limit)
    

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

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

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

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

     

    // 例一
    ({[Symbol.toStringTag]: 'Foo'}.toString())
    // "[object Foo]"
    
    // 例二
    class Collection {
      get [Symbol.toStringTag]() {
        return 'xxx';
      }
    }
    let x = new Collection();
    Object.prototype.toString.call(x) // "[object xxx]"
    

     

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

    成灰之前,抓紧时间做点事!!
  • 相关阅读:
    hdu4651(广义五边形数 & 分割函数1)
    Java基础面试题1
    Java8新特性--Lambda表达式
    Java中list在循环中删除元素的坑
    Java多线程面试题整理
    Java并发包--ConcurrentHashMap原理解析
    HashMap原理解析
    Java原子类--AtomicLongFieldUpdater
    Java原子类--AtomicReference
    Java原子类--AtomicLongArray
  • 原文地址:https://www.cnblogs.com/jony-it/p/10952018.html
Copyright © 2011-2022 走看看