ES6笔记( 七 )- Symbol

ES6笔记( 七 )- Symbol

符号: 符号是ES6新增的一个数据类型, 通过Symbol(符号名)来创建

目录:

  1. 符号概览
  2. 普通符号
  3. 共享符号
  4. 知名( 具名, 公共 )符号

符号概览

在开始之前, 咱先回顾一下ES6之前的我们所知道的数据类型

string, number, boolean, object, undefined, null

符号设计的初衷: 是为了给对象设置私有属性

在过去我们JS是没有私有成员这一说的( 当然你通过特殊手段实现的不算 ), 但是私有成员又是非常有必要的, 特别是想__proto__这类属性, 官方其实是不想让你看到的, 但是我们不仅看到了我们还操控了, 相较于我们个人而言, 自己在开发的时候也有很多东西是想直接不让别人操控的, 过去我们都会习惯性的用语义化命名( 变量前加上_)来提示其他合作伙伴这个属性是我私有的, 你别去操作

ES6的Symbol( 符号 )帮助我们处理了这个问题, 至于怎么处理的, 继续往下看吧

普通符号

符号的创建

const syb = Symbol();
// Symbol函数接收的参数为描述信息, 用于方便开发者调试( 知道这个符号到底是干什么用 )
const syb2 = Symbol('helloWorld');

符号具有以下特点:

  1. 没有字面量, 要想使用符号必须通过Symbol函数调用
  2. typeof符号为Symbol
const syb = Symbol();
console.log( typeof syb ); // symbol
  1. 每次调用Symbol得到的符号永远不相等, 无论符号名是否相同
const syb = Symbol('abc');
const syb2 = Symbol('cbd');
console.log(syb === syb2); // false
  1. 符号可以作为对象的属性名存在, 这种属性称之为符号属性

    • 由于第三点和这一点, 开发者可以通过精妙的设计, 让符号属性无法通过常规操作被外界访问到

      const obj = (function () {
      
          const valueSymbol = Symbol('valueSymbol');
      
          return {
              [valueSymbol]: '我是永远无法被更改的',
              name: 'loki'
          }
      }())
      
      console.log(obj)
      obj.name = 'thor';
      console.log(obj.name); // thor
      console.log(obj[Symbol('valueSymbol')]); // undefined
      

      如上, 我们的Symbol(valueSymbol)属性别人虽然可以看到但是永远无法访问和修改, 比我们自己写_注明安全多了吧

      扫描二维码关注公众号,回复: 11484082 查看本文章
    • 符号属性是不可枚举的, 因此在for…in中无法读取到符号属性, Object.keys也无法读取到符号属性

      const obj = {
          name: 'loki',
          [Symbol('helloWorld')]: 'helloWorld'
      }
      
      for(const item in obj) {
          console.log(item); // 只会输出name
      }
      
      const keys = Object.keys( obj );
      
      console.log(keys); // ["name"]
      
      
    • Object.getOwnPropertyNames尽管可以得到无法枚举的属性, 但是仍然无法读取到Symbol属性

      const obj = {
          name: 'loki',
          [Symbol('helloWorld')]: 'helloWorld'
      }
      
      const keys = Object.getOwnPropertyNames( obj );
      console.log(keys); // ["name"]
      
    • ES6新增Object.getOwnPropertySymbols方法, 用于获取符号列表, 同时可以进行操作

      const obj = {
          name: 'loki',
          [Symbol('helloWorld')]: 'helloWorld'
      }
      
      const symbols = Object.getOwnPropertySymbols( obj );
      
      console.log(symbols); // [Symbol(helloWorld)]
      
      console.log(obj[ symbols[0] ]); // helloWorld
      
      obj[ symbols[0] ] = 123;
      
      console.log(obj[ symbols[0] ]); // 123
      
      
  2. 符号无法进行隐式类型转换, 因此无法用于数学运算, 字符串拼接或者其他隐式类型转换的场景, 但是符号可通过String构造函数进行显示类型转换, console.log函数能够输出符号也是因为在log函数中进行了显示转换

