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...