1
0
mirror of https://github.com/ruanyf/es6tutorial.git synced 2025-05-24 18:32:22 +00:00

docs(proxy): edit proxy

This commit is contained in:
ruanyf 2016-11-27 23:28:35 +08:00
parent 95f8979eb7
commit ba0744a7a4
2 changed files with 89 additions and 17 deletions

View File

@ -1,10 +1,10 @@
# Proxy和Reflect # Proxy Reflect
## Proxy概述 ## Proxy 概述
Proxy用于修改某些操作的默认行为等同于在语言层面做出修改所以属于一种“元编程”meta programming即对编程语言进行编程。 Proxy 用于修改某些操作的默认行为等同于在语言层面做出修改所以属于一种“元编程”meta programming即对编程语言进行编程。
Proxy可以理解成在目标对象之前架设一层“拦截”外界对该对象的访问都必须先通过这层拦截因此提供了一种机制可以对外界的访问进行过滤和改写。Proxy这个词的原意是代理用在这里表示由它来“代理”某些操作可以译为“代理器”。 Proxy 可以理解成在目标对象之前架设一层“拦截”外界对该对象的访问都必须先通过这层拦截因此提供了一种机制可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。
```javascript ```javascript
var obj = new Proxy({}, { var obj = new Proxy({}, {
@ -30,15 +30,15 @@ obj.count = 1
// 2 // 2
``` ```
上面代码说明Proxy实际上重载overload了点运算符即用自己的定义覆盖了语言的原始定义。 上面代码说明Proxy 实际上重载overload了点运算符即用自己的定义覆盖了语言的原始定义。
ES6原生提供Proxy构造函数用来生成Proxy实例。 ES6 原生提供 Proxy 构造函数,用来生成 Proxy 实例。
```javascript ```javascript
var proxy = new Proxy(target, handler); var proxy = new Proxy(target, handler);
``` ```
Proxy对象的所有用法都是上面这种形式不同的只是`handler`参数的写法。其中,`new Proxy()`表示生成一个Proxy实例target参数表示所要拦截的目标对象`handler`参数也是一个对象,用来定制拦截行为。 Proxy 对象的所有用法,都是上面这种形式,不同的只是`handler`参数的写法。其中,`new Proxy()`表示生成一个`Proxy`实例,`target`参数表示所要拦截的目标对象,`handler`参数也是一个对象,用来定制拦截行为。
下面是另一个拦截读取属性行为的例子。 下面是另一个拦截读取属性行为的例子。
@ -54,9 +54,9 @@ proxy.name // 35
proxy.title // 35 proxy.title // 35
``` ```
上面代码中作为构造函数Proxy接受两个参数。第一个参数是所要代理的目标对象上例是一个空对象即如果没有Proxy的介入操作原来要访问的就是这个对象第二个参数是一个配置对象对于每一个被代理的操作需要提供一个对应的处理函数该函数将拦截对应的操作。比如上面代码中配置对象有一个`get`方法,用来拦截对目标对象属性的访问请求。`get`方法的两个参数分别是目标对象和所要访问的属性。可以看到,由于拦截函数总是返回`35`,所以访问任何属性都得到`35` 上面代码中,作为构造函数,`Proxy`接受两个参数。第一个参数是所要代理的目标对象(上例是一个空对象),即如果没有`Proxy`的介入,操作原来要访问的就是这个对象;第二个参数是一个配置对象,对于每一个被代理的操作,需要提供一个对应的处理函数,该函数将拦截对应的操作。比如,上面代码中,配置对象有一个`get`方法,用来拦截对目标对象属性的访问请求。`get`方法的两个参数分别是目标对象和所要访问的属性。可以看到,由于拦截函数总是返回`35`,所以访问任何属性都得到`35`
注意要使得Proxy起作用必须针对Proxy实例上例是proxy对象进行操作而不是针对目标对象上例是空对象进行操作。 注意,要使得`Proxy`起作用,必须针对`Proxy`实例(上例是`proxy`对象)进行操作,而不是针对目标对象(上例是空对象)进行操作。
如果`handler`没有设置任何拦截,那就等同于直接通向原对象。 如果`handler`没有设置任何拦截,那就等同于直接通向原对象。
@ -76,7 +76,7 @@ target.a // "b"
var object = { proxy: new Proxy(target, handler) }; var object = { proxy: new Proxy(target, handler) };
``` ```
Proxy实例也可以作为其他对象的原型对象。 Proxy 实例也可以作为其他对象的原型对象。
```javascript ```javascript
var proxy = new Proxy({}, { var proxy = new Proxy({}, {
@ -121,7 +121,7 @@ fproxy.prototype === Object.prototype // true
fproxy.foo // "Hello, foo" fproxy.foo // "Hello, foo"
``` ```
下面是Proxy支持的拦截操作一览。 下面是 Proxy 支持的拦截操作一览。
对于可以设置、但没有设置拦截的操作,则直接落在目标对象上,按照原先的方式产生结果。 对于可以设置、但没有设置拦截的操作,则直接落在目标对象上,按照原先的方式产生结果。
@ -175,11 +175,11 @@ fproxy.foo // "Hello, foo"
**12apply(target, object, args)** **12apply(target, object, args)**
拦截Proxy实例作为函数调用的操作比如`proxy(...args)``proxy.call(object, ...args)``proxy.apply(...)` 拦截 Proxy 实例作为函数调用的操作,比如`proxy(...args)``proxy.call(object, ...args)``proxy.apply(...)`
**13construct(target, args)** **13construct(target, args)**
拦截Proxy实例作为构造函数调用的操作比如`new proxy(...args)` 拦截 Proxy 实例作为构造函数调用的操作,比如`new proxy(...args)`
## Proxy实例的方法 ## Proxy实例的方法
@ -224,7 +224,7 @@ let obj = Object.create(proto);
obj.xxx // "GET xxx" obj.xxx // "GET xxx"
``` ```
上面代码中拦截操作定义在Prototype对象上面所以如果读取`obj`对象继承的属性时,拦截会生效。 上面代码中,拦截操作定义在`Prototype`对象上面,所以如果读取`obj`对象继承的属性时,拦截会生效。
下面的例子使用`get`拦截,实现数组读取负数的索引。 下面的例子使用`get`拦截,实现数组读取负数的索引。
@ -251,7 +251,7 @@ arr[-1] // c
上面代码中,数组的位置参数是`-1`,就会输出数组的倒数最后一个成员。 上面代码中,数组的位置参数是`-1`,就会输出数组的倒数最后一个成员。
利用Proxy可以将读取属性的操作`get`),转变为执行某个函数,从而实现属性的链式操作。 利用 Proxy可以将读取属性的操作`get`),转变为执行某个函数,从而实现属性的链式操作。
```javascript ```javascript
var pipe = (function () { var pipe = (function () {
@ -280,7 +280,7 @@ var reverseInt = n => n.toString().split("").reverse().join("") | 0;
pipe(3).double.pow.reverseInt.get; // 63 pipe(3).double.pow.reverseInt.get; // 63
``` ```
上面代码设置Proxy以后达到了将函数名链式使用的效果。 上面代码设置 Proxy 以后,达到了将函数名链式使用的效果。
下面的例子则是利用`get`拦截实现一个生成各种DOM节点的通用函数`dom` 下面的例子则是利用`get`拦截实现一个生成各种DOM节点的通用函数`dom`
@ -808,6 +808,77 @@ proxy.foo // TypeError: Revoked
`Proxy.revocable`方法返回一个对象,该对象的`proxy`属性是`Proxy`实例,`revoke`属性是一个函数,可以取消`Proxy`实例。上面代码中,当执行`revoke`函数之后,再访问`Proxy`实例,就会抛出一个错误。 `Proxy.revocable`方法返回一个对象,该对象的`proxy`属性是`Proxy`实例,`revoke`属性是一个函数,可以取消`Proxy`实例。上面代码中,当执行`revoke`函数之后,再访问`Proxy`实例,就会抛出一个错误。
## this 问题
虽然 Proxy 可以代理针对目标对象的访问,但它不是目标对象的透明代理,即不做任何拦截的情况下,也无法保证与目标对象的行为一致。主要原因就是在 Proxy 代理的情况下,目标对象内部的`this`关键字会指向 Proxy 代理。
```javascript
const target = {
m: function () {
console.log(this === proxy);
}
};
const handler = {};
const proxy = new Proxy(target, handler);
target.m() // false
proxy.m() // true
```
上面代码中,一旦`proxy`代理`target.m`,后者内部的`this`就是指向`proxy`,而不是`target`
下面是一个例子,由于`this`指向的变化,导致 Proxy 无法代理目标对象。
```javascript
const _name = new WeakMap();
class Person {
constructor(name) {
_name.set(this, name);
}
get name() {
return _name.get(this);
}
}
const jane = new Person('Jane');
jane.name // 'Jane'
const proxy = new Proxy(jane, {});
proxy.name // undefined
```
上面代码中,目标对象`jane``name`属性,实际保存在外部`WeakMap`对象`_name`上面,通过`this`键区分。由于通过`proxy.name`访问时,`this`指向`proxy`,导致无法取到值,所以返回`undefined`
此外,有些原生对象的内部属性,只有通过正确的`this`才能拿到,所以 Proxy 也无法代理这些原生对象的属性。
```javascript
const target = new Date();
const handler = {};
const proxy = new Proxy(target, handler);
proxy.getDate();
// TypeError: this is not a Date object.
```
上面代码中,`getDate`方法只能在`Date`对象实例上面拿到,如果`this`不是`Date`对象实例就会报错。这时,`this`绑定原始对象,就可以解决这个问题。
```javascript
const target = new Date('2015-01-01');
const handler = {
get(target, prop) {
if (prop === 'getDate') {
return target.getDate.bind(target);
}
return Reflect.get(target, prop);
}
};
const proxy = new Proxy(target, handler);
proxy.getDate() // 1
```
## Reflect概述 ## Reflect概述
`Reflect`对象与`Proxy`对象一样也是ES6为了操作对象而提供的新API。`Reflect`对象的设计目的有这样几个。 `Reflect`对象与`Proxy`对象一样也是ES6为了操作对象而提供的新API。`Reflect`对象的设计目的有这样几个。
@ -914,7 +985,7 @@ Reflect.apply(Math.floor, undefined, [1.75]) // 1
查找并返回`target`对象的`name`属性,如果没有该属性,则返回`undefined` 查找并返回`target`对象的`name`属性,如果没有该属性,则返回`undefined`
如果`name`属性部署了读取函数则读取函数的this绑定`receiver` 如果`name`属性部署了读取函数,则读取函数的`this`绑定`receiver`
```javascript ```javascript
var obj = { var obj = {

View File

@ -106,6 +106,7 @@
- Nicolas Bevacqua, [ES6 Proxies in Depth](http://ponyfoo.com/articles/es6-proxies-in-depth) - Nicolas Bevacqua, [ES6 Proxies in Depth](http://ponyfoo.com/articles/es6-proxies-in-depth)
- Nicolas Bevacqua, [ES6 Proxy Traps in Depth](http://ponyfoo.com/articles/es6-proxy-traps-in-depth) - Nicolas Bevacqua, [ES6 Proxy Traps in Depth](http://ponyfoo.com/articles/es6-proxy-traps-in-depth)
- Nicolas Bevacqua, [More ES6 Proxy Traps in Depth](http://ponyfoo.com/articles/more-es6-proxy-traps-in-depth) - Nicolas Bevacqua, [More ES6 Proxy Traps in Depth](http://ponyfoo.com/articles/more-es6-proxy-traps-in-depth)
- Axel Rauschmayer, [Pitfall: not all objects can be wrapped transparently by proxies](http://www.2ality.com/2016/11/proxying-builtins.html)
## Symbol ## Symbol