Proxy,顾名思义就是代理,是指在目标对象前加一层拦截(traps),可用于对访问对象的拦截和过滤。那么这个和上一节讲到的 Decorator 有什么区别呢?

Decorator 是给对象添加一些额外的职责,说白了是保持原有的逻辑不变。而 Proxy 的话更倾向于为对象提供一种代理,从而控制对这个对象的访问。

先来看看基本的实例。

let proxy = new Proxy(target, handler)

target 表示要拦截的对象,可以是 ArrayObjectFunction 或者另一个 Proxyhandler 表示要拦截的行为。而作用的结果则体现在 Proxy 的实例 proxy 上。如果目标没有拦截,就等同于原对象。

对于具体的 API 这里就不做展开了,需要补充的同学可以直接点击查看。无非是监听对象的操作,然后执行相应的目标行为。比如 getsetdeleteProperty 等等。

从一个简单的 get 操作来看看 proxy 实际是如何操作的。在取对象 obj 值的时候,记录一条日志。

let obj = {a: 1}
let proxy = new Proxy(obj, {
    get (target, key, proxy) {
        console.log(`get target key ${target[key]}`)
        return target[key]
    }
})
console.log(proxy.a)

在 Proxy 之前,我们会做类似功能来模拟。通过 globalGetInterceptor 函数来实现具体的对象操作内容。

// babel-plugin-proxy
var defaultHandle = {
    get: function(obj, propName) {
        return obj[propName]
    }
}
var Proxy = function(target, handle) {
    this.target = target
    this.handle = handle

    this.handle.get = this.handle.get || defaultHandle.get
}
Proxy.getTrap = function(propName) {
    return this.handle.get(this.target, propName)
}
function globalGetInterceptor(obj, propName) {
    if (obj instanceof Proxy) {
        return obj.getTrap(propName)
    }
    var value = defaultHandle.get(obj, propName)
    if (typeof value === 'function') {
        return value.bind(this)
    } else {
        return value
    }
}
globalGetInterceptor(proxy, 'a')

实际上,Proxy 的应用场景挺广的。目前看到他们常用有: * 对对象访问的控制 * 降低函数或类的复杂度 * 对资源的检验

通过具体的例子来演示一下。

第一个 console 的例子,稍微扩展一下的话,就可以当做监控来使用。比如在 get 或者 set 方法中加入对应的埋点等操作等等。

还是用上面讲到的 obj 对象。我们也可以做一些校验,访问控制等等。比如我们只允许 obj 属性赋值是数字。不然就报错。例子如下:

let obj = {a: 1}
let proxy = new Proxy(obj, {
    set (target, key, newValue) {
        if (isNaN(newValue)) {
            throw 'Error: isNaN'
        }
        target[key] = newValue
    }
})
proxy.b = 1

还有更高阶的,比如双向绑定等等。不一一例举了。