关于Symbol的理解

242 阅读3分钟

关于 Symbol 的理解

基础语法

  1. Symbol代表唯一值

  2. Symbol 函数不能被 new

- symbol()不是 Symbol 函数的一个实例
  1. Symbol.prototype 与 Object(Symbol())
- 把 Symbol 变成对象实例
- Symbol.prototype 原型上有 toString,valueOf,Symbol(Symbol.toPrimitive),Symbol(Symbol.toStringTag)方法

4. Symbol 不能与其他类型的值计算

- 数学计算:不能转换为数字

- 字符串拼接:隐式转换不可以,但是可以显示转换

- 模板字符串也不能转换

5. Symbol 属性不参与 for in(of)/Object.keys/Object.getOwnPropertyNames/JSON.stringify 遍历

+ 如果要遍历对象的 Symbol 属性,需要调用 Object.getOwnPropertySymbols()方法
let obj = {
  name: "珠峰培训",
  age: 11,
  [Symbol("A")]: 100,
  [Symbol("B")]: 200,
};
let [m, n] = Object.values(obj);
console.log(m, n);//"珠峰培训",11
console.log(Object.keys(obj));//{name,age}

------------------------------------------------------------------------------------------
let obj = {
          name: "珠峰培训",
          age: 11,
          [Symbol('A')]: 100,
          [Symbol('B')]: 200
      };
let [m, n] = Object.getOwnPropertySymbols(obj);
console.log(m, n)//{[Symbol('A')],[Symbol('B')]}

内置的 Symbol 值

ES6 提供很多内置的 Symbol 值,指向语言内部使用的方法

  • Symbol.hasInstance:对象的 Symbol.hasInstance 属性,指向一个内部方法,当其他对象使用 instanceof 运算符,判断是否为该对象的实例时,会调用这个方法

    本质是 Function 的原型上有 Symbol.hasInstance 这个方法,这样所有的函数都可以调用这个方法,从而检测是否为当前实例

    class person {
      constructor() {}
    }
    let p1 = new person();
    console.log(p1 instanceof person);//true
    console.log(person[Symbol.hasInstance](p1));//true
    console.log(Object[Symbol.hasInstance]({}));//true
    
  • Symbol.isConcatSpreadable:值为布尔值,表示该对象用于 Array.prototype.concat()时,是否可以展开

  • Symbol.iterator:拥有此属性的对象被誉为可被迭代的对象,可以使用 for…of 循环

    • 所有可被迭代的对象才能用 for of 循环。可被迭代对象:拥有 Symbol.iterator 这个属性的

    常见的迭代对象:map,set,array,类数组,string(他们的原型上都有 Symbol.iterator 属性)

问题:如何让普通对象可以被迭代

```javascript
let obj = {
  0: 1,
  1: 2,
  length: 2,
  //在obj中加一个Symbol.iterator,该对象就可以被迭代
  [Symbol.iterator]: Array.prototype[Symbol.iterator],
};
for (let item of obj) {
  console.log(item);
}
```
  • Symbol.toPrimitive: 该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值

    - 对象数据类型进行转换:
     
     1. 调用 obj[Symbol.toPrimitive](hint),前提是存在
     
     2. 否则,如果 hint 是 "string" —— 尝试 obj.toString() 和 obj.valueOf()
     
     3. 否则,如果 hint 是 "number" 或 "default" —— 尝试 obj.valueOf() 和 obj.toString()
    
    
    	
    

面试题:实现以下效果

 if (a == 1 && a == 2 && a == 3) {
     console.log('OK');
  }
  ------------------以下为实现效果-------------------------
  let a={
        i:1,
        [Symbol.toPrimitive](hint){
         switch(hint){
            case "number":
                return ++this.i;
            case "string":
                return String(this.i);
            case "default":
                return ++this.i;;
            }
          }
        };

  if (a == 1 && a == 2 && a == 3) {
        console.log('OK');}
--------------------其他方案-----------------------------
//方案一:数据类型转换
      let a = {
          i: 0,
          //处理三
          [Symbol.toPrimitive]() {
              return ++this.i;
          },
      };
      //处理一
      a.valueOf = function() {
          // this -> a
          return ++this.i;
      };
      //处理二
      a.toString = function() {
          // this -> a
          return ++this.i;
      };

      //处理四
      let a = [1, 2, 3];
      a.toString = a.shift;

//方案二:数据劫持
      //  + 在全局上下文中基于var / function声明变量,相当于给window设置对应的属性 -> window.a
      //   + Object.defineProperty劫持对象中某个属性的获取和设置等操作
      let i = 0;
      Object.defineProperty(window, "a", {
          get() {
              return ++i;
          },
      });
      if (a == 1 && a == 2 && a == 3) {
          console.log("OK");
      }
  • Symbol.toStringTag:在该对象上面调用 Object.prototype.toString 方法时,如果这个属性存在,它的返回值会出现在 toString 方法返回的字符串之中,表示对象的类型

      class Person {
            get[Symbol.toStringTag]() {
                      return "Person";
             }}
    
      let p1 = new Person;
      console.log(({}).toString.call(p1)); //"[object Person]"
    

【面试题】:关于 symbol 的理解

1.symbol("a")可以创建唯一值

let n = Symbol(),
  m = Symbol();
console.log(n === m); //false

n = Symbol("A");
m = Symbol("A");
console.log(n === m); //false

2.symbol 的应用场景:

  • 对象的唯一属性:防止同名属性,及被改写或覆盖
let n = Symbol("N");
let obj = {
  name: "珠峰培训",
  age: 11,
  [n]: 100,
};
  • 宏管理(消除魔术字符串):指代码中多次出现,强耦合的字符串或数值,应避免,而使用含义清晰的变量代替(在 redux 和 vuex 必须用到)
const vote_plus = Symbol("vote_plus");
function reducer(action) {
  let state = {
    count: 0,
  };
  switch (action.type) {
    case vote_plus:
      state.count++;
      break;
  }
  return state;
}
reducer({
  type: vote_plus,
});
  • 很多 JS 的内置原理都是基于这些 Symbol 的属性来处理的

    • Symbol.toPrimitive

    • Symbol.hasInstance

    • Symbol.toStringTag

    • Symbol.iterator

    • ...