7. 新增Symbol
# 1. Symbol
Symbol
是ES6新增的简单数据类型
,由Symbol构造函数生成,表示独一无二
的值
定义方式如下:
let s1 = Symbol('jack') // Symbol(jack)
let s2 = Symbol('jack') // Symbol(jack)
s1 === s2 // false,独一无二
创建Symbol时,传入的参数会成为它的description
属性
const sym = Symbol('jack')
sym.description // 'jack'
# 2. Symbol属性读写
Symbol类型作为属性读写需要使用形如[symbol]
的方式
let sym = Symbol()
let fn = Symbol('fn of Symbol')
let a = {
[sym]: 'Hello!',
[fn](){
alert(this[sym])
}
}
访问时不能通过点运算符
let sym = Symbol()
let a = {}
a[sym] = 'jack'
a.sym = 'tom' // 点操作符读取的不是Symbol类型的sym,而是普通字符串sym
a[sym] // jack
a.sym // tom
通过Symbol类型可以定义一组常量,以保证唯一性
const COLOR_RED = Symbol()
const COLOR_GREEN = Symbol()
function getColor(color) {
if(color === COLOR_RED) return 'red'
if(color === COLOR_GREEN) return 'green'
}
# 3. Symbol属性遍历
Symbol属性只能通过Object.getOwnPropertySymbols()
方法遍历,
不能被for-in
、for-of
、Object.keys()
、Object.getOwnPropertyNames()
、JSON.stringify()
遍历读取
const name = Symbol('name')
const id = Symbol('id')
const obj = {
[id]: 1007,
[name]: 'jack'
}
for(let key in obj) console.log(key) // 无输出
let propertySymArr = Object.getOwnPropertySymbols(obj) // [Symbol(id), Symbol(name)]
for(let key of propertySymArr){
console.log(obj[key]) // 1007, jack
}
另外,Reflect.ownKeys(obj)
可以返回包含常规键名
和Symbol键名
的数组
const id = Symbol('id')
const obj = {
[id]: 1007,
name: 'jack'
}
Reflect.ownKeys(obj) // [Symbol(id),'name']
# 4. Symbol定义私有属性
利用symbol属性名不能被常规方法遍历到的这一特性,可以将定义私有的属性
let size = Symbol('size')
class Collection {
constructor() {
this[size] = 0 // 私有
}
add(item) {
this[this[size]] = item // 妙啊~
this[size]++
}
static sizeOf(instance) {
return instance[size]
}
}
let x = new Collection()
x.add('a')
Collection.sizeOf(x) // 1
x[0] // 'a'
Object.keys(x) // ['0']
Object.getOwnPropertyNames(x) // ['0']
Object.getOwnPropertySymbols(x) // [Symbol(size)]
Reflect.ownKeys(x) // ['0',Symbol(size)]
# 5. Symbol.for()和Symbol.keyFor()
Symbol.for(str)
返回description
为str
的全局symbol变量,若没有则在全局注册一个
不多bb直接甩代码
let s3 = Symbol('foo')
let s1 = Symbol.for('foo') // 没有description属性为'foo'的symbol变量,全局注册一个 Symbol('foo')
let s2 = Symbol.for('foo') // 返回Symbol('foo')
s1 === s2 // true
s1 === s3 // false
Symbol.keyFor()
方法返回一个全局注册的 Symbol 类型值的key
,若没有则返回undefined
let s1 = Symbol.for('foo') // 注意,这里将会在全局注册
Symbol.keyfor(s1) // foo
let s2 = Symbol('foo') // 没在全局注册
Symbol.keyfor(s2) // undefined
# 6. Symbol应用于Singleton模式
Singleton模式指的是调用一个类,任何时候返回的都是同一个实例,对于Node保证的是每次执行一个模块文件,返回的都是同一个实例,其核心思想是将实例放到顶层对象global作为属性存在
以下是不使用Symbol的情况:
// mod.js
function A(){
this.name = 'jack'
}
if(!global._a){
global._a = new A()
}
module.exports = global._a
// index1.js
const a = require('./mod.js')
// index2.js
const b = require('./mod.js')
// a和b都应用同一个A类实例
问题在于,如果有个坏蛋在一个文件上修改了global._a
,其他后面文件再引入global._a
就GG了,那么可以用Symbol的唯一性和私用化属性这一特性了
const A_KEY = Symbol('a') // 局部注册,而非全局注册
function A() {
this.a = 'hello'
}
if (!global[A_KEY]) {
global[A_KEY] = new A()
}
module.exports = global[A_KEY]
// index3.js,写这个文件的小坏蛋想尝试修改global[A_KEY]
global[A_KEY] = {a: 'haha~'} // 抱歉,你没有这个[A_KEY]
global[Symbol('a')] = {a: '我再来'} //抱歉,symbol是唯一的,你这个Symbol('a') 与我的Symbol('a') 不是同一个
# 7. 全局内置Symbol值
# 7.1 [Symbol.hasInstance]
可以用于重写instanceof机制
,当执行 instanceof 操作符时,会在构造器内部调用这个方法
a instanceof A// 实际调用A[Symbol.hasInstance](a)
举个例子
class Person{
static [Symbol.hasInstance](obj){
return obj instanceof Array
}
}
[1,2,3] instanceof Person // true
new Person() instanceof Person // false
# 7.2 [Symbol.isConcatSpreadable]
[Symbol.isConcatSpreadable]
是一个布尔值
,当执行Array.prototype.concat()
时,决定其是否可以展开
let arr1 = [1,2]
let res = [3,4].concat(arr1, 5) // [3,4,1,2,5]
let arr2 = [1,2]
arr2[Symbol.isConcatSpreadable] = false // 关闭展开机制
let res = [3,4].concat(arr2, 5) // [3,4,[1,2],5]
若想将一个伪数组的对象展开,可以指定[Symbol.isConcatSpreadable]
为true
let obj = {length: 2, 0: 'c', 1: 'd'}
obj[Symbol.isConcatSpreadable] = true
['a', 'b'].concat(obj, 'e') // ['a', 'b', 'c', 'd', 'e']
# ☆7.3 [Symbol.iterator]
指向对象的默认遍历器方法
const myIterable = {}
myIterable[Symbol.iterator] = function* () {
yield 1
yield 2
yield 3
}
[...myIterable] // [1, 2, 3]
class Collection {
*[Symbol.iterator]() {
let i = 0
while(this[i]) {
yield this[i]
i++
}
}
}
let myCollection = new Collection()
myCollection[0] = 1
myCollection[1] = 2
for(let value of myCollection) {
console.log(value) // 1、2
}