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
}