Symbol类型

更新时间: 2023-03-01 16:19:05

# Symbol

自ECMAScript2015起,symbol成为了一种新的原生类型,就像numberstring一样。symbol类型的值是通过Symbol构造函数创建的。可以传递参数作为唯一标识,只支持stringnumber类型的参数。

let sym1:symbol = Symbol()
let sym2:symbol = Symbol("key")
1
2

# Symbol的值是唯一的

const s1:symbol = Symbol(1)
const s2:symbol = Symbol(1)
// s1 === s2 false
1
2
3

# 如何让两个Symbol的值相等呢

Symbol.for 会全局找有没有注册这个key,如果有就直接拿来用,没有就会创建一个

console.log(Symbol.for('daodao') === Symbol.for('daodao')) //true
1

# 用作对象属性的键

let sym:symbol = Symbol()

let obj = {
  [sym]:"value"
}

console.log(obj[sym]) // value
1
2
3
4
5
6
7

symbol定义的属性,是不能通过如下方式遍历拿到的

const symbol1:symbol = Symbol('666')
const symbol2:symbol = Symbol('777')
const obj1 = {
 [symbol1]:'daodao',
 [symbol2]:'miaomiao',
 age:18,
 sex:0
}

// 使用for in 遍历拿不到
for(const key in obj1) {
  console.log(key) // age,sex
}

// 使用Object.keys遍历,拿不到
console.log(Object.keys(obj1)) // ['age','sex']

// getOwnPropertyNames, 拿不到
console.log(Object.getOwnPropertyNames(obj1)) // ['age','sex']

// JSON.stringfy,拿不到
console.log(JSON.stringify(obj1)) // {age:18,sex:0}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

那么如何拿到呢

// 用getOwnPropertySymbols拿到symbol属性,对象中有几个就会拿到几个
console.log(Object.getOwnPropertySymbols(obj1)) //[Symbol('666'),Symbol(777)]

// 用es6的Reflect拿到对象的所有属性
console.log(Reflect.ownKeys(obj1)) // [Symbol('666'),Symbol('777'),'age','sex']
1
2
3
4
5

# 生成器

生成器函数需要在function的后面加一个符号*,可以通过yield关键字来控制函数的执行流程,范湖一个Generator,是一种特殊的迭代器。

function* gen() {
    yield Promise.resolve('喵') //同步异步都可以
    yield '喵喵'
    yield '喵喵喵'
    yield '喵喵喵喵'
}

const man = gen()
console.log(man.next()) //{ value: Promise { '喵' }, done: false }
console.log(man.next()) //{ value: '喵喵', done: false } 
console.log(man.next()) //{ value: '喵喵喵', done: false } 
console.log(man.next()) //{ value: '喵喵喵喵', done: false }
console.log(man.next()) //{ value: undefined, done: true } 
1
2
3
4
5
6
7
8
9
10
11
12
13

# 生成器提前结束 - return函数

return传值后这个生成器函数就会结束,之后调用next不会继续生成值了


function* foo(num) {
  console.log("函数开始执行~")
 
  const value1 = 100 * num
  console.log("第一段代码:", value1)
  const n = yield value1
 
  const value2 = 200 * n
  console.log("第二段代码:", value2)
  const count = yield value2
 
  console.log("函数执行结束~")
  return "123"
}
 
const generator = foo(10)
 
console.log(generator.next())
 
// 第二段代码的执行, 使用了return
// 那么就意味着相当于在第一段代码的后面加上return, 就会提前终端生成器函数代码继续执行
console.log(generator.return(15)) //这里不会执行第二段代码了,因为提前终止了
console.log(generator.next())
console.log(generator.next())
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 迭代器

set是es6新增的,具有天然去重的特性,不过去重只能用于字符串和数字

let set:Set<number> = new Set([1,1,2,2,3,3]) //天然去重
console.log(set) // Set(3) { 1, 2, 3 }
1
2

map和数组类似,它支持对象作为键

let map:Map<any,any> = new Map()

let Arr = [1,2,3]
map.set(Arr, '刀刀')
console.log(map) //Map(1) { [ 1, 2, 3 ] => '刀刀' }
console.log(map.get(Arr)) //刀刀
1
2
3
4
5
6

它们都是伪数组,并不支持数组的方式遍历,那么如何遍历呢,这里就要用到迭代器,他们都有Symbol.iterator方法

const each = (value:any) => {
    let It:any = value[Symbol.iterator]()
    let next:any = {done:false}
    while(!next.done) {
        next = It.next()
        if(!next.done) {
            console.log(next.value)
        }
    }
}

each(set) // 1 2 3
1
2
3
4
5
6
7
8
9
10
11
12

但是这样写未免也太麻烦了,其实迭代器是有语法糖的,就是for of,记住啦,for of是不能循环对象的因为对象没有iterator

// 迭代器的语法糖
// for of 对象不能用
for (let value of set) {
    console.log(value) // 1 2 3
}

// 解构 底层原理也是去调用iterator
let [a,b,c] = [4,5,6]
1
2
3
4
5
6
7
8

怎样才能让对象支持for of呢,我们可以给对象添加一个iterator


const obj = {
    max: 5,
    current: 0,
    [Symbol.iterator]() {
        return {
            max: this.max,
            current: this.current,
            next() {
                if (this.current == this.max) {
                    return {
                        value: undefined,
                        done: true
                    }
                } else {
                    return {
                        value: this.current++,
                        done: false
                    }
                }
            }
        }
    }
}
console.log([...obj]) [0,1,2,3,4]
 
for (let val of obj) {
   console.log(val); // 1 2 3 4 5
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

以下为这些symbols的列表

Symbol.hasInstance
会被instanceof运算符调用。构造器对象用来识别一个对象是否是其实例。

Symbol.isConcatSpreadable
布尔值,表示当在一个对象上调用Array.prototype.concat时,这个对象的数组元素是否可展开。

Symbol.iterator
方法,被for-of语句调用。返回对象的默认迭代器。

Symbol.match
方法,被String.prototype.match调用。正则表达式用来匹配字符串。

Symbol.replace
方法,被String.prototype.replace调用。正则表达式用来替换字符串中匹配的子串。

Symbol.search
方法,被String.prototype.search调用。正则表达式返回被匹配部分在字符串中的索引。

Symbol.species
函数值,为一个构造函数。用来创建派生对象。

Symbol.split
方法,被String.prototype.split调用。正则表达式来用分割字符串。

Symbol.toPrimitive
方法,被ToPrimitive抽象操作调用。把对象转换为相应的原始值。

Symbol.toStringTag
方法,被内置方法Object.prototype.toString调用。返回创建对象时默认的字符串描述。

Symbol.unscopables
对象,它自己拥有的属性会被with作用域排除在外。