美文网首页
ES6新引入的类型Symbol详解以及示例

ES6新引入的类型Symbol详解以及示例

作者: 开发小师傅 | 来源:发表于2022-05-07 23:20 被阅读0次

    ES6引入的新的原始数据类型Symbol,表示独一无二的值,它是js中的第7种数据类型,是一种类似于字符串的数据类型。
    Symbol特点:
    1、Symbol的值是唯一的,用来解决命名冲突的问题。
    2、Symbol值不能与其他数据进行运算。
    3、Symbol定义的对象属性不能使用for...in循环遍历,但是可以使用Reflect.ownKeys来获取对象的所有键名。然后遍历。

    如何创建?

    可以使用函数创建。
    let s = Symbol('demo');
    或者
    let s1 = Symbol.for('demo');
    使用for函数可以得到某个字符串唯一的symbol值,例如下面的比较是相等的。
    let s2 = Symbol.for('demo');
    s1 === s2 返回的就是true。

    Symbol内置的值

    用做对象的属性。通过对这些内置值的设置,来改变对象在某些特定场景下的表现。
    最常用的有:

    • Symbol.hasInstance
      在使用instanceof操作符时调用。
    class Person{
        static [Symbol.hasInstance](param){
            console.log(param);
            console.log('我被用来检查类型了')
            return false;
        }
    }
    
    let obj = {}
    console.log(obj instanceof Person) 
    
    • Symbol.isConcatSpreadable
      使用concat来合并两个数组的时候,js会默认将数组内的元素展开然后合并为一个数组返回(原数组不变),这个行为es6以后可以使用Symbol.isConcatSpreadable改变,将其置为false就可以将数组作为一整个对象合并到过去。
    let animals = ['cat','dog','panda']
    let fruit = ['apple','pear']
    fruit[Symbol.isConcatSpreadable] = true
    console.log(animals.concat(fruit))  // 结果是['cat','dog','panda','apple','pear']
    fruit[Symbol.isConcatSpreadable] = false
    console.log(animals.concat(fruit))  // 结果是['cat','dog','panda', Array(2)]
    
    • Symbol.iterator
      用于给对象定义迭代方法,这样就可以使用es6中新提供的for...of语法,需要注意的是,自定义的这个迭代器方法,需要按照一定的规范来写,需要返回一个具有next方法的对象,next方法需要返回具有value和done两个属性的对象。done会被用来判断什么时候结束迭代。
    const banji = { 
      name: "终极一班", 
      stus: [ 'xiaoming', 'xiaoning', 'xiaotian', 'knight' ],
      [Symbol.iterator]() {
        // 索引变量 
        let index = 0; 
        // 保存this 
        let _this = this; 
        return { 
          next: function() { 
            if (index < _this.stus.length) {
               const result = { value: _this.stus[index], done: false };
               // 下标自增
               index++; 
               // 返回结果 
               return result; 
            } else { 
               return { value: undefined, done: true }; 
            } 
          } 
        }; 
      }
    }
    // 遍历这个对象 
    for (let v of banji) {
       console.log(v); 
    }
    
    • Symbol.match
      一个在调用 String.prototype.match() 方法时调用的方法, 直白点说就是如果你定义一个字符串strValue,当你调用strValue.match(ObjectCase),如果ObjectCase这个对象定义了Symbol.match方法时,就会执行这个方法。
    let animal = {
        [Symbol.match](str){
            console.log(str)   //打印出字符串 cat
            return str.indexOf('animal');
        }
    }
    let kitty = "cat"
    console.log(kitty.match(animal))    //返回值-1,这里打印出-1
    
    • Symbol.replace
      一个在调用 String.prototype.replace() 方法时调用的方法,和上面的一样,当一个字符串调用strValue.replace(ObjectCase)时,会去执行ObjectCase中的Symbol.replace方法。
    let animalName = {
        /* strValue 会返回调用者本身,replacement为调用repalce方法时传入的第二个参数 */
        [Symbol.replace](strValue, replacement){
            console.log("strValue: " + strValue,"replacement: " + replacement)  //strValue: cat, its name is {params} replacement: {params}
            return strValue.replace("{params}", "kitty")
        }
    }
    let kitty = "cat, its name is {params}"
    console.log(kitty.replace(animalName, "{params}"))  //cat, its name is kitty
    
    • Symbol.search
      一个在调用 String.prototype.search() 方法时调用的方法,用于在字符串中定位子串,这个也和上面str相关扩展一样直接看示例。
    let animalName = {
        /* strValue就是调用者本身  */
        [Symbol.search](strValue){
            console.log(strValue)  //cat, its name is kitty
            return strValue.split("name is")[1]
        }
    }
    let kitty = "cat, its name is kitty"
    console.log(kitty.search(animalName))  //kitty
    
    • Symbol.split
      一个在调用 String.prototype.split() 方法时调用的方法,用于分割字符串。
    let animalName = {
        /* strValue就是调用者本身 */
        [Symbol.split](strValue){
            console.log(strValue)  //cat, its name is kitty
            return strValue.indexOf("name")
        }
    }
    let kitty = "cat, its name is kitty"
    console.log(kitty.split(animalName))  //8
    
    • Symbol.toPrimitive
      这个方法决定了当一个对象被转换为原始值时的行为。JavaScript引擎在每个类型值的原型上定义了Symbol.toPrimitive方法。这个方法被调用时,会接受一个字符串参数,表示当前运算的模式,一共有三种模式:
      number: 该场景下需要转换成数值.对应操作符:* / -
      string: 该场景下需要转换成字符串,显示使用string的构造方法
      default:该场景下可以转换成数值,也可以转换成字符串,对应操作符:+
    let obj = {
        [Symbol.toPrimitive](hint){
            switch(hint){
                case 'number':
                    return 123;
                case 'string':
                    return 'str';
                case 'default':
                    return 'default';
                default:
                    throw new Error();
            }
        }
    }
    console.log(2 * obj) //246
    console.log(3 + obj) //3default
    console.log(obj == 'default') //true
    console.log(String(obj)) //str
    
    • Symbol.species
      对象的Symbol.species属性,指向一个构造函数,创建衍生对象时,会使用该属性。这个的主要用途是,有些类库是在基类的基础上修改的,那么子类使用继承的方法时,作者可能希望返回基类的实例,而不是子类的实例。
    class MyArray extends Array {
        static get [Symbol.species](){
            return Array;
        }
    }
    //b,c是a的衍生对象
    const a = new MyArray(1,2,3)
    const b = a.map(x => x+1)
    const c = a.filter(x => x > 0)
    //如果没有定义上面的Symbol.species,那么下面的instanceof均返回true
    console.log(b instanceof MyArray)   //true
    console.log(c instanceof MyArray)   //true
    //定义了上面的Symbol.species时,返回值是下面这样
    console.log(b instanceof MyArray)   //false
    console.log(b instanceof Array)     //true
    
    • Symbol.toStringTag
      一个在调用 String.prototype.toString() 方法时使用的字符串,用于创建对象描述。在对象上面调用Object.prototype.toString方法时,如果这个属性存在,它的返回值会出现在toString方法返回的字符串中,表示对象的类型。
    //第一种写法,注意这里的get关键字是必须的,call方法需要用到get取值器
    let animal = {
        get [Symbol.toStringTag](){
            return 'animal'
        }
    }
    console.log(Object.prototype.toString.call(animal)) // [object animal]
    //第二种写法
    console.log({[Symbol.toStringTag]: 'tag'}.toString()) // [object tag]
    
    • Symbol.unscopables
      一个定义了一些不可被 with 语句引用的对象属性名称的对象集合。集合中的属性名称,会在with环境绑定中被排除。
    const animal = {
        type: 'animal',
        name: 'kitty'
    }
    with(animal){
        console.log(type, name) //animal kitty
    }
    //定义Symbol.unscopables以便在with环境中排除某些属性
    animal[Symbol.unscopables] = {
        type: true
    }
    with(animal){
        console.log(type)   //Uncaught ReferenceError: type is not defined
    }
    

    以上这些被称为众所周知的symbols,其实其中很多值并不常用,为了验证这些symbols的准确用法,我搜了很多资料,总算把每一个都亲自验证了一遍。
    这里附上一个地址,这里面有特别详细的示例代码,我也是自己费了半天劲儿快验证完的时候才发现这个网址的,白白浪费了好多时间。
    Symbol比较全面的示例

    另外,之所以要研究这个,主要是最近在学vue3 + typescript,身为一个没太多基础的前端小白,学习的过程中遇到一个知识点可能都会要查半天资料才能弄懂,不过我也乐在其中。

    相关文章

      网友评论

          本文标题:ES6新引入的类型Symbol详解以及示例

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