js中代理

代理原对象的 get 方法

// 代理
const target = {
  id: "target_id",
  name: "target_name",
};

const handler = {
  get(origin, property, receiver) {
    // 原始对象 (target)
    console.log(origin === target);
    // 访问的属性
    console.log(property);
    // 目标对象
    console.log(receiver === proxy);
    // 原对象的属性 + :proxy
    return origin[property] + ":proxy";
  },
};
const proxy = new Proxy(target, handler);
// 访问属性时回出发 get() 方法
console.log(proxy.name);

捕获器不变式

不可以对只读属性设置代理

// 代理
const target = {};
// 把 name 属性设置只读
Object.defineProperty(target, "name", {
  configurable: false,
  writable: false,
  value: "张三",
});

const handler = {
  get(origin, property, receiver) {
    return origin[property] + ":proxy";
  },
};
const proxy = new Proxy(target, handler);

// 报错: 无法通过代理改变原始对象的 name 属性值
console.log(proxy.name);
// TypeError: 'get' on proxy: property 'name' is a read-only and non-configurable data property on the proxy target but the proxy did not return its actual value (expected '张三' but got '张三:proxy')

可撤销代理

const target = {
  id: "target_id",
  name: "target_name",
};

const handler = {
  get(origin, property, receiver) {
    return origin[property] + ":proxy";
  },
};
// 创建一个可撤销对象
const { proxy, revoke } = Proxy.revocable(target, handler);
console.log(proxy.name);
// 断开代理
revoke();
// 报错:TypeError: Cannot perform 'get' on a proxy that has been revoked
console.log(proxy.name);

十三种捕获器

1. get()

2. set()

// set 捕获器
const target = {};
const proxy = new Proxy(target, {
  // 必须返回布尔值,返回非布尔值会被转义成布尔
  set(origin, property, value, receiver) {
    console.log("set()");
    return Reflect.set(...arguments);
  },
});

proxy.name = "bar";

3. has 捕获器

const proxy1 = new Proxy(target, {
  // 必须返回布尔值,返回非布尔值会被转义成布尔
  has(origin, property) {
    console.log("has()");
    return Reflect.has(...arguments);
  },
});
"name" in proxy1;

4. defineProperty() 捕获器

const proxy2 = new Proxy(target, {
  // 必须返回布尔值,返回非布尔值会被转义成布尔
  defineProperty(origin, property, descriptor) {
    console.log("defineProperty()");
    return Reflect.defineProperty(...arguments);
  },
});
console.log(Object.defineProperty(proxy2, "name", { value: "张三" }));

5. getOwnPropertyDescriptor


const proxy3 = new Proxy(target, {
  getOwnPropertyDescriptor(origin, property) {
    console.log("getOwnPropertyDescriptor()");
    return Reflect.getOwnPropertyDescriptor(...arguments);
  },
});

console.log(Object.getOwnPropertyDescriptor(proxy3, "name"));

6. deleteProperty()

const proxy4 = new Proxy(target, {
  deleteProperty(origin, property) {
    console.log("deleteProperty()");
    return Reflect.deleteProperty(...arguments);
  },
});

console.log(delete proxy4.name);

7. ownKeys()

8. getPrototypeOf()

9. setPrototypeOf()

10. isExtensible()

11. preventExtensions()

12. apply()

13. construct()

常见代理使用场景

跟踪属性访问

通过捕获get、set和has等操作,可以知道对象属性什么时候被访问、被查询。把实现相应捕获器的某个对象代理放到应用中,可以监控这个对象何时在何处被访问

const user = {
  name: "jake",
};

const proxy = new Proxy(user, {
  get(origin, property, receiver) {
    console.log(`get ${property}`);
    return Reflect.get(...arguments);
  },

  set(origin, property, value, receiver) {
    console.log(`set ${property} = ${value}`);
    return Reflect.set(...arguments);
  },
});

proxy.name;
proxy.name = "dc";

隐藏属性

const hiddenAttr = ["name", "idCard"];

const user = {
  name: "dc",
  idCard: "kjhadskfa",
  age: 10,
  sex: 1,
};

const proxy = new Proxy(user, {
  get(origin, property, receiver) {
    if (hiddenAttr.includes(property)) {
      return `${property} 不可访问`;
    }
    return Reflect.get(...arguments);
  },
});

// name  不可访问
console.log(proxy.name);

属性验证

检验值是否合法

user = {};

proxy = new Proxy(user, {
  set(origin, property, value, receiver) {
    if (typeof value !== "number") {
      console.log("非法类型");
      return false;
    }
    return Reflect.set(...arguments);
  },
});

proxy.age = 1;
proxy.age = "2";
console.log(proxy.age);

构造函数参数验证

class User {
  constructor(id) {
    this._id = id;
  }
}

const UserProxy = new Proxy(User, {
  construct(origin, args, newTarget) {
    if (args[0] === undefined || args[0] === null) {
      throw "id 不能为空";
    }
    console.log(args);
    return Reflect.construct(...arguments);
  },
});

new UserProxy(1);
new UserProxy(null);

数据绑定

一个数据发生变化,另外一个同步变化

const userList = [];

function addEvent(newValue) {
  console.log(newValue);
}

const proxy = new Proxy(userList, {
  set(origin, property, value, receiver) {
    const result = Reflect.set(...arguments);
    if (result) {
      addEvent(value);
    }
    return result;
  },
});

proxy.push("张三");

联系方式:dccmmtop@foxmail.com