ES6 proxy

133 阅读4分钟

Proxy

ProxyES6 中新增的一个功能,用于创建一个对象的代理,从而实现对对象的拦截、修改、增加等操作。下面详细介绍 Proxy 的使用方法。

基本使用

我们可以通过 Proxy(target, handler) 方法来创建一个代理对象,其中 target 是需要被代理的对象,handler 是一个对象,用于定义代理的行为。下面是一个简单的例子:

javascriptCopy code
let obj = new Proxy({}, {
  get: function(target, propKey) {
    console.log('get ' + propKey);
    return target[propKey];
  },
  set: function(target, propKey, value) {
    console.log('set ' + propKey + ' = ' + value);
    target[propKey] = value;
    return true;
  }
});

上面的代码创建了一个空对象的代理 obj,并定义了代理的行为。当我们通过 obj.xxx 来访问或修改对象的属性时,代理会拦截这个操作,并在控制台打印相应的信息。

javascriptCopy code
obj.foo = 'bar';
// set foo = bar
console.log(obj.foo);
// get foo
// bar

get

get 方法用于拦截对象属性的读取操作,它接受两个参数:

  • target:需要被代理的对象;
  • propKey:属性名。 当我们通过 obj.xxx 来访问对象的属性时,如果该属性存在,get 方法会被调用,并返回属性的值。如果该属性不存在,get 方法会返回 undefined
javascriptCopy code
let obj = new Proxy({foo: 'bar'}, {
  get: function(target, propKey) {
    console.log('get ' + propKey);
    return target[propKey];
  }
});
console.log(obj.foo);
// get foo
// bar
console.log(obj.baz);
// get baz
// undefined

set

set 方法用于拦截对象属性的设置操作,它接受三个参数:

  • target:需要被代理的对象;
  • propKey:属性名;
  • value:属性值。 当我们通过 obj.xxx = yyy 来修改对象的属性时,set 方法会被调用,并设置属性的值。如果 set 方法返回 false,表示修改失败。
javascriptCopy code
let obj = new Proxy({}, {
  set: function(target, propKey, value) {
    console.log('set ' + propKey + ' = ' + value);
    target[propKey] = value;
    return true;
  }
});
obj.foo = 'bar';
// set foo = bar
obj.foo = 'baz';
// set foo = baz

has

has 方法用于判断对象是否有某个属性,它接受两个参数:

  • target:需要被代理的对象;
  • propKey:属性名。 当我们通过 propKey in obj 来判断对象是否有某个属性时,has 方法会被调用,并返回一个布尔值。
javascriptCopy code
let obj = new Proxy({foo: 'bar'}, {
  has: function(target, propKey) {
    console.log('has ' + propKey);
    return propKey in target;
  }
});
console.log('foo' in obj);
// has foo
// true
console.log('baz' in obj);
// has baz
// false

deleteProperty

deleteProperty 方法用于拦截对象属性的删除操作,它接受两个参数:

  • target:需要被代理的对象;
  • propKey:属性名。 当我们通过 delete obj.xxx 来删除对象的属性时,deleteProperty 方法会被调用,并删除相应的属性。如果 deleteProperty 方法返回 false,表示删除失败。
javascriptCopy code
let obj = new Proxy({foo: 'bar'}, {
  deleteProperty: function(target, propKey) {
    console.log('delete ' + propKey);
    delete target[propKey];
    return true;
  }
});
delete obj.foo;
// delete foo

apply

apply 方法用于拦截函数的调用,它接受三个参数:

  • target:需要被代理的函数;
  • thisArg:函数的上下文对象;
  • argumentsList:函数的参数列表。 当我们通过 obj.xxx() 来调用对象的方法时,apply 方法会被调用,并执行相应的操作。如果 apply 方法返回的是一个函数,那么这个函数将被作为调用结果返回。
javascriptCopy code
let obj = new Proxy(function(x, y) {
  return x + y;
}, {
  apply: function(target, thisArg, argumentsList) {
    console.log('apply ' + argumentsList);
    return target.apply(thisArg, argumentsList);
  }
});
console.log(obj(1, 2));
// apply 1,2
// 3

construct

construct 方法用于拦截 new 操作符,它接受两个参数:

  • target:需要被代理的函数;
  • argumentsList:构造函数的参数列表。 当我们通过 new obj() 来创建对象时,construct 方法会被调用,并执行相应的操作。如果 construct 方法返回的是一个对象,那么这个对象将被作为实例返回。
javascriptCopy code
let obj = new Proxy(function(x, y) {
  this.x = x;
  this.y = y;
}, {
  construct: function(target, argumentsList) {
    console.log('construct ' + argumentsList);
    return new target(...argumentsList);
  }
});
let instance = new obj(1, 2);
console.log(instance.x, instance.y);
// construct 1,2
// 1 2

实战应用

Proxy 的应用非常广泛,可以用来实现数据的双向绑定、实现数据的缓存、实现数据的节流等功能。下面是一个简单的例子,用来实现数据的缓存。

javascriptCopy code
function getData(key) {
  return localStorage.getItem(key);
}
function setData(key, value) {
  localStorage.setItem(key, value);
}
let cache = new Proxy({}, {
  get: function(target, propKey) {
    console.log('get ' + propKey);
    return getData(propKey);
  },
  set: function(target, propKey, value) {
    console.log('set ' + propKey + ' = ' + value);
    setData(propKey, value);
    return true;
  }
});
cache.foo = 'bar';
// set foo = bar
console.log(cache.foo);
// get foo
// bar

上面的代码通过 Proxy 实现了一个简单的缓存功能,每当我们通过 cache.xxx 来访问或修改缓存时,代理会拦截相应的操作,并将数据存储到本地存储中。