大家好,我是穷小白,人如其名,是一个前端小白,希望能和各位大佬学习,以后主要会发一些前端的文章,写的不好,请各位多多担待,我也是一个动漫爱好者,喜欢花里胡哨,希望可以和大家进行交流一下前端的问题。
Symbol(符号)是ECMAScript6新增的数据类型。符号是原始值,且符号实例是唯一的、不可变的。所以符号的用途是确保对象属性使用的唯一标识符,且不会发生属性冲突的危险,看起来Symbol和私有属性有点类似,但符号并不是为了提供私有属性的行为而增加的。相反,符号就是用来创建唯一记号,进而用作非字符串形式的对象属性。 1.符号的基本用法 符号需要使用Symbol()函数初始化,所以可以用typeof操作符对符号返回Symbol。 const s=Symbol(); console.log(typeof s);//返回Symbol 在调用Symbol()函数时,也可以传入一个字符串参数作为对符号的描述,但这个参数与符号定义或标识无关: let Symbol1=Symbol(); let Symbol2=Symbol(); console.log(Symbol1==Symbol2);//返回一Boolean值false let strSymbol1=s=Symbol('hello world'); let strSymbol2=s=Symbol('hello world'); console.log( strSymbol1== strSymbol2);//返回一Boolean值false Symbol()函数不能与new关键字一起作为构造函数,这样为避免创建符号包装对象,Symbol不能像Boolean和String及Number一样,它们支持构造函数且可用于初始化包含原始值的包装对象 let Boolean1=new Boolean() console.log(typeof Boolean);//返回Object 但Symbol()函数使用会报错像这样 let newSymbol=new Symbol();//TypeError:Symbol is not a constructor 但是如果想用符号包装对象,可以借用Object()函数 let mySymbol=Symbol(); let myWarppedSymbol=Object(mySymbol) console.log(typeof myWarppedSymbol );//返回Object 2.使用全局符号注册表 如果运行时不同的部分需要和重用符号实例,可以用一个某个字符串调用作为键,在全局符号注册表中创建并重用符号。但需要使用Symbol.for()方法 let strSymbol=Symbol.for('Hello world'); console.log(typeof strSymbol);//可以返回一个Symbol Symbol.for()对每个字符串键都执行幂等操作。第一次使用某个字符串调用时,他会检查全局运行时注册表,发现不存在对应的符号,于是就会生成一个新符号实例并添加到注册表中。后续使用相同字符串的调用同样的会检查注册表,发现存在与该字符串对应的符号,然后返回该符号实例 let strSymbol1=Symbol.for('Hello world')//创建新符号 let strSymbol2=Symbol.for(''Hello world');//重用已有的符号 console.log(strSymbol1== strSymbol2);//true 但即使采用相同的符号描述,全局注册表中定义的符号跟使用Symbol()定义的符号也并不等同: let strSymbol=Symbol('Hello world'); let strSymbol2=Symbol.for('Hello world'); console.log(strSymbol1== strSymbol2);//false 如果不传字符串入字符串的话里面为空 let strSymbol2==Symbol.for(); console.log(strSymbol2) //Symbol(underfined) 此外symbol还有一个Symbol.keyFor()可以用来查询全局注册表,它可以接收符号,返回全局符号对应的字符串键,如果查询的不是全局符号,则返回underfined。 //全局符号 let a=Symbol.for('foo') console.log(Symbol.keyFor(a))//foo //普通符号 let a=Symbol('foo') console.log(Symbol.keyFor(a))//underfined 如果传的不是符号,这个方法就会抛出TypeError 3.使用符号作为属性 凡是可以使用字符串或者数值作为属性的地方,都可以使用符号。这就包括了对象字面量属性和Object.defineProperty()/Object.defineProperties()定义的属性。对象字面量只能在计算属性语法中使用符号作为属性 let s1=Symbol('foo'), s2=Symbol('bar'), s3=Symbol('baz') s4=Symbol('qux') let o={ [s1]:'foo val' } console.log(o)//控制台返回 {Symbol(foo): 'foo val'} Object.defineProperty(o,s2,{value:'bar val'}); console.log(o)//控制台返回{Symbol(foo): 'foo val', Symbol(bar): 'bar val'} Object.defineProperties(o,{ [s3]:{value:'bar val'}, [s4]:{value:'qux val'} }); console.log(o)//{Symbol(foo): 'foo val', Symbol(bar): 'bar val', Symbol(baz): 'bar val', Symbol(qux): 'qux val'} 类似于Object.getOwnPropertyName()返回对象实例的常规属性数组,Object.getOwnPropertySymbols()返回对象实例的符号属性数组。这两个方法的返回值彼此互斥。Object.getOwnPropetyDescriptors()返回同时包含常规和符号属性描述符的对象。Reflect.ownKeys()返回两种类型的键,有兴趣的可以看一下。 4.常用内置符号 ES6中也引用了一批常用的内置符合,用于暴露语言的内部行为,可以直接访问、重写或者模拟这些行为。这些内置符号都以Symbol函数字符串属性的形式存在。 这些内置符号最重要的用途之一是重新定义它们,从而改变原生结构的行为。例如for-of循环会在相关对象上使用Symbol.iterator属性,这样就可以通过在自定义对象上重新定义Symbol.iterator的值,来改变for-of在迭代该对象时的行为。 这些内置符号没有特别的地方,只是全局函数Symbol的普通字符串属性,指向一个符号的实例。所有内置符号属性都是不可写、不可枚举、不可配置的。 5.Symbol.asyncIterator 根据ES的规范,这个符号作为一个属性表示“一个方法,这个方法返回对象默认的AsyncIterator。由for-awit-of语句使用”。这个符号表示实现异步迭代器API的函数。 for-awit-of循环会利用这个函数执行异步迭代操作。循环时,它们会调用Symbol.asyncIterator为键的函数,并期望这个函数会返回一个实现迭代器API的对象,返回的对象实现该API的AsyncGenerator: class Fun{ async *Symbol.asyncIterator{} } let f=new Fun(); console.log(fSymbol.asyncIterator)//AsyncGenerator {} 技术上,这个由Symbol.asyncIterator函数生成的对象应该通过其next()方法陆续返回Promise实例。可以通过显示地调用next()方法返回,也可以隐式通过异步生成器函数返回: class Emitter{ 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 emitter=new Emitter(5); for await(const x of emitter ){ console.log(x ) } } asyncCount()//0 1 2 3 4
这篇文章我的第一篇文章,写的不好请各位大佬多多指教,目前还在探索,我比较喜欢分享生活,以后会经常更新些前端的基础性文章,以及在工作中遇到的问题及解决的办法,希望各位可以多提出意见,希望大家能够喜欢我的短文,认识更多的朋友。