# Proxy 对象

# 概述

  • Proxy也就是代理,在目标对象前架设一层“拦截”,外界对该对象访问时,必须先通过这些拦截。
  • ES6原生提供Proxy构造函数,用来生产Proxy实例。
var proxy = new Proxy(target, handler)
  • target代表要拦截的目标对象,handler也是一个对象,用于定制拦截的行为。

# Proxy 支持拦截的13种操作

  • get(target, propKey, receiver):拦截对象属性的读取,比如proxy.fooproxy['foo']
  • set(target, propKey, value, receiver):拦截对象属性的设置,比如proxy.foo = vproxy['foo'] = v
  • has(target, propKey):拦截propKey in proxy操作,返回一个布尔值。
  • deleteProperty(target, propKey):拦截delete proxy[propKey], 返回一个布尔值
  • apply(target, ctx, args):拦截Proxy实例作为函数调用的操作,如:proxy(...args)proxy.call(ctx, ...args)proxy.apply(...).
  • ownkeys(target)
  • getOwnPropertyDescriptor(target, propKey)
  • defineProperty(target, propKey, propDesc)
  • preventExtensions(target)
  • getPrototypeOf(target)
  • isExtensible(target)
  • setPrototypeOf(target, proto)
  • construct(target, args)

# 特殊说明

  • get(target, propKey, receiver)
    1. target为目标对象;propKey操作的属性名称;receiverproxy实例本身(严格地说,是操作行为所针对的对象)(可选)
    2. 若属性设置为不可配置(configurable),且不可写(writable),则Proxy不能去修改该属性的值,否则通过Proxy对象访问属性会报错。
const proxy = new Proxy({}, {
  get: function(target, key, receiver) {
    return receiver;
  }
});

// proxy 对象的 a 属性是有 proxy 对象提供的,所以 receiver 指向 proxy 对象
proxy.a === proxy // true

// d 对象没有属性 a, 所以读取 a 时会去 d 的原型 proxy 对象找,
// 此时 receiver 指向 d 对象,代表原始的读操作所在的那个对象。
const d = Object.create(proxy);
d.a === d // true
var target = Object.defineProperty( {}, 'foo',
  {
    value: 123,
    writable: false,
    configurable: false
  }
 );

var handler = {
  get(target, propKey) {
    // 1. 此时把原来的值123 修改成了 'abc', 所以会报错
    return 'abc';
    // 2. 而这个值一样,相当于没有修改,所以不会报错
    // return 123;
    // 3. Reflect.get确保完成了原有的行为,值没变,所以不会报错
    // return Reflect.get(target, propKey)
  }
};
var proxy = new Proxy(target, handler);
target.foo
  • set(target, propKey, value, receiver)
    • receiver代表 Proxy实例本身(原始的操作行为所在的那个对象)
    • 严格模式下, set代理必须返回true(有值就行),否则就会报错。
const handler = {
  set: function(obj, prop, value, receiver) {
    obj[prop] = receiver;
  }
};
const proxy = new Proxy({}, handler);
const myObj = {};
// 设置 myObj 的原型为 proxy
Object.setPrototypeOf(myObj, proxy);

// 此时 myObj 对象上没有属性 foo, 所以会去原型对象上 proxy 找,此时触发 set 方法,
myObj.foo = 'bar';
// 此时 receiver 指向原始赋值行为所在的对象 myObj。
myObj.foo === myObj // true

// 若上方 改为
// const myObj = { foo: 1 }
// 则因为 myObj 有属性 foo, 所有不会触发 set 方法, 所以
// myObj.foo === myObj // false
  • has(target, propKey),返回一个布尔值
    • 拦截propKey in target, 但是不会拦截for...in操作。
    • 拦截的是HasProperty操作,而不是HasOwnProperty操作,即不去判断是否是对象自身的属性,还是继承的属性。
    • 目标对象的某个属性若不可配置configurable或者目标对象不可扩展Object.preventExtensions(target), 则此时拦截对应的属性会报错。(不可配置,不可扩展还需详细了解)
  • deleteProperty(target, propKey),返回一个布尔值
    • 在具体执行删除前抛出错误或者返回false,则删除不成功。
    • 目标对象的某个属性若不可配置configurable, 则此时拦截对应的属性会报错。
  • apply(target, ctx, args),target此时是一个函数
最后一次修改时间: 9/28/2020, 11:37:23 AM