四、ES6+详情篇—Symbol


1、普通符号

符号是ES6新增的一个数据类型,它通过使用函数Symbol(符号描述)来创建

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

私有属性:只能在对象内部使用,外面无法使用

符号具有以下特点:

  • 没有字面量
  • 使用 typeof 得到的类型是 symbol
  • 每次调用 Symbol 函数得到的符号永远不相等,无论符号名是否相同
  • 符号可以作为对象的属性名存在,这种属性称之为符号属性(这句话挺有意思的)
    • 开发者可以通过精心的设计,让这些属性无法通过常规方式被外界访问
    • 符号属性是不能枚举的,因此在 for-in 循环中无法读取到符号属性,Object.keys 方法也无法读取到符号属性
    • Object.getOwnPropertyNames 尽管可以得到所有无法枚举的属性,但是仍然无法读取到符号属性
    • ES6 新增 Object.getOwnPropertySymbols 方法,可以读取符号
  • 符号无法被隐式转换,因此不能被用于数学运算、字符串拼接或其他隐式转换的场景,但符号可以显式的转换为字符串,通过 String 构造函数进行转换即可,console.log 之所以可以输出符号,是它在内部进行了显式转换

基本用法

// 第一部分
const syb1 = Symbol();
const syb2 = Symbol("abc");

console.log(syb1, syb2);


// 第二部分: 为什么要有Symbol,其实就是为了私有属性,不想被人用getRandom这个方法应该怎么办
class Hero {
  constructor(attack, hp, defence) {
    this.attack = attack;
    this.hp = hp;
    this.defence = defence;
  }

  gongji() {
    //伤害:攻击力*随机数(0.8~1.1)
    const dmg = this.attack * this.getRandom(0.8, 1.1);
    console.log(dmg);
  }

  getRandom(min, max) { //根据最小值和最大值产生一个随机数
    return Math.random() * (max - min) + min;
  }
}

// 第三部分:类型就是synbol,并且每个都是独有的,绝不相等
const syb1 = Symbol();
const syb2 = Symbol("abc");

console.log(syb1, syb2);

console.log(typeof syb1 === "symbol", typeof syb2 === "symbol")

const syb1 = Symbol("这是随便写的一个符号");
const syb2 = Symbol("这是随便写的一个符号");

console.log(syb1, syb2);
console.log(syb1 === syb2)

// 第四部分:符号作为属性的用法
const syb1 = Symbol("这是用于对象的一个属性");

const obj = {
  a: 1,
  b: 2,
  [syb1]: 3  //符号属性
}

console.log(obj);

// 第四部分:对第二部分优化,体现一下私有属性的作用
const Hero = (() => {
  const getRandom = Symbol();

  return class {
    constructor(attack, hp, defence) {
      this.attack = attack;
      this.hp = hp;
      this.defence = defence;
    }

    gongji() {
      //伤害:攻击力*随机数(0.8~1.1)
      const dmg = this.attack * this[getRandom](0.8, 1.1);
      console.log(dmg);
    }

    [getRandom](min, max) { //根据最小值和最大值产生一个随机数
      return Math.random() * (max - min) + min;
    }
  }
})();

const h = new Hero(3, 6, 3);
// 打印我们发现getRandom方法是不可访问的,这就是私有属性
// 注意打印结果是看的见的,但是我们用不了,已使用就会报错
console.log(h);


// 第五部分:symbol属性的可访问性
const syb = Symbol();

const obj = {
  [syb]: 1,
  a: 2,
  b: 3
}

// symbol遍历不到,不可枚举
for (const prop in obj) {
  console.log(prop)
}

console.log(Object.keys(obj))// 也拿不到
console.log(Object.getOwnPropertyNames(obj))// 可以拿到无法枚举的值,但是仍然拿不到符号属性

//下面得到的是一个符号属性的数组
const sybs = Object.getOwnPropertySymbols(obj);
console.log(sybs, sybs[0] === syb)


// 第六部分:强行访问symbol属性并使用
const Hero = (() => {
  const getRandom = Symbol();

  return class {
    constructor(attack, hp, defence) {
      this.attack = attack;
      this.hp = hp;
      this.defence = defence;
    }

    gongji() {
      //伤害:攻击力*随机数(0.8~1.1)
      const dmg = this.attack * this[getRandom](0.8, 1.1);
      console.log(dmg);
    }

    [getRandom](min, max) { //根据最小值和最大值产生一个随机数
      return Math.random() * (max - min) + min;
    }
  }
})();

const h = new Hero(3, 6, 3);
const sybs = Object.getOwnPropertySymbols(Hero.prototype);
const prop = sybs[0];
console.log(h[prop](3, 5))

2、共享符号

根据某个符号名称(符号描述)能够得到同一个符号

// 第一部分:什么是共享符号
Symbol.for("符号名/符号描述")  //获取共享符号

const syb1 = Symbol.for("abc");
const syb2 = Symbol.for("abc");
// 相等,奇不奇怪,符号又可以相等!!!!!
console.log(syb1 === syb2)
const obj1 = {
    a: 1,
    b: 2,
    [syb1]: 3
}

const obj2 = {
    a: "a",
    b: "b",
    [syb2]: "c"
}

console.log(obj1, obj2);


const obj = {
    a: 1,
    b: 2,
    [Symbol.for("c")]: 3
}
// 可以访问!!!!!!
console.log(obj[Symbol.for("c")]);

// 第二部分:实现共享符号
const SymbolFor = (() => {
  const global = {};//用于记录有哪些共享符号
  return function (name) {
    console.log(global)
    if (!global[name]) {
      global[name] = Symbol(name);
    }
    console.log(global);
    return global[name];
  }
})();

const syb1 = SymbolFor("abc");

const syb2 = SymbolFor("abc");

console.log(syb1 === syb2);

3、知名符号(了解)

知名符号是一些具有特殊含义的共享符号,通过 Symbol 的静态属性得到

ES6 延续了 ES5 的思想:减少魔法,暴露内部实现!

因此,ES6 用知名符号暴露了某些场景的内部实现

  1. Symbol.hasInstance

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

obj instanceof A

//等效于

A[Symbol.hasInstance](obj) // Function.prototype[Symbol.hasInstance]
  1. [扩展] Symbol.isConcatSpreadable

该知名符号会影响数组的 concat 方法

  1. [扩展] Symbol.toPrimitive

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

  1. [扩展] Symbol.toStringTag

该知名符号会影响 Object.prototype.toString 的返回值


文章作者: 吴俊杰
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 吴俊杰 !
 上一篇
五、ES6+详情篇—Set和Map 五、ES6+详情篇—Set和Map
一直以来,JS只能使用数组和对象来保存多个数据,缺乏像其他语言那样拥有丰富的集合类型。因此新增了两种集合类型set和map......
2024-12-03
下一篇 
三、ES6+详情篇—Reflect 三、ES6+详情篇—Reflect
第一次看到反射的时候真的不知道它到底有什么用,觉得它是在做重复的事情。慢慢的我发现其实我不懂它的底层,不懂它的设计思想......
2024-12-03
  目录