es6学习笔记11--Proxy和Reflect
2016-07-20 10:16
375 查看
Proxy概述
Proxy用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。Proxy可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。
ES6原生提供Proxy构造函数,用来生成Proxy实例。
var proxy = new Proxy(target, handler);
Proxy对象的所有用法,都是上面这种形式,不同的只是
handler参数的写法。其中,
new Proxy()表示生成一个Proxy实例,target参数表示所要拦截的目标对象,
handler参数也是一个对象,用来定制拦截行为。
下面是另一个拦截读取属性行为的例子。
var proxy = new Proxy({}, { get: function(target, property) { return 35; } }); proxy.time // 35 proxy.name // 35 proxy.title // 35
上面代码中,作为构造函数,Proxy接受两个参数。第一个参数是所要代理的目标对象(上例是一个空对象),即如果没有Proxy的介入,操作原来要访问的就是这个对象;第二个参数是一个配置对象,对于每一个被代理的操作,需要提供一个对应的处理函数,该函数将拦截对应的操作。比如,上面代码中,配置对象有一个
get方法,用来拦截对目标对象属性的访问请求。
get方法的两个参数分别是目标对象和所要访问的属性。可以看到,由于拦截函数总是返回
35,所以访问任何属性都得到
35。
注意,要使得Proxy起作用,必须针对Proxy实例(上例是proxy对象)进行操作,而不是针对目标对象(上例是空对象)进行操作。
如果
handler没有设置任何拦截,那就等同于直接通向原对象。
var target = {}; var handler = {}; var proxy = new Proxy(target, handler); proxy.a = 'b'; target.a // "b"
上面代码中,
handler是一个空对象,没有任何拦截效果,访问
handeler就等同于访问
target。
同一个拦截器函数,可以设置拦截多个操作。
var handler = { get: function(target, name) { if (name === 'prototype') return Object.prototype; return 'Hello, ' + name; }, apply: function(target, thisBinding, args) { return args[0]; }, construct: function(target, args) { return args[1]; } }; var fproxy = new Proxy(function(x, y) { return x + y; }, handler); fproxy(1,2); // 1 new fproxy(1,2); // 2 fproxy.prototype; // Object.prototype fproxy.foo; // 'Hello, foo'
下面是Proxy支持的拦截操作一览。
对于可以设置、但没有设置拦截的操作,则直接落在目标对象上,按照原先的方式产生结果。
(1)get(target, propKey, receiver)
拦截对象属性的读取,比如
proxy.foo和
proxy['foo'],返回类型不限。最后一个参数
receiver可选,当
target对象设置了
propKey属性的
get函数时,
receiver对象会绑定
get函数的
this对象。
(2)set(target, propKey, value, receiver)
拦截对象属性的设置,比如
proxy.foo = v或
proxy['foo'] = v,返回一个布尔值。
(3)has(target, propKey)
拦截
propKey in proxy的操作,返回一个布尔值。
has方法可以隐藏某些属性,不被
in操作符发现。
(4)deleteProperty(target, propKey)
拦截
delete proxy[propKey]的操作,返回一个布尔值。
(5)enumerate(target)
拦截
for (var x in proxy),返回一个遍历器。
注意与Proxy对象的
has方法区分,后者用来拦截
in操作符,对
for...in循环无效。
(6)ownKeys(target)
拦截
Object.getOwnPropertyNames(proxy)、
Object.getOwnPropertySymbols(proxy)、
Object.keys(proxy),返回一个数组。该方法返回对象所有自身的属性,而
Object.keys()仅返回对象可遍历的属性。
(7)getOwnPropertyDescriptor(target, propKey)
拦截
Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。
(8)defineProperty(target, propKey, propDesc)
拦截
Object.defineProperty(proxy, propKey, propDesc)、
Object.defineProperties(proxy, propDescs),返回一个布尔值。
(9)preventExtensions(target)
拦截
Object.preventExtensions(proxy),返回一个布尔值。
(10)getPrototypeOf(target)
拦截
Object.getPrototypeOf(proxy),返回一个对象。
(11)isExtensible(target)
拦截
Object.isExtensible(proxy),返回一个布尔值。
(12)setPrototypeOf(target, proto)
拦截
Object.setPrototypeOf(proxy, proto),返回一个布尔值。
如果目标对象是函数,那么还有两种额外操作可以拦截。
(13)apply(target, object, args)
拦截Proxy实例作为函数调用的操作,比如
proxy(...args)、
proxy.call(object, ...args)、
proxy.apply(...)。
apply方法可以接受三个参数,分别是目标对象、目标对象的上下文对象(
this)和目标对象的参数数组。
(14)construct(target, args, proxy)
拦截Proxy实例作为构造函数调用的操作,比如
new proxy(...args)。
construct方法用于拦截
new命令。
Reflect概述
Reflect对象与
Proxy对象一样,也是ES6为了操作对象而提供的新API。
Reflect对象的设计目的有这样几个。
(1) 将
Object对象的一些明显属于语言内部的方法(比如
Object.defineProperty),放到
Reflect对象上。现阶段,某些方法同时在
Object和
Reflect对象上部署,未来的新方法将只部署在
Reflect对象上。
(2) 修改某些Object方法的返回结果,让其变得更合理。比如,
Object.defineProperty(obj, name, desc)在无法定义属性时,会抛出一个错误,而
Reflect.defineProperty(obj, name, desc)则会返回
false。
// 老写法 try { Object.defineProperty(target, property, attributes); // success } catch (e) { // failure } // 新写法 if (Reflect.defineProperty(target, property, attributes)) { // success } else { // failure }
(3) 让
Object操作都变成函数行为。某些
Object操作是命令式,比如
name in obj和
delete obj[name],而
Reflect.has(obj, name)和
Reflect.deleteProperty(obj, name)让它们变成了函数行为。
// 老写法 'assign' in Object // true // 新写法 Reflect.has(Object, 'assign') // true
(4)
Reflect对象的方法与
Proxy对象的方法一一对应,只要是
Proxy对象的方法,就能在
Reflect对象上找到对应的方法。这就让
Proxy对象可以方便地调用对应的
Reflect方法,完成默认行为,作为修改行为的基础。也就是说,不管
Proxy怎么修改默认行为,你总可以在
Reflect上获取默认行为。
Reflect对象的方法
Reflect对象的方法清单如下,共14个。
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.enumerate(target)
Reflect.isExtensible(target)
Reflect.preventExtensions(target)
Reflect.getOwnPropertyDescriptor(target, name)
Reflect.getPrototypeOf(target)
Reflect.setPrototypeOf(target, prototype)
上面这些方法的作用,大部分与
Object对象的同名方法的作用都是相同的,而且它与
Proxy对象的方法是一一对应的。下面是对其中几个方法的解释。
(1)Reflect.get(target, name, receiver)
查找并返回
target对象的
name属性,如果没有该属性,则返回
undefined。
(2)Reflect.set(target, name, value, receiver)
设置
target对象的
name属性等于
value。如果
name属性设置了赋值函数,则赋值函数的
this绑定
receiver。
(3)Reflect.has(obj, name)
等同于
name in obj。
(4)Reflect.deleteProperty(obj, name)
等同于
delete obj[name]。
(5)Reflect.construct(target, args)
等同于
new target(...args),这提供了一种不使用
new,来调用构造函数的方法。
(6)Reflect.getPrototypeOf(obj)
读取对象的
__proto__属性,对应
Object.getPrototypeOf(obj)。
(7)Reflect.setPrototypeOf(obj, newProto)
设置对象的
__proto__属性,对应
Object.setPrototypeOf(obj, newProto)。
(8)Reflect.apply(fun,thisArg,args)
等同于
Function.prototype.apply.call(fun,thisArg,args)。一般来说,如果要绑定一个函数的this对象,可以这样写
fn.apply(obj, args),但是如果函数定义了自己的
apply方法,就只能写成
Function.prototype.apply.call(fn, obj, args),采用Reflect对象可以简化这种操作。
另外,需要注意的是,
Reflect.set()、
Reflect.defineProperty()、
Reflect.freeze()、
Reflect.seal()和
Reflect.preventExtensions()返回一个布尔值,表示操作是否成功。它们对应的Object方法,失败时都会抛出错误。
// 失败时抛出错误 Object.defineProperty(obj, name, desc); // 失败时返回false Reflect.defineProperty(obj, name, desc);
上面代码中,
Reflect.defineProperty方法的作用与
Object.defineProperty是一样的,都是为对象定义一个属性。但是,
Reflect.defineProperty方法失败时,不会抛出错误,只会返回
false。
相关文章推荐
- Cannot launch AVD in emulator
- Android内存泄漏的八种可能
- socket长连接、短连接以及心跳包机制
- Error ITMS-90635 invalid Mach-o format. the mach-o in hundle “*** ***.app/Frameworks/Result.framewo
- matlab一段音频取固定点数目
- android之自定义View和ViewGroup(五)(代码篇,实现类似竖着的ViewPager引导页,竖向引导页)
- jquery的几种异步请求,ajax
- Python包管理工具——Pip方法大全
- http://blog.csdn.net/ameyume/article/details/9958951
- Android ListView的字母排序和过滤搜索功能
- codeforces 699B One Bomb
- linux常用20条命令
- 处理触摸和手势
- java web开发(三) JavaWeb应用开发架构浅谈
- Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境搭建教程
- 第三方库之-MJRefresh
- React 应用的性能优化思路
- TCP的3次握手和4次挥手
- Bundler 的作用及原理
- loadrunner访问https后端服务