ES6 新特性梳理系列文章将在本号持续发布,一起查漏补缺学个痛快!若您有遇到其它相关问题,非常欢迎在评论中留言讨论,达到帮助更多人的目的。若感本文对您有所帮助请点个赞吧!
JavaScript 与 ECMAScript
JavaScript 诞生于1995年,设计者是就职于 Netscape 公司的工程师 Brendan Eich。它是一门仅用10天就完成设计的编程语言,但至今为止已对业界保持26年的影响,且势头愈发强劲
1996年 Netscape 将 JavaScript 提交给ECMA,希望它可以成为“标准化一个通用的、跨平台的、中立于厂商的脚本语言的语法和语义标准”,1997年 ECMA 确定将 JavaScript 作为浏览器脚本语言的标准,并为之重命名为 ECMAScript,所以通常来讲我们将 ECMAScript 视为 JavaScript 的标准,而 JavaScript 则是 ECMAScript 的实现及扩展
1997年-1999年连续发布了ES1-ES3发布,2000年开始酝酿新版本的升级内容,中间因标准委员会意见未能达成一致,只做了部分功能的小范围升级及支持,于2009年12月发布了过渡版 ECMAScript 5.0,2011年6月发布 ECMAScript 5.1 并成为 ISO 国际标准
2013年3月 ECMAScript 6 草案冻结,2013年12月 ECMAScript 草案发布,2015年6月 ECMAScript 6 正式通过,成为新的国际标准。ES6 泛指自2015年升级为 ECMAScript 6.0 后的所有子版本,如:ES2015-ES2020,等同于ES6.0-ES6.5,这是 JavaScript 走向企业级编程语言的强势升级
不断升级的标准与实现,对于开发效率及产品质量起到强有力的支撑,接下来我们开始梳理ES6的新特性吧!
什么是 Proxy
Proxy 是ES6提出的一个新的概念,在目标对象上层加了一层“拦截”,所有对这个对象的操作都会先通过这层“拦截”,我们可以根据这层拦截,完成一个过滤和改写的功能,来修改目标对象的某些默认行为,比如取值、设置值等。
创建一个 Proxy
我们通过 Proxy 构造函数来生成 Proxy 实例。Proxy 构造函数包含两个参数, target 表示要拦截的目标对象,handler 用来定义拦截行为。
下面是一个拦截读取目标对象属性行为的例子:
var proxy = new Proxy({}, {
get: function(target, property) {
return 12;
}
});
proxy.a // 12
proxy.b // 12
proxy.c // 12
在上面例子中,我们的第一个参数是一个空对象,即如果没有 Proxy 的介入,操作原来要访问的就是这个对象。
第二个参数就是开始说的 handler,它也是一个对象,里面提供了一系列的处理函数,这些函数表示将要拦截的行为操作。
比如,在这里我们只有一个 get 方法,表示拦截对目标对象属性的访问拦截。get 方法的两个参数分别是目标对象和将要访问的属性。由于我们这里拦截以后返回的值都是固定值 12,所以我们访问任何属性都会得到12。
var target = {};
var handler = {};
var proxy = new Proxy(target, handler);
proxy.a = 'b';
target.a // "b"
并且,我们定义了 Proxy 拦截之后,想要使 Proxy 起作用,我们要针对我们创建出来的 Proxy 实例操作,不可以再对目标对象进行操作。如果我们的 handler 没有设置任何拦截,也就是一个空对象,那么等同于直接通向原对象。
Proxy实例的方法
get(target,propKey,receiver
拦截对象属性的读取,比如proxt.foo和proxy['foo']。
最后一个参数receiver是一个对象,可选,参见下面Reflect.get的部分。
set(target, propKey, value, receiver)
拦截对象属性的设置,比如proxy.foo = v或proxy['foo'] = v,返回一个布尔值。
has(target, propKey)
拦截propKey in proxy的操作,以及对象的hasOwnProperty方法,返回一个布尔值。
deleteProperty(target, propKey)
拦截delete proxy[propKey]的操作,返回一个布尔值。
ownKeys(target)
拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy),返回一个数组。该方法返回对象所有自身的属性,而Object.keys()仅返回对象可遍历的属性。
getOwnPropertyDescriptor(target, propKey)
拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。
defineProperty(target, propKey, propDesc)
拦截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一个布尔值。
preventExtensions(target)
拦截Object.preventExtensions(proxy),返回一个布尔值。
getPrototypeOf(target)
拦截Object.getPrototypeOf(proxy),返回一个对象。
isExtensible(target)
拦截Object.isExtensible(proxy),返回一个布尔值。
setPrototypeOf(target, proto)
拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。
如果目标对象是函数,那么还有两种额外操作可以拦截。
apply(target, object, args)
拦截 Proxy 实例作为函数调用的操作,比如 proxy(...args)、proxy.call(object, ...args)、proxy.apply(...)。
construct(target, args)
拦截 Proxy 实例作为构造函数调用的操作,比如 new proxy(...args)。
Proxy的this指向问题
在 Proxy 代理的情况下,目标对象中的 this 将会指向 Proxy 代理。
var target = {
fn() {
console.log(this === proxy);
}
};
const proxy = new Proxy(target, {});
target.fn() // false
proxy.fn() // true
上面代码中,一旦 proxy 代理 target.fn,后者内部的 this 就是指向 proxy,而不是 target。
什么是 Reflect
Reflect 也是 ES6 新提出的一个新的对象,它主要用来代替原有的 Object 对象,对 Object 对象上的一些方法的返回结果进行优化,让一些命令式操作变成函数行为。
// 老写法-命令式
'a' in Object // true
// 新写法-函数式
Reflect.has(Object, 'a') // true
Reflect对象的方法
Reflect 对象的方法与 Proxy 对象的方法一一对应,只要是 Proxy 对象的方法,就能在 Reflect 对象上找到对应的方法。这就让 Proxy 对象可以方便地调用对应的 Reflect 方法,完成默认行为,作为修改行为的基础。
也就是说,不管 Proxy 怎么修改默认行为,你总可以在 Reflect 上获取默认行为。
var a = new Proxy(obj, {
get(target, name) {
console.log('get', target, name);
return Reflect.get(target, name);
}
});
上面的代码仅仅添加了一个功能,就是在读取目标对象 obj 的属性时候,会输出依据 console.log 语句,其他没有一点变化。return Reflect.get(target,name) 会返回 Proxy 代理前的值,也就是不管Proxy 代理中的get拦截方法如何修改,Reflect.get 方法都会保证默认值的不变更。
Reflect 的方法有如下13个:
Reflect.apply(target,thisArg,args)
Reflect.construct(target,args)
Reflect.get(target,name,receiver)
Reflect.set(target,name,value,receiver)
Reflect.defineProperty(target,name,desc)
Reflect.deleteProperty(target,name)
Reflect.has(target,name)
Reflect.ownKeys(target)
Reflect.isExtensible(target)
Reflect.preventExtensions(target)
Reflect.getOwnPropertyDescriptor(target, name)
Reflect.getPrototypeOf(target)
Reflect.setPrototypeOf(target, prototype)
上面这些方法的作用,大部分与 Object 对象的同名方法的作用都是相同的,而且它与 Proxy 对象的方法是一一对应的。
ES6 新特性梳理系列文章将在本号持续发布,一起查漏补缺学个痛快!若您有遇到其它相关问题,非常欢迎在评论中留言讨论,达到帮助更多人的目的。若感本文对您有所帮助请点个赞吧!
叶阳辉
HFun 前端攻城狮
往期精彩: