ES6 的新特性小记。目前简单测试了一下,在 Firefox 最新版本 Night39 里面支持的还不错,其它浏览器都只支持一小部分。
ES6的一些新鲜类型介绍
Typed Array/Data View系列
在Web方面,这个暂时想不出使用场景,应该是给nodejs或native等去用的,就不多了,大致如下
- Int8Array / Uint8Array / Unit8ClampedArray / DataView(Int8 / Unit8)
- Int16Array / Uint16Array / DataView(Int16 / Unit16)
- Float32Array / Float64Array / DataView(Float32 / Float64)
TypedArray相关的方法,from和of为静态方法,可以使用 Int8Array.from(args)
来调用。
TypedArray和普通Array的方法区别不大,新增了一些。由于用的不多就不说了,看方法名去理解吧,如下:
- (static) from
- (static) of
- subarray
- join
- indexOf
- lastIndexOf
- slice
- every
- filter
- forEach
- map
- reduce
- reduceRight
- reverse
- some
- sort
- copyWithin
- find
- findIndex
- fill
- keys
- values
- etries
Map && WeakMap
有了Map对象,很多数据存放会方便清晰了。
WeakMap,从名字来看就是弱引用的Map,个人理解是如果值没有的了情况下,就会回收掉?比如$(element).remove()?
//新建一个Map对象
var map = new Map();
//也可以定义初始数据
var map = new Map([ [ key1, value1 ], [ key2, value2 ] ] );
//注意参数是一个array [] 里面有 [key,value] 这样的数组
//WeakMap的创建方式和Map一样
var map = new WeakMap();
//WeakMap的key必须为对象或者null,所以不能为字符 map.set( 'key', 'value' )会报错
常用属性方法,看名字就知道的方法不过多解释。
- get( key )
- set( key, value ) //说明set会返回this,就像append一样可以用链式不停set
- has( key )
- size
- delete( key )
- clear()
- forEach( function(value , key , map){} ) // 说明:三个参数顺序分别为值、键、map本身
- keys()
- values()
- entries()
说明:WeakMap 仅有 get
/ set
/ has
/ delete
/ clear
五个方法 及 size
属性
Set && WeakSet
有了Map的地方肯定也会有Set
//新建一个Set对象
var set = new Set();
//也可以定义初始数据
var set = new Set( [ object1, object2 ] );
//WeakSet与Set创建方式一样
var set = new WeakSetp();
//WeakSet里面的成员必须是object类型
常用方法
- add( object )
- has( object )
- delect( object )
- clear()
- size
- forEach()
- keys()
- values()
- entries()
说明:WeakSet 仅有 add
/ delete
/ has
三个方法 及 size
属性
Proxy 代理对象
代理对象看上去不错,使用方法也比自己去实现一个代理模式要简单。
比对于比较较复杂的页面的话(也可以是其它js程序),已经存在一些已知的使用场景了。如果刚好页面上有这种页面逻辑,可以试试把你原先写的一套代理模式的JavaScript代码替换为Proxy对象实现. 当然,以前有头疼的场景也可以考虑考虑~。
使用过程:
必须有一个需要被代理的对象
使用被代理的对象new一个代理
根据功能需求实现各种handder,一般来说,配置至少得实现
get
这个handder.使用代理对象进行访问
语言难以表达清楚,看代码就是了,如果自己在编辑器里面敲一遍,比看一启启遍的效果高出99999999999倍。
//被代理的源对象,不提供直接访问
var sourceObject = {
field1 : 'The public field',
field2 : 'The private field',
method1 : function(){
return 'Source Method';
}
}
//代理对象,提供访问,可以代理源对象的能力,也可以自行扩展
var proxyObject = new Proxy( sourceObject, {
/**
* get Handder 一般都是要实现的,不然就没什么意义
*
*@param sourceObject 被代理的对象
*@param properties 属性名称
*@param proxy 代理对本身,即proxyObject
*
* 说明:
* 每个Handder里面的的第一个参数和最后一个参数都是 sourceObject 和 proxyObject
* proxyObject一般来说没什么意义通常不传
* 中间的参数取决于handder本身响应的方法。
*/
get : function( source, properties, proxy ) {
console.log('Call Proxy Get Handder');
switch (properties){
// 假设不让用户访问原对象的field2
case 'field2':
return 'Not allow access to field2';
break;
// field3为代理定义的一个属性
case 'field3':
return 'Hello Proxy Custom Field';
break;
//这是个错误的,会陷入死循环,这里写代码说明一下,不能这么写
case 'method2':
console.log(proxy);
return proxy.method2(source);
// 自定义方法的话,也写在getHandder里面
case 'method3':
return () => 'Hello Proxy Custom Function';
// 默认代理源对象属性,没有就返回错误提示
default :
if(sourceObject.hasOwnProperty(properties)){
return sourceObject[properties];
}else{
return 'Not Found the ' + properties;
}
break;
}
},
//这是个错误的,会陷入死循环,这里写代码说明一下,不能这么写
//Proxy一般来说只实现各不不同的haddder,如果需要自定义方法
method2:function(source){
return source.method1;
},
// set Handder 返回一个boolean(最好是永远返回true不然会抛异常,看具体场景吧)
// 下面的handder的示例代码就不写了,有兴趣请自行研究
set: function( source, key, value){
console.log('Call Proxy Set Handder');
return Math.random()*10>5;
},
/*
// 下面的这些handder 一般不怎么用的上,不说了。
has: function( source, key){
console.log('Call Proxy Has Handder');
},
deletePropery:function( source, name, p ){
console.log('Call Proxy DeletePropery Handder');
},
getOwnPropertyDescriptor:function( source, name, p ){
console.log('Call Proxy GetOwnPropertyDescriptor Handder');
},
defineProperty:function( source, name, p ){
console.log('Call Proxy DefineProperty Handder');
},
getPrototypeOf:function( source, name, p ){
console.log('Call Proxy GetPrototypeOf Handder');
},
setPrototypeOf:function( source, name, p ){
console.log('Call Proxy SetPrototypeOf Handder');
},
isExtensible:function( source, name, p ){
console.log('Call Proxy IsExtensible Handder');
},
preventExtensions:function( source, name, p ){
console.log('Call Proxy PreventExtensions Handder');
},
enumerate:function( source, name, p ){
console.log('Call Proxy Enumerate Handder');
},
ownKeys:function( source, name, p ){
console.log('Call Proxy OwnKeys Handder');
},
apply:function( source, name, p ){
console.log('Call Proxy Apply Handder');
},
construct:function( source, name, p ){
console.log('Call Proxy Construct Handder');
}
*/
} );
//get Handder
console.log(proxyObject.field1); //The public field
console.log(proxyObject.field2); //Not allow access to field2
console.log(proxyObject.field3); //Hello Proxy Custom Field
console.log(proxyObject.field4); //Not Found the field4
console.log(proxyObject.method1()); //Source Method
// console.log(proxyObject.method2); // method2会产生死循环,慎之
console.log(proxyObject.method3()); //Source Method
try{
console.log(proxyObject.method4()); //TypeError: proxyObject.method4 is not a function
}catch(e){
console.error('Error :' + e);
}
//set Handder
try{
proxyObject.CustomField1 = 123;
}catch(e){
console.error('set Error :' + e);
}
try{
proxyObject['CustomField2'] = 456;
}catch(e){
console.error('set Error :' + e);
}try{
proxyObject['CustomMethod3'] = function(){
};
}catch(e){
console.error('set Error :' + e);
}
基本的Proxy,加上本身JavaScript的灵活性,如果深度使用是可以发生很神奇的结果的。当然目前不知了,个人很看好Proxy,可能在未来的很多前框架里面Proxy会成为重要的一部分。
目前来说,本人对ES6的Proxy有一个建议和一个意见。
建议:太过简单,如果没有特殊说明,可能某个动作引发的现象你都不好追踪代码。所以各位写Proxy时一定要写好注释,必须说明变量的用途。否则后果会很严重。
意见:一个Proxy,只能代理一个对象,使用的时候会受到很多限制。因为有些比较灵活的场景经常需要在使用的多个对象之间进行切换。如果能像require那样,接受多个对象参数,就完美了。
Reflect
在各种编程语言中,基本都有反射机制,可让用户的代码高度灵活化,主要功能就是让程序不用预先知道需要怎么去做,在做的过程中去根据过程去执行过程,虽然目前JavaScript不直接支持反射,但基本各路都已经使用了各种判断逻辑去支持反射了。ES6直接对反射进行支持,提供了相应API,效果就更上一层楼了。
Reflect对象,ES6新的内置对象,提供的方法大多为静态方法,看上去和Math差不多的uitls对象。基本方法也都清晰明了(对于任何在其它语言里面使用过反射相关API的人),我就不写注释了。
- Reflect.get
- Reflect.set
- Reflect.has
- Reflect.deleteProperty
- Reflect.getOwnPropertyDescriptor
- Reflect.defineProperty
- Reflect.getPrototypeOf
- Reflect.setPrototypeOf
- Reflect.isExtensible
- Reflect.preventExtensions
- Reflect.enumerate
- Reflect.ownKeys
- Reflect.ownKeys, symbol order
- Reflect.apply
- Reflect.construct
- Reflect.construct, new.target
怎么说呢,反射是不错的API,由于JavaScript本身是弱类型,所以除了一些较有用的API方便使用以外,没有像其它语言里面的反射机制那么用处大。
比如一个常用的场景是看一个对象是否有某个属性或方法,然后使用这个属性方法,如果使用新增的反射API的话,代码可能是这样
var fn = Reflect.has(obj, 'methodName')?Reflect.get(obj, 'methodName'):Reflect.get(self, 'defaultMethod');
fn.call();
在JavaScript里面,不使用反射API也可以实现
var fn = obj.hasOwnProperty('methodName') ? obj['methodName']:obj['defaultMethod'];
fn.call();
个人认为不使用反射,代码也不没有看不明白(反而比用反射本身清晰些),也看不出会有什么性能方面的问题,以目前个人的鼠目寸光来看,反射最大的作用就是当个util工具包,需要的时候拿过来用一下就行了,做为核心驱动功能的话,就看开发者的个人意愿了。
Promise
用于异步并发任务,应该是有几套规范的,可以自己去了解下。
jQuery自实现一套,如果原生支持,当然是更好,ES6定义的应该也是Promise A规范,貌似还多支持的更多。
比如Ajax在jQuery里面底层是使用Promise机制进行管理。实际上,只要运行较久的任务都可以用Promise进行管理
Symbol
Symbol 为ES6新定义的一种数据类型,再次说明和基本的 “objcet” 不是一种数据类型。之前说的新增的Map,Int8Array之类的类型,实际上还是 “object” 类型。使用typeof打出来是 “symbol”.
symbol类型不能与 string number类型相加(在JavaScript里面经常使用这种加法,得一个字符串)。但symbol类型可以转换成一个string类型后,再与string相加。
symbol类型如果用在对象里面的话,就是私有类型,外面访问不了,JavaScript之前一直无法用私有,现在可通过Symbol解救了。但个人感觉还是什么用,一是麻烦,二是在class或其它结构体中中不支持。想在class 里面定义一个私有的方法较难(只能做为结构体扩展,不能做为原生结构体)。
var key = Symbol("key");
function Module() {
this[key] = 'Private Data';
}
Module.prototype = {
get: function() {
return this[key]
}
set: function(data){
this[key] = data;
}
};
var m = new Module()
console.log(m.get());
console.log(m.key); //undefined
m.set('The new Data');
console.log(m.get());
console.log(m.key); //undefined
另外,Symbol自定义了一些常量,称为 “well-known symbols”,可以在对应的属性里面设置之后,相应动作就会触发,
- hasInstance 触发 [Objcet] instanceof
- isConcatSpreadable 触发 [Array] cancat
- iterator 触发 [Object] iterator
- species 触发 [RegExp] .$n操作
- toPrimitive 触发 [Object] 计算
- toStringTag触发 [Object] toString
- unscopables 触发 [Object] with操作
- toPrimitive 触发 [Object] 计算
- species 触发 [RegExp] .$n操作
- iterator 触发 [Object] iterator
- isConcatSpreadable 触发 [Array] cancat
个人觉得理解有些难,而且实际做用不是太大,且方法极不规范。所以不暂不关注了。比如使用toStringTag符修改toString时可以定义为一个字符串,而其实的Symbol,确需要定义一个方法
var a = {} ;
console.log(a.toString()); // "[object Object]"
a.toString = function(){return "ddd"}
console.log(a.toString()); // "ddd"
a[Symbol.toStringTag] = "xdnote.com";
console.log(a + ''); // "ddd xdnote"
小结:
功能性并非增加太多,灵活性增加了不少,如果各端进行一些优化,提升性能就更加不错了,不过目前看来,普及还遥遥无期~。