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 用知名符号暴露了某些场景的内部实现
- Symbol.hasInstance
该符号用于定义构造函数的静态成员,它将影响 instanceof 的判定
obj instanceof A
//等效于
A[Symbol.hasInstance](obj) // Function.prototype[Symbol.hasInstance]
- [扩展] Symbol.isConcatSpreadable
该知名符号会影响数组的 concat 方法
- [扩展] Symbol.toPrimitive
该知名符号会影响类型转换的结果
- [扩展] Symbol.toStringTag
该知名符号会影响 Object.prototype.toString 的返回值