- 符号是 ES6 新增的一个数据类型,它通过使用函数 Symbol (符号描述) 来创建符;
- 符号设计的初衷,是为了给对象设置私有属性;
- 共享符号可以根据某个符号名称 (符号描述) 能够得到同一个符号,通过使用函数 Symbol.for(“符号名/符号描述”) 来创建;
Symbol 特点
没有字面量 (每个 Symbol 都是通过 Symbol 函数创建);
每次调用 Symbol 函数得到的符号永远不相等,无论符号名是否相同;
符号可以作为对象的属性名存在,这种属性称之为符号属性 (es6 之前对象属性只能是字符串或者数字);
符号无法被隐式转换,不能被用于数学运算、字符串拼接或其他隐式转换,但符号可以通过 String 构造函数显式的转换为字符串;
不能被 new 执行,但是有原型;
Symbol.iterator
该符号可以判断数据是否拥有迭代功能,它可以影响数据迭代器遍历功能;
只有对象具备 Symbol.iterator 才是可以被遍历的数据结构;
迭代器协议
迭代器协议定义了产生一系列值的一个标准方式,迭起协议规定需要返回一个带 next() 方法的对象,next() 可以被多次执行,每次执行都会返回一个对象,该对象包含 done 和 value 两个属性:
- done:是一个 boolean,在没有迭代完时返回 false,迭代完成后返回 true;
- value:就是被迭代的返回值,当 done 为 true 时可以省略;
实现了以上两点才会满足一个迭代器协议;
手动实现一个迭代器
const obj = {
store: ['foo', 'bar', 'baz'],
[Symbol.iterator]: function () {
let index = 0
return {
next: () => {
const result = {
value: this.store[index],
done: index++ >= this.store.length
}
return result
}
}
}
}
// 实现了 iterator 迭代器协议,则可以使用 for...of 遍历
for (const item of obj) {
console.log('循环体', item)
}
// 循环体 foo
// 循环体 bar
// 循环体 baz
Symbol.toStringTag
该知名符号会影响 Object.prototype.toString 的返回值;
class Person {}
const p = new Person();
console.log(Object.prototype.toString.apply(p)); // [object Object]
// Symbol.toStringTag 可以实现使用 toString 判断类型时更加精确的知道自身的构造函数
class Person {
constructor() {
// 通过 Symbol.toStringTag 改写 Object.prototype.toString.call 更准确
this[Symbol.toStringTag] = "Person"
}
}
const p = new Person();
console.log(Object.prototype.toString.apply(p)); // [object Person]
Symbol 应用
设置私有属性
// 实现类中私有属性使其外面不能访问到只能内部使用
const Hero = (() => {
const getRandom = Symbol();
return class {
constructor(a) {
this.a = a;
}
attack() {
const dmg = this.a * this[getRandom](0.8, 1.1);
console.log(dmg);
}
// 私有属性随机函数
[getRandom](min, max) {
return Math.random() * (max - min) + min;
}
}
})();
// 测试
const h = new Hero(3);
h.attack(); // 不报错
h[Symbol]() // 报错
// 也可以使用一些不寻常的手段获取使用(强烈不建议)
const sybs = Object.getOwnPropertySymbols(Hero.prototype);
console.log(h[sybs[0]](3)); // 不报错
唯一属性值
ES6+ Map、WeakMap
上一篇