Symbol下的方法:
1、Symbol.asyncIterator
根据ECMAScript规范,这个符号作为一个属性表示“一个方法,该方法返回对象默认的AsyncIterator”。由for-await-of语句使用,表示实现异步迭代器API的函数。for-await-of循环会利用这个函数执行异步迭代操作。
例:
class Example{
constructor(max){
this.max=max
this.asyncIdx=0
}
async*[Symbol.asyncIterator](){
while(this.asyncIdx<this.max){
yield new Promise((resolve)=>resolve(this.asyncIdx++))
}
}
}
async function asyncCount(){
let example=new Example(5)
for await(const x of example){
console.log(x)
}
}
// 输出:asyncCount()
// 0 1 2 3 4
注意:Symbol.asyncIterator是ES2018规范定义的,因此只有版本非常新的浏览器支持它。
2、Symbol.hasInstance
根据ECMAScript规范,这个符号作为一个属性表示“一个方法,该方法决定一个构造函器对象是否认可一个对象是它的实例”。由instanceof操作符使用,instanceof操作符可以用来确定一个对象实例的原型上是否有原型。
例:
function Foo(){}
let f=new Foo()
console.log(f instanceof Foo) //true
在ES6中,instanceof操作符会使用Symbol.hasInstance函数来确定关系,以Symbol.hasInstance为键的函数会执行同样的操作,只是操作数对调了一下。
function Foo(){}
let f=new Foo()
console.log(Foo[Symbol.hasInstance](f)) //true
这个属性定义在Function的原型上,因此默认所有函数和类上都可以调用。由于instanceof操作符会在原型链上寻找这个属性定义,就跟在原型链上寻找其他属性一样,因此可以在继承的类上通过静态方法重新定义这个函数。
例:
class Bar{}
class Baz extends Bar{
static [Symbol.hasInstance](){
return false
}
}
let b=new Baz()
console.log(Bar[Symbol.hasInstance](b)) //true
console.log(b instanceof Bar) //true
console.log(Baz[Symbol.hasInstance](b)) //false
console.log(b instanceof Baz) //false
3、Symbol.isConcatSpreadable
根据ECMAScript规范,这个符号作为一个属性表示“一个布尔值,如果是true,则意味着对象应用Array.prototype.concat()打平其数组元素”。ES6中Array.prototype.concat()方法会根据接收到的对象类型选择如何将一个类数组对象拼接成数组实例。覆盖Symbol.isConcatSpreadable的值可以修改这个行为。
(1)数组对象默认情况下会被打平到已有的数组
例:
let initial=['foo']
let array=['bar']
console.log(initial.concat(array)) // ['foo', 'bar']
(2)false或假值会导致整个对象被追加到数组末尾。类对象默认情况下会被追加到数组末尾。
例:
array[Symbol.isConcatSpreadable]==false
console.log(initial.concat(array)) //['foo', 'bar']
(3)true或真值会导致这个类数组对象被打平到数组实例。
例:
let list={length:1,0:'baz'}
console.log(initial.concat(list)) //['foo', {…}]
list[Symbol.isConcatSpreadable]=true
console.log(initial.concat(list)) //['foo', 'baz']
4、Symbol.iterator
根据ECMAScript规范,这个符号作为一个属性表示“一个方法,该方法返回对象默认迭代器”。由for-of语句使用。换句话说,这个符号实现迭代器API的函数。
例:
class Emitter{
constructor(max){
this.max=max
this.id=0
}
*[Symbol.iterator](){
while(this.id<this.max){
yield this.id++;
}
}
}
function count(){
let emitter=new Emitter(5)
for(const x of emitter){
console.log(x)
}
}
count()
// 输出 0 1 2 3 4
5、Symbol.match
根据ECMAScript规范,这个符号作为一个属性表示“一个正则表达方法,该方法用正则表达式去匹配字符串”。由String.prototype.match()方法使用。String.prototype.match()方法会使用以Symbol.match为键的函数来对正则表达式求值。
例:
console.log('foobar'.match(/bar/))
//['bar', index: 3, input: 'foobar', groups: undefined]
给这个方法传入非正则表法式值会导致该值被转换为RegExp对象。如果想改变这种行为,让方法直接使用参数,则可以重新定义Symbol.match函数以取代默认正则表达式求值的行为,从而让match()方法使用非正则表达式实例。Symbol.match函数接收一个参数,就是调用match()方法的字符串实例,返回值没有限制。
例:
class FooMatcher{
static [Symbol.match](target){
return target.includes('foo')
}
}
console.log('foobar'.match(FooMatcher)) //true
console.log('barbaz'.match(FooMatcher)) //false
6、Symbol.replace
根据ECMAScript规范,这个符号作为一个属性表示“一个正则表达式方法,该方法替换一个字符串中匹配的子串”。由String.prototype.replace()方法使用。String.prototype.replace()方法会使用以Symbol.replace为键的函数来对正则表达式求值。
console.log('foobaraz'.replace(/bar/,'qux')) //fooquxaz
给这个方法传入非正则表法式值会导致该值被转换为RegExp对象。如果想改变这种行为,让方法直接使用参数,则可以重新定义Symbol.replace函数以取代默认正则表达式求值的行为,从而让replace()方法使用非正则表达式实例。Symbol.replace函数接收一个参数,就是调用replace()方法的字符串实例和替换字符串,返回值没有限制。
class FooReaplace {
static [Symbol.replace](target, replacement) {
return target.split('foo').join(replacement)
}
}
console.log('barfoobaz'.replace(FooReaplace, 'qux')) //barquxbaz
class StringReplacer {
constructor(str) {
this.str = str
}
[Symbol.replace](target, replacement) {
return target.split(this.str).join(replacement)
}
}
console.log('barfoobaz'.replace(new StringReplacer('foo'),'qux')) //barquxbaz
7、Symbol.search
根据ECMAScript规范,这个符号作为一个属性表示“一个正则表达式方法,该方法返回字符串中匹配正则表达式的索引,由String.prototype.search()方法使用”。String.prototype.search()方法会使用以Symbol.search为键的函数来对正则表达式求值。正则表达式的原型上默认有这个函数的定义,因此所有正则表达式实例默认是这个String方法有效值。
console.log('foobar'.search(/bar/)) //3
给这个方法传入非正则表法式值会导致该值被转换为RegExp对象。如果想改变这种行为,让方法直接使用参数,则可以重新定义Symbol.search函数以取代默认正则表达式求值的行为,从而让search()方法使用非正则表达式实例。Symbol.search函数接收一个参数,就是调用match()方法的字符串实例,返回值没有限制。
class FooSearch{
static [Symbol.search](target){
return target.indexOf('foo')
}
}
console.log('foobar'.search(FooSearch)) //0
console.log('barfoo'.search(FooSearch)) //3
console.log('barbaz'.search(FooSearch)) //-1
8、Symbol.species
根据ECMAScript规范,这个符号作为一个属性表示"一个函数值,该函数值作为创建派生对象的构造函数。"这个属性在内置类型中最为常见,用于对内置类型实例方法的返回值暴露实例化派生对象的方法。用Symbol.species定义静态的获取器(getter)方法,可以覆盖创建实例的原型定义。
class Bar extends Array{}
class Baz extends Array{
static get [Symbol.species](){
return Array
}
}
let bar=new Bar()
console.log(bar instanceof Array) //true
console.log(bar instanceof Bar) //true
console.log(bar instanceof Baz) //false
let baz=new Baz()
console.log(baz instanceof Array) //true
console.log(baz instanceof Bar) //false
console.log(baz instanceof Baz) //true
baz=baz.concat('baz')
console.log(baz instanceof Array) //true
console.log(baz instanceof Bar) //false
console.log(baz instanceof Baz) //false
9、Symbol.split
根据ECMAScript规范,这个符号作为一个属性表示"一个正则表达式方法,该方法在匹配正则表达式的索引位置拆分字符串。由String.prototype.split()方法使用"。String.prototype.split()方法会使用以Symbol.split为键的函数来对正则表达式求值。正则表达式的原型上默认有这个函数的定义,因此所有正则表达式实例默认是这个String方法的有效参数。
console.log('foobarbaz'.split(/bar/)) //['foo','baz']
给这个方法传入非正则表法式值会导致该值被转换为RegExp对象。如果想改变这种行为,让方法直接使用参数,则可以重新定义Symbol.split函数以取代默认正则表达式求值的行为,从而让split()方法使用非正则表达式实例。Symbol.split函数接收一个参数,就是调用match()方法的字符串实例,返回值没有限制。
class FooSplitter{
static [Symbol.split](target){
return target.split('foo')
}
}
console.log('barfoobaz'.split(FooSplitter)) // ['bar', 'baz']
10、Symbol.toPrimitive
根据ECMAScript规范,这个符号作为一个属性表示"一个方法,该方法将对象转换成相应的原始值。由ToPrimitive抽象操作使用。"很多内置操作都会强制将对象转化为原始值,包括字符串、数值和未指定的原始类型。对于一个自定义的对象实例,通常在这个实例的Symbol.toPrimitive属性上定义一个函数可以改变默认行为。
class Bar{
constructor(){
this[Symbol.toPrimitive]=function(hint){
switch (hint){
case 'number':
return 3
case 'string':
return 'string bar'
case 'default':
default:
return 'default bar'
}
}
}
}
let bar=new Bar()
console.log(3+bar) //3default bar
console.log(3-bar) //0
console.log(String(bar)) //string bar
11、Symbol.toStringTag
根据ECMAScript规范,这个符号作为一个属性表示"一个字符串,该字串用于创建对象的默认字符串描述。由内置方法Object.prototype.toString()使用。"
let s=new Set()
console.log(s) //Set(0) {}
console.log(s.toString()) //[object Set]
console.log(s[Symbol.toStringTag]) //Set
12、Symbol.unscopables
根据ECMAScript规范,这个符号作为一个属性表示"一个对象,该对象所有的以及继承的属性。都会从关联对象的with环境绑定中排除"。设置这个符号并让其映射对应属性的键值为true,就可以阻止该属性出现在with环境绑定中。
let o={foo:'bar'}
with(o){
console.log(foo) //bar
}
o[Symbol.unscopables]={
foo:true
}
with(o){
console.log(foo) //foo is not defined
}