ES6中的基本类型增加到7个,比之前的版本多了一个Symbol。好像出现很久了,但是因为没有使用场景,所以一直被理解为一个概念层,我觉得最好的使用方式就是主动去深入了解,所以我从基础部分和总结的实际场景来分析这个特性。已经知道如何使用或者时间紧迫的可以从实际场景部分开始阅读基础。首先,它给我的第一印象是ES6在发布语言特性方面做了很多改变,让我们更好的理解语言的内部机制,Symbol通过对象的键值来定义,比如让key=Symbol('测试');让对象={};obj[key]='单独';对象[键];//“alone”符号,顾名思义,表示一个作为属性存在于对象中的唯一标记,它接收一个参数,它没有真正的作用,只是为了描述。上面我们通过直接数量的方式来定义,取值的时候也需要使用key来读取。如果出现跨scope的情况,是不是获取不到呢?functionsent(key){accept({[key]:"2018"})}functionaccept(obj){obj[???]//怎么混合呢?}在上面两个scope中,如果不传key,是读不到的。一个属性可以,但是如果太多的话,通过参数传递key就不现实了。在这种情况下,我们可以使用Symbol.for为它添加另一个标记,它接受一个参数String{key}。通常表现为局部功能标记,在全剧中独树一帜。functionsent(key){returnaccept({[key]:"2018"},key)}functionaccept(obj,key){console.log(Symbol.keyFor(key))//CURRENT_YEAR返回obj[Symbol.for(Symbol.keyFor(key))]//CURRENT_YEAR}sent(Symbol.for('CURRENT_YEAR'))并使用Symbol.for生成,它会存储在当前全局上下文中的一个结构中,我们调用它对于GlobalSymbolRegistry,顾名思义,它是全局的,所以我们在使用key的时候需要谨慎,尤其是在大型项目中。还应注意以下几点:要读取它,您需要使用getOwnPropertySymbols方法。详见MDNSymbol()!==Symbol()但Symbol.for('t')===Symbol.for('t')GlobalSymbolRegistry对象存在于当前窗口的进程中,并没有被清除直到窗户关闭。在当前的浏览器版本中,Symbol是以字符串格式打印的,并没有显示具体的对象结构。我们可以直接打印Symbol来查看对应的prototype属性和Internal方法,所以Symbol().__proto__===Symbol.prototype在使用Symbol作为键值时,经历了以下几个步骤。如果未定义指向的对象,将抛出类型错误。如果描述符未定义,则将''将描述符转换为String格式,生成一个唯一的key,并返回上一步,将这个key赋值给对象,并以Symbol(des)的形式显示,以及内部还是以key为基础,所以Symbol()!==Symbol(),虽然看起来都是字符串的"Symbol()",但是这样写也是可以的,但是好像没有意义varn=1;varkey=Symbol('numer')n[key]='SymbolNumber当'n[key]时,将n隐式转换为一个封装对象,为其添加一个Symbol,但是没有办法返回这个Symbol通过封装的对象。除了简单的使用key,Symbol类下还有一些有趣的方法,如下:iterator为指向的对象添加一个迭代器接口,比如使用数组解构或者使用forof,它接受一个生成器函数classIteratorExec{constructor(){this.count=1}*[Symbol.iterator]=function*(){yieldthis.count++;yieldthis.count++;yieldthis.count++;}}letobj=newIteratorExec()[...obj]//[1,2,3]通过添加迭代器来使用数据解构,也可以使用forofletvalues=[];for(letvalueofobj){values.push(value)}values;//[1,2,3]注意:在ES6中,Map、Set、array和添加了Iterator接口的对象,都有Iterator接口。asyncIterator不是ES6的特性,ES7好像是,可以提前看下面的代码:forawait(constlineofreadLines(filePath)){console.log(line);}toPrimitive在右边当转换对象类型时,将执行一个toPrimitive。使用这个Symbol可以改变目标对象的转换规则,改变之前“[objectObject]”的固定形式letobj={[Symbol.toPrimitive](hint){switch(hint){case'number':return5;case'string':返回'string';case'default':return'default'}}}obj+11//'default11'obj*2//这里需要注意10+Number操作不属于'number',其他正常,可以定义转换规则对于对象类型toStringTag在javascript中是一个对象,在每个对象中,都会有一个内部属性[[Class]]表示它的对象类型,可以在Symbol.toStringTag中修改,即'[objectObject]后面的字符串'是可定制的letobj={[Symbol.toStringTag]:'custom'}Object.prototype.toString(obj);//[object对象]obj.toString();//[对象自定义]通常我们使用Object.prototype.toString来读取对象属性。正是因为向后兼容,规范才在对象自身的toString上实现了这个特性,而依然沿用老套的方法。但是我们可以使用下面的方式:obj={[Symbol.toStringTag]:'custom'get[Symbol.toStringTag](){return'custom'}}Object.prototype.toString.call(obj)我们将obj传入执行toString,可以达到这个效果。可以预料,在es6中,Object.toString是受上下文影响的。很明显,上面两个例子都是从Object.prototype.toString中获取的。两者区别很大,只有它能精确转换,如果你的toString不等于它,就不能转换,比如varn=newNumber();n[Symbol.toStringTag]=123;n.toString();//"0"太天真了,太无聊了?,Number的私有toString直接把[[PrimitiveValue]]转成字符串,这里大家要注意,不要误以为加Symbol.toStringTag就可以改变所有对象,如果当前对象不是纯Object,那么可以给这个对象加上getter返回对应的类型,这样在外面使用Object...call的时候,就会得到自定义的类型。因此,这需要在外部使用。如果你加个getter,人家不给你打电话你也无能为力。此外,Symbol还公开了几种类型,这些类型为原生对象定义了一些类型,例如Math.toString();//[objectMath]其他类型包括JSON,Promise,Map,TypedArray,DataView,ArrayBuffer,Generator等unscopeablesconstobject1={property1:42};object1[Symbol.unscopables]={property1:true};with(object1){console.log(property1);}这个功能我觉得可用度为0,基本用不到。instanceoperator,为这个操作添加一个hook,第一个参数是instance的左值,我们可以返回true|false来定义operator的返回值varobj1={[Symbol.hasInstance](instance){returnArray.isArray(数组)}}classArray1{static[Symbol.hasInstance](instance){returnArray.isArray(instance);}}[]instanceobj1//trueconsole.log([]instanceofArray1);//trueisConcatSpreadable表示[].concat是否可以展开,默认为true.letarr=[1,2];arr.concat([3,4],5)//[1,2,3,4,5]arr[Symbol.isConcatSpreadable]=false;arr.concat([3,4],5)//[[1,2],3,4,5]//[3,4]也可以提升为处理letarr2=[3,4]arr2[Symbol.isConcatSpreadable]=false;arr.concat(arr2,5);//[[1,2],[3,4],5]只有数组中的symbol属性为false时,concat操作时,才不会被解构。那么是不是说属性设置为true,就没有意义了呢?对于数组是的,因为它默认为真,但是对于类似数组的对象,它还有一个小功能://(continued)arr.concat({length:2,0:3,1:4,[Symbol.isConcatSpreadable]:true})//[1,2,3,4]match&replace&split&search一些字符串操作方法放在一起说,它们都是同一个意思,就是接受一个对象,然后实现一个钩子处理的函数并返回它们的处理结果。它们都是可以接收正则表达式的方法。在ES6之前,如果我们需要对字符串进行更复杂的操作,基本都在方法之外。ClassMyMatch{[Symbol.match](string){returnstring.indexOf('world')}}'helloworld'.match(newMyMatch());//6classMyReplace{[Symbol.replace](string){return'def'}}'abcdef'.replace(newMyReplace(),'xxx');//'abcxxx'classmySplit{[Symbol.split](val){returnval.split('-');}}"123-123-123".split(newmySplit());//['123','123','123']classMySearch{constructor(value){this.value=value;}[Symbol.search](string){returnstring.indexOf(this.value);}}varfooSearch='foobar'.search(newMySearch('foo'));//0varbarSearch='foobar'.search(newMySearch('bar'));//3varbazSearch='foobar'.search(newMySearch('baz'));//-1实践可以通过Symbol实现上面的功能方法,比如添加Iterator接口让对象队友接口特性。在实际开发中,我估计很少用到,但是我觉得sanycIterator是未来的前景,还在草稿阶段。Symbol作为键值的作用就很尴尬了。我从来没有在实际开发中使用过这个。到目前为止,我们只需要记住它是唯一的。例如,如果我们要使用一个对象添加两个相同的键名是非常不常见的,varfirstPerson=Symbol("peter");varsecondPerson=Symbol("peter");varpersons={[firstPerson]:"first",[secondPerson]:"pan"};综上所述,Symbol在使用层面和语言本身,暴露了更多的使用方法和特性(在Object类型上)。对于对象,它为NextECMScriptStandard提供了更多的可能性。这也是ES6中最大的变化之一。虽然不常用,但还是需要我们总结学习,以便在极端情况下能够自如应对。文中如有未尽之处,欢迎补充!注意:特别是在使用场景方面
