ES6中基础类型增长到了7种,比上一个版本多了一个Symbol
,貌似出现了很长时间,但却因没有使用场景,一直看成一个概念层来理解它,我想,用它的最好的方式,仍是要主动的去深刻了解它吧,因此我从基础部分和总结的实用场景来分析这个特性。已经了解使用方法或者时间紧迫者能够从实用场景一节开始阅读javascript
首先,它给个人第一感受就是ES6作出了不少释放语言特性方面的改变
,它能让咱们更加了解语言内部机制,Symbol以对象的键值定义,好比java
let key = Symbol('test'); let obj = {}; obj[key] = 'alone'; obj[key]; // "alone"
Symbol正如其名,表示一个惟一的标示,以属性的方式存在于对象当中,它接收一个参数,没有实质的做用,只是为了作一个描述。以上咱们经过直接量的方式来定义它,而且取值时,也须要使用key进行读取,若是出现跨做用域的状况,是否是就不能获取了?git
function sent(key){ accept({[key]:"2018"}) } function accept(obj) { obj[???] //我怎么拌? }
以上两个做用域中,若是不把key传递过来,是没法读取的,一个属性还好,可是若是多了,那么靠参数传递key是不现实的. 在这种状况下,咱们可使用 Symbol.for
来为它再添加一个标示
,它接受一个参数String{key}。一般,它作为一个偏功能性的标记来表示,在全剧中它是惟一的。es6
function sent(key){ return accept({[key]:"2018"},key) } function accept(obj,key) { console.log(Symbol.keyFor(key)) //CURRENT_YEAR return obj[Symbol.for(Symbol.keyFor(key))] //CURRENT_YEAR } sent(Symbol.for('CURRENT_YEAR'))
而且使用 Symbol.for
来生成,会在存入当前全局上下文中一个<List>
结构中,咱们称它为GlobalSymbolRegistry
, 顾名思义,它是全局的,因此使用key时咱们须要谨慎,尤为是在大型项目中。github
须要还注意如下几点:segmentfault
getOwnPropertySymbols
方法,具体请参看MDN but
Symbol.for('t') === Symbol.for('t')
目前的浏览器版本中把Symbol打印出来是字符串的格式,并无显示具体的对象结构,咱们能够直接打印 Symbol,来查看对应的prototype属性以及内部方法,因此
Symbol().__proto__ === Symbol.prototype
在使用 Symbol 作key值时,它经历了如下步骤数组
因此这样写也是能够的,可是貌似没有什么意义浏览器
var n = 1; var key = Symbol('numer') n[key] = ‘Symbol Number’
n[key]的时候把n隐式转换成封装对象,并为他添加Symbol,但并无办法去经过封装对象回访这个Symbolasync
除了单纯的用key之外,在Symbol类下还有一些有意思的方法,following :函数
为指向对象添加 iterator 接口,好比使用数组解构
或者使用for of
,它接受一个generator函数
class IteratorExec { constructor(){ this.count = 1 } *[Symbol.iterator] = function* (){ yield this.count++; yield this.count++; yield this.count++; } } let obj = new IteratorExec() [...obj] //[1,2,3]
经过添加iterator
使用数据解构,还可使用for of
let values = []; for (let value of obj) { values.push(value) } values; //[1,2,3]
注:ES6中Map,Set,数组和添加了Iterator
接口的对象,拥有Iterator接口.
这不是ES6中的特性,貌似放到了ES7中,能够提早意淫一下以下代码:
for await (const line of readLines(filePath)) { console.log(line); }
在对对象类型进行转换时,会进行一次 toPrimitive
,利用这个Symbol能够改变目标对象的转换规则,改变了之前的 "[object Object]"的固定形式
let obj = { [Symbol.toPrimitive](hint){ switch(hint){ case 'number': return 5; case 'string': return 'string'; case 'default': return 'default' } } } obj+11 // 'default11' obj*2 // 10
这里须要注意+ Number操做是不属于 'number' 的,其余正常,这样就能够定义转对象类型的转换规则了。
在javascript一切皆为对象,而在每一个对象中,都会有一个内部属性[[Class]]表示其对象类型,这在Symbol.toStringTag
,中是能够修改的,也就是说 '[object Object]' 后边的字符串是可自定义的
let obj = { [Symbol.toStringTag]:'custom' } Object.prototype.toString(obj); // [object Object] obj.toString(); //[object custom]
一般咱们使用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不全等于它,那是没法转换的,好比
var n = new Number(); n[Symbol.toStringTag] = 123; n.toString(); // “0”
太幼稚了,太无聊了?,Number私有的toString是直接把[[PrimitiveValue]]
转换成了字符串,这里你们要千万留心,不要误认为全部的对象添加了Symbol.toStringTag
均可以改变,若是当前对象不是纯对象,那么你能够为此对象添加一个 getter
返回对应的类型,这样外部在使用Object...call的时,会获取自定的类型。因此,这须要外部配合使用,你添加getter,人家不call你也是没办法的。
另外Symbol暴露了几种为原生对象定义了一些类型,好比
Math.toString(); //[object Math]
其余类型有 JSON, Promise, Map, TypedArray, DataView, ArrayBuffer, Genterator等等
const object1 = { property1: 42 }; object1[Symbol.unscopables] = { property1: true }; with (object1) { console.log(property1); }
这个功能我感受可用性为0,基本不用,with就是据对禁止的.
对于 instance
运算符,为此操做添加一个钩子,第一参数是instance的左值,咱们能够返回true|false来定义运算符的返回值
var obj1 = { [Symbol.hasInstance](instance){ return Array.isArray(Array) } } class Array1 { static [Symbol.hasInstance](instance) { return Array.isArray(instance); } } [] instance obj1 //true console.log([] instanceof Array1); //true
表示[].concat是否能够展开,默认是true.
let arr = [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]提出来处理 let arr2 = [3,4] arr2[Symbol.isConcatSpreadable] = false; arr.concat(arr2,5); //[[1,2],[3,4],5]
只有在数组中这个symbol属性为false,concat操做时,就不会去解构。那么是否是意味着属性设置为ture,没有意义了?对于数组来讲是的,由于它默认就是true,但是对于类数组对象,它还有一个小功能:
// (续) arr.concat({length:2,0:3,1:4,[Symbol.isConcatSpreadable]:true}) //[1,2,3,4]
一些字符串的操做方法,一块儿都说了,大概都一个意思,就是接受一个对象,而后实现一个钩子处理的函数,并返回其处理结果,它们都是能够接收正则的方法,在ES6以前,若是咱们须要对字符串有比较复杂的操做基本上都是在方法外部的,必
class MyMatch { [Symbol.match](string){return string.indexOf('world') } } 'hello world'.match(new MyMatch()); //6 class MyReplace{ [Symbol.replace](string) { return 'def' } } 'abcdef'.replace(new MyReplace(),'xxx'); //'abcxxx' class mySplit { [Symbol.split](val){ return val.split('-'); } } "123-123-123".split(new mySplit()); //['123','123','123'] class MySearch { constructor(value) { this.value = value; } [Symbol.search](string) { return string.indexOf(this.value); } } var fooSearch = 'foobar'.search(new MySearch('foo')); //0 var barSearch = 'foobar'.search(new MySearch('bar')); //3 var bazSearch = 'foobar'.search(new MySearch('baz')); //-1
sanycIterator
是将来的前景,目前还在草案阶段 unique
性,好比咱们想要在一个对象中添加两个同样的key名,这种需求很不常见,var firstPerson = Symbol("peter"); var secondPerson = Symbol("peter"); var persons = {[firstPerson]:"first", [secondPerson]:"pan"};
Symbol
更多的是在使用和语言自己层面暴露更多的使用方式和特性(on Object type),是的,它只以key的方式存在Object当中,在一切皆为对象中,它为 Next ECMScript Standard 提供了更多的可能性扩展性,这也是ES6中作的最大改变方面之一,虽不经常使用但咱们仍是要总结学习一下,以便在极端状况下应变自如,若是有什么文章中没有涉及到的点,欢迎补充! 注: 尤为是使用场景方面