7. 新增Symbol

2/17/2022 ES6

# 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-infor-ofObject.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)返回descriptionstr的全局 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
}
    人生如梦,
    我投入了的却是真情,
    世界先爱了我,
    我不能不爱它。
    红莲华
    x
    loading...