ES6 新特性梳理系列丨Proxy 和 Reflect

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 前端攻城狮

往期精彩:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值