const symb = Symbol('hello');
const str = '123' + symb; // 这一句直接报错  Cannot convert a Symbol value to a string

共享符号

啥是共享符号, 共享符号就是说, 在某些情况下, 你想让两个对象或者两个地方用同一个符号, 我们用上面普通符号的知识往往会这样做

const symb = Symbol('needRepeatSymbol');

const obj1 = {
    [symb]: 'helloWorld'
}

const obj2 = {
    [symb]: 'helloWorld2'
}

const symbs1 = Object.getOwnPropertySymbols(obj1); 
const symbs2 = Object.getOwnPropertySymbols(obj2);

console.log(symbs1[0] === symbs2[0]); // true

上面那么写其实略显麻烦, 而且有一些问题( 比如如果这两哥们在不同的组件和作用域下, 那你还怎么给人家共享一个Symbol

所以ES6还推出了一个创建共享符号的api: Symbol.for

  • Symbol.for规定只要你的符号长得一样, 那无论你创建多少次都是同一个, 在不同的作用域都适用
const symb = Symbol.for('hello');
const symb2 = Symbol.for('hello');

const symb3 = Symbol('hello');
const symb4 = Symbol('hello');

console.log(symb === symb2); // true
console.log(symb3 === symb4); // false
console.log(symb === symb3); // false

知名( 具名, 公共 )符号

知名符号是一些具有特殊含义的符号, 通过Symbol的静态属性可以拿到, 就是系统已经给你内置好了, 不是你自己写的, 包括系统很多的内置方法都有或多或少的用到这些知名符号

  1. Symbol.hasInstance

该符用于定义构造函数的静态成员, 它将影响instanceof的判定

const arr = [1 ,2, 3];

console.log(arr instanceof Array); // true, 这是我们的常规写法

// 同时arr instanceof Array也等效于如下写法
console.log(Array[Symbol.hasInstance](obj)); // true

上面说到它将影响instanceof的判定, 因为他是insetanceof的底层实现, 那我们可以来试一试

Object.defineProperty(Array, Symbol.hasInstance, {
    value: function( obj ) {
        return false;
    }
})

const arr = [1, 2, 3];
console.log( arr instanceof Array ); // false

经过我们这么一折腾, 数组的instanceof已经出现了事与愿违的效果了, 所以这些知名符号其实就是ES6暴露给我们开发者, 让我们在某些情况下是可以直接参与到JS的内部实现的, 否则比如instanceof你看到他这语法都能给你整懵圈, 这是咋实现的啊? a instanceof b, 现在你就知道了吧

  1. 【 扩展 】Symbol.isConcatSpreadable

该符号会影响数组的concat

const arr = [1, 2, 3];

const arr3 = arr.concat( 10, [4, 5, 5] );

console.log(arr3); // [1, 2, 3, 10, 4, 5, 5]

// 上面我们会发现concat方法在连接数组的时候会将数组展开, 如果我们不想让他展开
// 就操作Symbol.isConcatSpreadable

const newArr = [4, 5, 5];
newArr[Symbol.isConcatSpreadable] = false; // 设置为true就会展开, false就不会
const newArr2 = arr.concat(10, newArr);

console.log(newArr2); // [1, 2, 3, 10, [4, 5, 5]]
  1. 【 扩展 】Symbol.toPrimitive

该符号会影响类型转换的结果

const obj = {
    a: 1,
    b: 123
}

const resp = obj + 123;
console.log(resp); // [object Object]123

// 上面的结果是因为发生了隐式类型转换, 过去我们是无法参与到这种JS的内部操作的, 但现在可以

obj[Symbol.toPrimitive] = function() {
    return 10;
}

const newResp = obj + 123;
console.log(newResp); // 133还是number类型的
  1. 其他知名符号就不一一书写了, 可以直接去MDN的Symbol中可以查看

猜你喜欢

转载自blog.csdn.net/weixin_44238796/article/details/107740063