介绍Symbol

295 阅读4分钟

1.简介

Symbol(符号)ES6新增数据类型

符号是原始值,并且实例的唯一的、不可变的

符号的用途是确保对象属性使用唯一标识符,不会发生属性冲突的危险,它用来创造唯一记号,用作对象非字符串属性

2. 基础用法

2.1、简单使用

符号需要使用 Symbol() 函数来初始化,使用 typeof 操作符返回的是symbol

使用:let symbol = Symbol() 或者 let symbol = Symbol('description')

唯一性:只要创建符号,并用作对象属性,就一定可以保证,它不会覆盖之前的属性,不管是符号还是字符串

let s1 = Symbol() 
let s2 = Symbol()
console.log(s1==s2) // false
let s3 = Symbol('foo')
let s4 = Symbol('foo')
console.log(s3==s4) // false

2.2、共享与重用(使用全局符号注册表)

共享和重用可以使用 Symbol.for() 方法,创建全局注册表

let globalSymbol = Symbol.for('foo') // 第一次创建新得全局符号
let useSymbol = Symbol.for('foo') // 重用
console.log(globalSymbol===useSymbol) // true,在2.1中可以看到,没有使用Symbol.for()方法返回的是false

let localSymbol = Symbol('foo')
console.log(globalSymbol === localSymbol) // false

可以使用 Symbol.keyFor() 方法查询全局注册表

let s = Symbol.for('foo')
console.log(Symbol.keyFor(s)) // foo

2.3、使用符号作为属性

凡是可以使用字符串或者数字作为属性的地方,都可以使用符号

let s1 = Symbol('foo')
let s1 = Symbol('car')

let obj = {
[s1]: 'foo value'
}
obj[s2] = 'car value'
console.log(obj) // {Symbol('foo'): 'foo value', Symbol('car'): 'car value'}

符号属性是对内存中符号的一个引用,直接创建用作属性的符号不会丢失,但是如果没有显式的保存,就要遍历对象的所有属性才能找到相应的属性键

let obj = {Symbol('foo'): 'foo value', Symbol('car'): 'car value'}
// 想要找到Symbol('foo'),必须遍历对象的所有属性
let res = Object.getOwnPropertySymbols(obj).find((f)=> f.toString.match(/foo/))
console.log(res) // Symbol('foo')

3. 内置符号

3.1、Symbol.asyncIterator(表示实现异步迭代器API的函数)

该符号作为一个属性表示:一个方法,该方法返回对象默认的AsyncIterator。由 for-await-of 使用

class Foo {
  constructor(name) {
    this.name = name;
  }
  async *[Symbol.asyncIterator]() {
    yield new Promise((resolve) => resolve(this.name));
  }
}
let f = new Foo("xiaoli");
// 手动调用 next()
let func = f[Symbol.asyncIterator]();
const generator = async () => {
  console.log(await func.next()); // {value: "xiaoli", done: false}
  console.log(await func.next()); // {value: undefined, done: true}
};
generator();
// 隐式通过异步生成器函数返回
const generator1 = async () => {
   for await (const key of f) {
      console.log(key); // xiaoli
   }
};
generator1();

3.2、Symbol.hasInstance

该符号作为一个属性表示:一个方法,该方法决定一个构造器对象是否认可一个对象是它的实例。由 instanceof 操作符使用。

该属性定义在Function的原型上,所以所有函数和类上都可以调用

function Foo() {}
let f = new Foo()
console.log(Foo[Symbol.hasInstance](f)) // true

class Obj {}
let o = new Obj()
console.log(Obj[Symbol.hasInstance](o)) // true

instanceof 操作符会在原型链上寻找这个属性定义,由于js查找遵循原型链查找,并且采用就近原则,所以我们可以通过定义静态方法重新实现这个函数

class Person {}
class Man extends Person {
	static [Symbol.hasInstance]() { 
     return false
    }
}
let p = new Man
console.log(Person[Symbol.hasInstance](p)) // true
console.log(p instanceof Person) // true

console.log(Man[Symbol.hasInstance](p)) // false
console.log(p instanceof Man) // false,这里之所以返回false,是因为在原型链上查找Symbol.hasInstance属性时,在就近的自身中就有这个属性,并且任何时候都返回false,所以这里就使用的自身定义的Symbol.hasInstance

3.3、Symbol.isConcatSpreadable

该符号作为一个属性表示:一个布尔值,如果是true,意味着对象应该调用Array.prototype.concat() 打平其数组元素,fasle 会导致整个对象被追加到数组末尾

let array = [1]
let array1 = [2]
array.concat(array1) // [1,2]
array1[Symbol.isConcatSpreadable] = false
array.concat(array1) // [1,Array(1)]

3.4、Symbol.iterator

该符号作为一个属性表示:一个方法,该方法返回对象默认的迭代器。由 for-of 语句使用。

class Emitter {
  constructor(max) {
    this.max = max;
    this.index = 0;
  }
  *[Symbol.iterator]() {
    while (this.index < this.max) {
      yield this.index++;
    }
  }
}
const func1 = async () => {
  let e = new Emitter(5);
  for (const x of e) {
    console.log(x);
  }
};
func1();

3.5、Symbol.match

该符号作为一个属性表示:表示一个正则表达式方法,该方法用正则表达式去匹配字符串。由 String.prototype.match() 使用。

class MyMatch {
	constructor(str) {
    	this.str = str
    }
	
    static [Symbol.match](target) {
    	return target.includes(this.str)
    }
}

console.log('abc'.match(new MyMatch('b'))) // true
console.log('abc'.match(new MyMatch('d'))) // false

3.6、Symbol.replace

该符号作为一个属性表示:表示一个正则表达式方法,该方法替换一个字符串中匹配的子串。由 String.prototype.replace() 使用。

Symbol.replace方法接收两个参数,第一个是调用replace方法的字符串实例,第二个是要替换的字符串,实现和match类似

'abcd'.replace(/bc/,'ef') // aefd

3.7、Symbol.search

该符号作为一个属性表示:表示一个正则表达式方法,该方法返回字符串中匹配正则表达式的索引。由 String.prototype.search() 使用。

'abcd'.search(/bc/) // 1

3.8、Symbol.species

3.9、Symbol.split

该符号作为一个属性表示:表示一个正则表达式方法,该方法在匹配正则表达式的索引位置拆分字符串。由 String.prototype.split() 使用。 3.10、Symbol.toPromitive 该符号作为一个属性表示:表示一个方法,该方法将对象转换为相应的原始值 3.11、Symbol.toStringTag 该符号作为一个属性表示:表示一个字符串,该字符串用于创建对象的默认字符串描述 3.12、Symbol.unscopables