概述
符号是ES6新增的一个数据类型,它通过使用函数 Symbol(符号描述)
来创建。
// 创建一个符号
const syb1 = Symbol();
const syb2 = Symbol('这是一个符号');
console.log(syb1,syb2);// Symbol() Symbol(这是一个符号)
符号设计的初衷,是为了给对象设置私有属性。(私有属性:只能在这个歌对象内部的方法里用,别的方法用不了。)
举例说明:
// 创建一个hero对象
const hero = {
attack: 30, // 攻击力
hp: 300, // 血量
defence: 10, // 防御值
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;
}
}
注意:getRandom方法只是一个工具方法,无需暴露给外部。所以,以前的做法是:
给这些方法命名时前面加符号,比如$getRandom、@getRandom、 _getRandom
不暴露给外部的方法用如下写法 :
const hero = {
attack: 30, // 攻击力
hp: 300, // 血量
defence: 10, // 防御值
gongji() { // 攻击方法:产生伤害
// 伤害:攻击力 * 随机数(0.8 ~ 1.1)
const dmg = this.attack * this.getRandom(0.8, 1.1);
console.log(dmg);
function getRandom(min, max){ // 根据最小值和最大值产生一个随机数
return Math.random() * (max - min) + min;
}
},
}
缺点:
- 每一次调用gongji方法都会创建getRandom方法,没有必要一直创建,浪费内存空间,只创建一次就好了;
- 如果其他方法也要调用getRandom方法,会有点麻烦。
总结:造成以上缺点的原因就是因为缺少了私有属性。
私有属性:只能在对象内部使用,外部无法使用。
ES6类的写法也有同样的问题:
class Hero {
constructor(attack, hp, defence) {
this.attack = attack;
this.hp = hp;
this.defence = defence;
}
gongji(){
const dmg = this.attack * this.getRandom(0.8, 1.1);
console.log(dmg);
}
getRandom(min, max) { // 根据最小值和最大值产生一个随机数
return Math.random() * (max - min) + min;
}
}
特点
没有字面量
只能通过Symbol()创建使用typeof得到的类型是 symbol
每次调用Symbol函数得到的符号永远不相等,无论符号名是否相同
const syb1 = Symbol('abc');
const syb2 = Symbol('abc');
console.log(syb1 === syb2); // false
- 符号可以作为对象的属性名存在,这种属性称之为符号属性
对象的属性肯定是字符串,现在多了一个符号属性。
const syb1 = Symbol('这是用于对象的一个属性');
const obj = {
a: 1,
[syb1]: 3 // 符号属性
}
console.log(obj); // {a: 1, Symbol(这是用于对象的一个属性): 3}
- 基于以上特点,开发者可以通过精心的设计,让这些属性无法通过常规方式被外界访问
如何精心设计?
用符号把属性变成私有成员。因为符号是唯一的
const hero = (function () {
const getRandom = Symbol("用于产生随机数的符号");
return {
attack: 30, // 攻击力
hp: 300, // 血量
defence: 10, // 防御值
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;
}
}
}())
hero.gongji();
//hero.getRandom(); // 报错 hero.getRandom is not a function
//hero[getRandom](3,5); // 报错 getRandom is not defined
ES6类版:
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(20, 200, 10);
- 符号属性是不能枚举的,因此在for-in循环中无法读取到符号属性,Object.keys方法也无法读取到符号属性;
- getOwnPropertyNames尽管可以得到所有无法枚举的属性,但是仍然无法读取到符号属性
- ES6新增Object.getOwnPropertySymbols方法,可以读取符号
const syb = Symbol();
const obj = {
[syb]: 1,
a: 2,
b: 3
}
for (const prop in obj) {
console.log(prop); // a b
}
console.log(Object.keys(obj)); // ['a','b']
console.log(Object.getOwnPropertyNames(obj)); // ['a','b']
// 得到的是一个符号属性的数组
console.log(Object.getOwnPropertySymbols(obj)); // [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);
//console.log(sybs); // [Symbol(用于产生随机数的符号)]
const prop = sybs[0];
console.log(h[prop](3, 5)); // 3.447697709789846
- 符号无法被隐式转换,因此不能被用于数学运算、字符串拼接或其他隐式转换的场景,但符号可以显示的转换为字符串,通过 String 构造函数进行转换即可,console.log 之所以可以输出符号,是它在内部进行了显示转换
// 有时为了做一些日志记录,看一下哪些符号出了问题
const syb = Symbol();
const str = String(syb);
console.log(str); // Symbol()