Symbol是ES6中新引入的一种基本数据类型,在此以前Javascript中已有几种基本数据类型:函数
不一样于其余基本类型的通俗易懂,Symbol
是什么和有什么用一直有些让人困惑。this
JavaScript标准中规定对象的key只能是 String
或 Symbol
类型,区别在于 String
类型的key能够重复而 Symbol
类型的key是惟一的。Symbol
的本质是表示一个惟一标识。每次建立一个Symbol,它所表明的值都不可能重复,该值的内部实现能够视为一段数字(相似:3423498431987719455..
)。因此理论上 Symbol 的存在只有一个意义:用于必须使用惟一值的场景。spa
建立 Number、String等基本类型的实例有两种方法:经过构造函数(或者叫工厂函数)和文字语法糖。好比:debug
// 构造函数 const num = Number(3); const str = String('hi'); // 语法糖 const num = 3; const str = 'hi';
显然使用语法糖更加简洁。可是 Symbol 只能经过构造函数 Symbol()
进行建立:code
const sym = Symbol();
或者,咱们能够传入一个字符串参数(descriptor)用于描述该Symbol:对象
const sym = Symbol('cat');
注意:传入的参数对 Symbol 值的产生并没有影响,由于就算每次传入的参数都同样,生成的Symbol值也是不等的。该参数的做用仅用于描述被建立的Symbol,以便debug时能够识别出Symbol的含义。 因此,下列等式结果为 false
:blog
Symbol('cat') === Symbol('cat') // false
Symbol.for(key)
和 Symbol()
相似,Symbol.for(key)
也能够建立一个Symbol,不同的是:建立的 Symbol 是全局的(在全局Symbol表中注册),而若是全局已经存在相同 key
的Symbol,则直接返回该Symbol。因此,下列等式结果为 true
:ip
Symbol.for('cat') === Symbol.for('cat') // true
其实 Symbol 自己很简单,可是如何把它用好、且用的恰到好处却令人困惑,由于在日常工做中并无多少非Symbol不用的场景。可是用对了Symbol会对你的代码质量有很多提高。来看下面几种案例:rem
使用Symbol做为Object的key,能够保证和其余key都不重复。所以,Symbol很是适合用于对对象的属性进行拓展。字符串
好比,当使用 String 做为对象的key时,一旦出现重复的key则后面的属性会覆盖前面的:
const persons = { 'bruce': 'wayne', 'bruce': 'banner' } console.log(persons.bruce); // 'wayne'
使用Symbol做为Key能够避免这种状况:
const bruce1 = Symbol('bruce'); const bruce2 = Symbol('bruce'); const persons = { [bruce1]: 'wayne', [bruce2]: 'banner' } console.log(persons[bruce1]); // 'wayne' console.log(persons[bruce2]); // 'banner'
JS不少内建的方法都是经过 Symbol 进行指定的,好比:Symobol.iterator
指定了一个iterable对象的迭代器方法;Symbol.replace
指定了对象字符串替换的方法,这类 Symbol 被称为 Well-know Symbols,表明了JS语言的内部行为。
因为Javascript并不自带枚举类型,一般状况下咱们会使用一个freezed的Object来模拟枚举类型,好比定义一个日期的枚举:
const DAYS = Object.freeze({ monday: 1, tuesday: 2, wednesday: 3 });
此时有一个方法,接收 DAYS
的枚举值来返回当天要作的事:
function getTodo(day) { switch (day) { case DAYS.monday: return "看电影"; case DAYS.tuesday: return "购物"; case DAYS.wednesday: return "健身"; default: return "日期错误"; } }
咱们但愿代码逻辑足够严谨,传入的参数严格按照 DAYS.monday
的形式,不然就返回日期错误,可是该枚举类型的实现却作不到。好比:getTodo(1)
依然能获得 “看电影” 这个结果。
可是使用Symbol却能够解决这一问题,DAYS
枚举类型能够从新定义为:
const DAYS = Object.freeze({ monday: Symbol('monday'), tuesday: Symbol('tuesday'), wednesday: Symbol('wednesday') });
此时 getTodo
方法必须接收 DAYS.monday
这样的枚举值做为参数,不然就返回 “日期错误”,由于世界上再没有任何一个值和 DAYS.monday
相等了。
这样定义枚举显然更严谨了。
Key为Symbol类型的属性是不能被枚举的,这是 Symbol 除了惟一性外的第二大特性,所以使用for...in
,Object.keys()
、Object.hasOwnProperty()
等方法不能识别Symbol属性,简而言之Symbol属性对用户是“隐藏”的(但并非private的,由于有其余途径能够获取Symbol属性),例如:
所以Symbol做为“隐藏”属性能够用来存储对象的元数据。好比,有一个 TodoList
:
class TodoList { constructor() { // todo数量 this.count = 0; } // 增长todo add(id, content) { this[id] = content; this.count++; } } const list = new TodoList();
咱们使用 add()
方法向其中增长几个todo:
list.add('a', '看电影'); list.add('b', '购物'); list.add('c', '健身');
当咱们想使用 for...in
查看里面全部的todo时,会把 count
属性也带出来:
为了隐藏count属性,更方便的对todo进行操做,咱们可使用Symbol来存储它,TodoList
类修改成:
const count = Symbol('count'); class TodoList { constructor() { this[count] = 0; } add(id, content) { this[id] = content; this[count]++; } }
当咱们再遍历 TodoList
的时候,count就隐藏了:
当咱们想获取存储在Symbol中的原数据时,可使用 Object.getOwnPropertySymbols()
方法:
以上是我能想到的 Symbol 的用途,若是你们有其余心得体会欢迎补充。