最近在一些 Demo 中经常会看到 Symbol()
这个东西,例如:
var race = {
protoss: Symbol(),
terran: Symbol(),
zerg: Symbol()
}
就只能记得大概了,就是赋值一个独特的值,但是这个值具体是什么我想不起来了。
所以还是记个笔记加深下印象,省的看了又忘。
ES6 引入了一种新的原始数据类型
Symbol
,表示独一无二的值。它是 JavaScript 语言的第七种数据类型
如果只看 阮一峰的 ECMAScript 6入门 和 MDN 上的说明,可能会有点迷糊,但是大概知道只要是 Symbol类型 是都是独一无二的,保证不会与其他属性名产生冲突。
例如:
var s1 = Symbol(1)
// Symbol(1)
var s2 = Symbol(1)
// Symbol(1)
s1 === s2
// false
s1 == s2
// false
所以,就出现了文章一开始的那个例子,出自方应杭老师的的每日一题(在文章最后有附链接)
大概的需求场景,就是需要通过传入不同的 String
来执行不同的操作,接下来我就大概引用该文章内的例子来说明
例如:在玩SC2的时候,我们需要在开始时选择种族,选择完成之后在游戏加载时就会执行对应的创建角色操作。
var race = {
protoss: 'protoss', // 神族
terran: 'terran', // 人族
zerg: 'zerg' // 虫族
}
function createRole(type){
if(type === race.protoss){ 创建神族角色 }
else if(type === race.terran){ 创建人族角色 }
else if(type === race.zerg){ 创建虫族角色 }
}
用户选择种族后,就需要调用 createRole 来创建角色:
// 传入字符串
createRole('zerg')
// 或者传入变量
createRole(race.zerg)
但是在传入 type 值的时候,如果直接用字符串,就不是一个好的方式(不利于将来的修改和维护),所以需要传入例如 race.zerg
这样的变量属性。
那么如果这样操作的话,其实这些对象属性(race.protoss
,race.terran
,race.zerg
)对应的值并不重要,因为传入的肯定是同一个值。
所以以下声明和前一个例子中声明可以完成同一个作用。
var race = {
protoss: 'askdjaslkfjas;lfkjas;flkj', // 神族
terran: ';lkfalksjfl;askjfsfal;skfj', // 人族
zerg: 'qwieqwoirqwoiruoiwqoisrqwroiu' // 虫族
}
也就是说, race.zerg
的值是多少并不重要,只要它的值跟 race.protoss
和 race.terran
的值不一样就行,Symbol
的用途就是如此。
凡是数据类型属于
Symbol
类型,就都是独一无二的
所以可以用以下代码来声明,并且保证不会出现冲突的属性值:
var race = {
protoss: Symbol(),
terran: Symbol(),
zerg: Symbol()
}
// 抑或填入描述
var race = {
protoss: Symbol('protoss'),
terran: Symbol('terran'),
zerg: Symbol('zerg')
}
至此我觉得 Symbol
数据类型已经解释清楚了,如果还是不明白,请重新回到头部阅读一遍。
Symbol
拥有的一些特性
一、Symbol
作为属性名进行遍历时:
- Symbol 作为属性名,遍历对象的时候,该属性不会出现在
for...in
、for...of
循环中,也不会被Object.keys()
、Object.getOwnPropertyNames()
、JSON.stringify()
返回。- Symbol 也不是私有属性。
可以通过 Object.getOwnPropertySymbols()
方法来获取指定对象的所有 Symbol 属性名(方法返回一个数组,成员是当前对象的所有用作属性名的 Symbol 值)
const obj = {};
let a = Symbol('a');
let b = Symbol('b');
obj[a] = 'Hello';
obj[b] = 'World';
Object.getOwnPropertySymbols(obj);
// [Symbol(a), Symbol(b)]
当然也可以使用 ES6 的新 API Reflect.ownKeys()
来获取所有类型的键名,包括 常规键名 和 Symbol
键名。
二、Symbol.for()
方法
Symbol.for()
方法可以接受一个字符串作为参数,然后搜索有没有以该参数作为名称的Symbol
值。
如果有,就返回这个Symbol
值,否则就新建一个以该字符串为名称的Symbol
值,并将其注册到全局。
let s1 = Symbol.for('foo');
let s2 = Symbol.for('foo');
s1 === s2
// true
Symbol.for("bar") === Symbol.for("bar")
// true
Symbol.for()
与 Symbol()
都会生成新的 Symbol
。它们的区别是,前者会被登记在全局环境中供搜索,后者不会。
三、Symbol.keyFor()
方法
Symbol.keyFor()
方法返回一个已登记的Symbol
类型值的key
。
let s1 = Symbol.for("foo");
Symbol.keyFor(s1) // "foo"
let s2 = Symbol("foo");
Symbol.keyFor(s2) // undefined
四、内置的 Symbol 属性
除了定义自己使用的 Symbol
值以外,ES6 还提供了 11 个内置的 Symbol 值,指向语言内部使用的方法。
Symbol.hasInstance
Symbol.isConcatSpreadable
Symbol.species
Symbol.match
Symbol.replace
Symbol.search
Symbol.split
Symbol.iterator
Symbol.toPrimitive
Symbol.toStringTag
Symbol.unscopables
这11个属性具体的说明看阮一峰老师的 ECMAScript 6入门 就可以了,里边有单独的小Demo可以阅读,我就不单独说明了,因为如果全部说明的话,内容就太多了,并且和书的内容高度重复了。
附
「每日一题」JS 中的 Symbol 是什么? | 方应杭
Symbol - ECMAScript 6入门 | 阮一峰
Symbol - JavaScript | MDN
Symbol.prototype.description - JavaScript | MDN
Reflect - ECMAScript 6入门 | 阮一峰
Reflect.ownKeys() - JavaScript | MDN