「这是我参与2022首次更文挑战的第3天,活动详情查看:2022首次更文挑战」。
大家好,我是L同学,这是我的ES6总结的第三篇文章。本文主要总结了Symbol的基本使用。
Symbol是什么
Symbol是ES6新增的一种数据类型,翻译为符号。为什么需要它呢😕?
在ES6之前,对象的属性名都是字符串,很容易造成属性名的冲突。比如原来有一个对象,我们不确定它内部有什么属性但又希望给它添加一个新的属性和值时,那么会很容易造成冲突,导致覆盖了内部原有的属性。
我们可以看到对象obj的name属性值被覆盖了,变成了lala👇。
var obj = {
name: 'haha',
friend: {name: 'xixi'},
age: 18
}
obj.name = 'lala'
console.log(obj); // { name: 'lala', friend: { name: 'xixi' }, age: 18 }
那么使用Symbol,它会生成一个独一无二的值。即使多次创建值,它们也是不同的。Symbol是一种全新的原始数据类型,表示独一无二的值。
Symbol的基本使用
Symbol值是通过Symbol函数来创建的。我们可以看出每次创建出来的Symbol值不相等。
const s1 = Symbol()
const s2 = Symbol()
console.log(s1 === s2); // false
我们可以在创建Symbol值时传入一个description,这是ES2019(ES10)新增的特性。这样的话,对于我们多次使用Symbol的情况,我们你就可以在控制台中区分出来到底是哪个对应的Symbol。
console.log(Symbol('foo'))
console.log(Symbol('bar'))
console.log(Symbol('baz'))
const s3 = Symbol('aaa')
console.log(s3.description); // aaa
Symbol值具有唯一性。不管传入的描述符是不是相同的,每次调用Symbol函数得到的结果都是一个全新的值。
console.log(Symbol('foo') === Symbol('foo')); // false
在ES6中,对象的属性名除了字符串,我们还可以使用Symbol值。
通常我们在对象中会使用Symbol来表示唯一的属性名。Symbol的值都是独一无二的,不用去担心可能会产生冲突。
写法一:对象属性名赋值
const s1 = Symbol()
const s2 = Symbol()
const obj = {}
obj[s1] = 'aaa'
obj[s2] = 'bbb'
console.log(obj);
写法二: Object.defineProperty
const s4 = Symbol()
Object.defineProperty(obj, s4, {
enumerable: true,
configurable: true,
writable: true,
value: 'ccc'
})
写法三: 定义字面量时直接使用
const obj1 = {
[s1]: 'qqq',
[s2]: 'www'
}
obj1[s4] = 'eee'
console.log(obj1[s1], obj1[s2]);
使用Symbol值作为对象的属性名,我们无法通过Object.keys(obj)或者Object.getOwnPropertyNames(obj)来获取Symbol值。
console.log(Object.keys(obj)); // []
console.log(Object.getOwnPropertyNames(obj)); // []
使用for...in,Object.keys都不拿不到Symbol类型的属性名,JSON.stringify去序列化我们的对象为json字符串的话,Symbol属性也会被忽略。
const obj = {
[Symbol()]: 'symbol value',
foo: 'normal value'
}
for(var key in obj) {
console.log(key); // foo
}
console.log(Object.keys(obj)); // ['foo]
console.log(JSON.stringify(obj)); // {"foo":"normal value"}
那么我们该如何获取Symbol值呢?我们可以通过Object.getOwnPropertySymbols(obj)
来获取所有Symbol的key。
const skeys = Object.getOwnPropertySymbols(obj)
for(const skey of skeys) {
console.log(obj[skey]);
}
我们知道通过Symbol()每次创建出来的Symbol值是不一样的,那么有些情况我们又希望创建出来的Symbol值是一样的,那么我们可以通过Symbol.for(key)来创建相同的Symbol值。Symbol.for方法接收一个字符串作为参数,相同的字符串就一定会返回相同Symbol类型的值。这个方法内部它维护了一个全局注册表,为我们的字符串和symbol值提供了一一对应的关系。
const sx = Symbol.for('xxx')
const sy = Symbol.for('xxx')
console.log(sx === sy); // true
如果我们传入的不是字符串,那这个方法内部会自动把它转换为字符串,这样就会导致我们传入的布尔值true和传入字符串的true拿到的结果都是一样的。
console.log(Symbol.for(true) === Symbol.for('true')); // true
通过Symbol.keyFor(Symbol)获取Symbol的key。
const key = Symbol.keyFor(sx)
console.log(key); // xxx
const sz = Symbol.for(key)
console.log(sx === sz); // true
内置Symbol常量
在Symbol类型当中还提供了内置的Symbol常量,用来作为内部方法的标识,这些标识可以让自定义对象去实现一些js内置的接口。
console.log(Symbol.iterator);
console.log(Symbol.hasInstance);
我们可以使用对象的toString来获取对象的toString标签。
const obj = {}
console.log(obj.toString());
这个对象的toString结果默认就是[object Object],这样的字符串就叫做对象的toString标签。
如果我们想要自定义对象的toString标签,就可以在这个对象当中添加特定的成员来标识。考虑到如果使用字符串去添加这种标识符,就有可能跟内部的成员产生重复。所以ECMAScript就要求我们用Symbol值去实现这样一个接口。
const obj = {
[Symbol.toStringTag]: 'XObject'
}
console.log(obj.toString());
我们可以看到obj对象的toString标签就成了XObject。
这里的toStringTag就是内置的Symbol常量,这种Symbol我们在为对象去实现迭代器时会经常用到。
往期文章 👇👇👇