7.符号

80 阅读4分钟

7-1. 普通符号

符号是ES6新增的一个数据类型,它通过使用函数 Symbol(符号描述) 来创建 符号描述的作用,其实就是就符号起个名字,你想起就起,不想起就不起,比较随便。

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

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

符号具有以下特点:

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

1.符号最基本的使用

const syb1 = Symbol();
const syb2 = Symbol("abc");

console.log(syb1, syb2);

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

用 typeof syb1 都是 “symbol”

2.同样的符号名创建的符号也不相等

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

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

上面的结果是flase

3.符号作为属性的使用

//创建一个符号

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);
console.log(h);

4.对象的符号的获取

const syb = Symbol();

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

for (const prop in obj) {
    console.log(prop)
}

console.log(Object.keys(obj))
console.log(Object.getOwnPropertyNames(obj))
//上面两个都无法获取符号

//得到的是一个符号属性的数组,可以通过getOwnPropertySymbols来获取。
const sybs = Object.getOwnPropertySymbols(obj);
console.log(sybs, sybs[0] === syb)

7-2. 共享符号

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

Symbol.for("符号名/符号描述")  //获取共享符号

共享符号首先是为了共享的,如果使用共享符号,创建符号和获取符号都是通过Symbol.for("符号名/符号描述")来获取,描述一样,那么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);

7-3. 知名(公共、具名)符号

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

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

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

  1. Symbol.hasInstance

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


obj instanceof A

//等效于

A[Symbol.hasInstance](obj) // Function.prototype[Symbol.hasInstance]

知名符号让我可以参与到某些方法的内部实现,修改他内部方法实现。比方说下面这个例子

function A() {

}

//下面几行代码就修改了instanceof的方法实现
Object.defineProperty(A, Symbol.hasInstance, {
    value: function (obj) {
        return false;
    }
})

const obj = new A();

console.log(obj instanceof A);
  1. [扩展] Symbol.isConcatSpreadable

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

  1. [扩展] Symbol.toPrimitive

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

  1. [扩展] Symbol.toStringTag

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

  1. 其他知名符号