Skip to content
本页目录

Proxy 代理

代理(Proxy)是一种可以拦截并改变底层JavaScript引擎操作的包装器,在新语言中通过它暴露内部运作的对象。

介绍

Proxy 主要用于改变对象的默认访问行为,实际上是在访问对象之前增加一层拦截,在任何对对象的访问行为都会通过这层拦截。在这层拦截中,我们可以增加自定义的行为。

JS
var proxy = new Proxy(target,handler)

var obj = new Proxy(obj, {
    // 拦截对象属性的读取,比如proxy.foo和proxy['foo']。
    get(target, propKey, receiver) {
        
    },
    // 拦截对象属性的设置,比如proxy.foo = v或proxy['foo'] = v,返回一个布尔值。
    set(target, propKey, value, receiver) {
        
    },
    // 拦截propKey in proxy的操作,返回一个布尔值。
    has(target, propKey) {

    },
    // 拦截delete proxy[propKey]的操作,返回一个布尔值。
    deleteProperty(target, propKey) {

    },
    // 拦截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一个布尔值。
    defineProperty(target, propKey, propDesc) {

    },
    // 其它
});
var proxy = new Proxy(target,handler)

var obj = new Proxy(obj, {
    // 拦截对象属性的读取,比如proxy.foo和proxy['foo']。
    get(target, propKey, receiver) {
        
    },
    // 拦截对象属性的设置,比如proxy.foo = v或proxy['foo'] = v,返回一个布尔值。
    set(target, propKey, value, receiver) {
        
    },
    // 拦截propKey in proxy的操作,返回一个布尔值。
    has(target, propKey) {

    },
    // 拦截delete proxy[propKey]的操作,返回一个布尔值。
    deleteProperty(target, propKey) {

    },
    // 拦截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一个布尔值。
    defineProperty(target, propKey, propDesc) {

    },
    // 其它
});

this 指向问题

Proxy 代理的情况下,目标对象内部的 this 关键字会指向 Proxy 代理。

JS
const target = {
  m: function () {
    console.log(this === proxy);
  }
};
const handler = {};
const proxy = new Proxy(target, handler);
target.m() // false
proxy.m()  // true
const target = {
  m: function () {
    console.log(this === proxy);
  }
};
const handler = {};
const proxy = new Proxy(target, handler);
target.m() // false
proxy.m()  // true

ProxyObject.defineProperty 区别

  • Proxy 代理整个对象,Object.defineProperty 只能代理某个属性;
  • Proxy 可以监听对象新增属性
  • Proxy 可以监听数组新增修改
  • 若对象内部属性要全部递归代理,Proxy 可以只在调用的时候递归,而 Object.definePropery 需要一次完成所有递归,性能比 Proxy 差;
  • Proxy 不兼容IE,Object.defineProperty 不兼容IE8及以下;
  • Proxy 语法更简单;

问题

1. 为什么 Vue2defineProperty 无法监听数组原生操作?

Vue2 中这是全量劫持了对象,出于性能考虑,没有全量劫持数组,Object.defineProperty可以监听数组操作的,但是存在些缺陷。对数组 length 赋值,或者通过数组下标修改元素的值,是不会响应式的,原因是不会触发 setter 方法。当然,length 属性的 configurablefalse 是一个不可配置的,即不能通过劫持修改与删除。
举个例子:let arr = [1],改变其长度 arr.length = 10,此时我们打印 arr[1] 结果为 undefined,这个值肯定是无法被监听的。