美文网首页
内置Symbol属性

内置Symbol属性

作者: ITgecko | 来源:发表于2018-10-19 14:35 被阅读0次

    内置Symbol属性

    • Symbol是ES6引入的内置(built-in)全局函数,它自带一些内置好的属性。下面依次介绍一下。
    Symbol.hasInstance
    • Symbol.hasInstance用来定义一个构造对象/类(class)如何识别一个变量是否是他的实例,对应的instanceof命令符。
    // 示例
    class Array1 {
      static [Symbol.hasInstance](instance) {
        return Array.isArray(instance);
      }
    }
    
    console.log([] instanceof Array1); // true
    
    • 即使不是构造对象,普通对象也能定义它的Symbol.hasInstance。这样甚至可以用instanceof来进行值的校验,比如判断一个字符串是否是手机号码格式,就可以改写成这样:
    const Ismoblie = {
        [Symbol.hasInstance]: function(text) {
        var pattern = /^1[3-9]\d{9}$/
        return pattern.test(text)
      }
    }
    'abc' instanceof Ismoblie // false
    '18312345678' instanceof Ismoblie // true
    
    Symbol.isConcatSpreadable
    • Symbol.isConcatSpreadable用来配置一个数组对象,表示他在使用Array.prototype.concat()
      方法时,数组元素是否能够被展开。对应的值为真值true时就可以,false时就不展开。
    const alpha = ['a', 'b', 'c'];
    const numeric = [1, 2, 3];
    let alphaNumeric = alpha.concat(numeric);
    
    console.log(alphaNumeric);
    // expected output: Array ["a", "b", "c", 1, 2, 3]
    
    numeric[Symbol.isConcatSpreadable] = false;
    alphaNumeric = alpha.concat(numeric);
    
    console.log(alphaNumeric);
    // expected output: Array ["a", "b", "c", Array [1, 2, 3]]
    
    • Symbol.isConcatSpreadable属性只能规定在调用concat函数时不能展开,使用...时仍然还是能够展开然后拼接的。
    var arr1 = [1,2]
    var arr2 = [3,4]
    arr1[Symbol.isConcatSpreadable] = false
    arr1[Symbol.isConcatSpreadable] = false
    console.log([...arr1, ...arr2])
    // expected output: Array [1,2,3,4]
    
    Symbol.iterator
    • Symbol.iterator用来声明一个对象的默认遍历器(iterator),主要结合for...of使用,for...of会遍历那些可遍历(Iterable)的对象,执行对象的Symbol.iterator所对应的generator函数。普通对象是没有遍历器接口的,为它指定了Symbol.iterator之后,就可以用for...of遍历它了,注意Symbol.iterator对应的一定是一个generator函数。
    const iterable1 = new Object();
    
    iterable1[Symbol.iterator] = function* () {
      yield 1;
      yield 2;
      yield 3;
    };
    
    console.log([...iterable1]);
    // expected output: Array [1, 2, 3]
    
    
    Symbol.match
    • 定义了Symbol.match属性的对象,在执行*.match(obj)时,就会调用Symbol.match对应的函数,如果不是函数,则会报错。(这个Symbol感觉没什么用)
    let str = new String('123')
    str[Symbol.match] = function (arg) {
      console.log(arg);
      return false
    }
    '123'.match(str)
    // console输出123,返回值为false
    
    Symbol.replace
    • 与Symbol.match类似,定义了Symbol.replace属性的对象,在执行string.replace(obj)时,就会调用Symbol.match对应的函数,如果不是函数,则会报错。(这个Symbol感觉没什么用)
    class Replace1 {
      constructor(value) {
        this.value = value;
      }
      [Symbol.replace](string) {
        return `s/${string}/${this.value}/g`;
      }
    }
    
    console.log('foo'.replace(new Replace1('bar')));
    // expected output: "s/foo/bar/g"
    
    Symbol.search
    • 同上,定义了Symbol.search属性的对象,再执行String.prototype.search方法时,会调用Symbol.search指向的函数。
    class Search1 {
      constructor(value) {
        this.value = value;
      }
      [Symbol.search](string) {
        return string.indexOf(this.value);
      }
    }
    
    console.log('foobar'.search(new Search1('bar')));
    // expected output: 3
    
    Symbol.species
    • Symbol.species指向一个构造函数。创建衍生对象时,会使用该函数返回的属性。正常情况下,例如一个类Array1继承自Array,那么Array1的实例对象产生的衍生对象,既是Array1的实例,又是Array的实例。如果像下面这样定义Symbol.species之后则会这样:
    class Array1 extends Array {
      static get [Symbol.species]() { return Array; }
    }
    
    const a = new Array1(1, 2, 3);
    const mapped = a.map(x => x * x);
    
    console.log(mapped instanceof Array1);
    // expected output: false
    
    console.log(mapped instanceof Array);
    // expected output: true
    
    • 所谓产生衍生对象,对于数组对象来说,包括了filtermapslice这些函数生成的对象。而对于promise对象,thencatch函数返回的都是一个新的promise对象,这也是衍生对象。
    class T1 extends Promise {
    }
    
    class T2 extends Promise {
      static get [Symbol.species]() {
        return Promise;
      }
    }
    
    new T1(r => r()).then(v => v) instanceof T1 // true
    new T2(r => r()).then(v => v) instanceof T2 // false
    
    • 至此Symbol.species的主要作用就在于,子类的实例生成衍生对象时,可以通过修改Symbol.species,让衍生对象通过父类来创造,不通过子类。
    Symbol.split
    • 对象的Symbol.split属性,指向一个方法,当该对象被String.prototype.split方法调用时,会返回该方法的返回值。
    class Split1 {
      constructor(value) {
        this.value = value;
      }
      [Symbol.split](string) {
        var index = string.indexOf(this.value);
        return this.value + string.substr(0, index) + "/"
          + string.substr(index + this.value.length);
      }
    }
    
    console.log('foobar'.split(new Split1('foo')));
    // expected output: "foo/bar"
    
    
    Symbol.toPrimitive
    • Symbol.toPrimitive用于声明一个值为函数的属性,当一个对象要转换为一个相应的原始(primitive)值时,会调用该函数,该函数接收一个字符串参数,根据场景有不同的值:
      • Number:该场合需要转成数值
      • String:该场合需要转成字符串
      • Default:该场合可以转成数值,也可以转成字符串
    let a = {
      valueOf() {
        return 0;
      },
      toString() {
        return '1';
      },
      [Symbol.toPrimitive](hint) {
        switch (hint) {
          case 'number':
            return 123;
          case 'string':
            return 'str';
          case 'default':
            return 'default';
          default:
            throw new Error();
         }
      }
    }
    2 * a // 246
    3 + a // '3default'
    a == 'default' // true
    String(a) // 'str' 
    
    • 注意上面代码中,我同时还为a对象定义了valueOftoString函数,但实际发生toPrimitive类型转换时,实际执行的是Symbol.toPrimitive对应的函数,也就是说Symbol.toPrimitive的优先级更高。
    Symbol.toStringTag
    • 对象的Symbol.toStringTag属性,指向一个方法。在该对象上面调用Object.prototype.toString方法时,如果这个属性存在,它的返回值会出现在toString方法返回的字符串之中,表示对象的类型。也就是说,这个属性可以用来定制[object Object]或[object Array]中object后面的那个字符串。
    class ValidatorClass {
      get [Symbol.toStringTag]() {
        return 'Validator';
      }
    }
    
    console.log(Object.prototype.toString.call(new ValidatorClass()));
    // expected output: "[object Validator]"
    let c = {}
    c[Symbol.toStringTag] = 'nike sb'
    c.toString()
    // expected output: "[object nike sb]"
    

    相关文章

      网友评论

          本文标题:内置Symbol属性

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