Proxy 对象使用

Proxy 语法

const p = new Proxy(targer, handler);
  • targer 参数说明

    要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)

  • handler 参数说明

    此函数的功能主要设置对代理对象属性操作的方法,参数类型为对象。

Proxy 对象中 handler 对象所拥有的方法

  • applay(目标对象, 被调用时的上下文对象, 被调用时的参数数组)

    拦截函数的调用。这里需要注意目标对象必须是函数。假如这里目标对象设置为对象的话,程序在运行的时候会报错。

  • construct(目标对象, constructor 的参数列表, 最初被调用的构造函数)

    用于拦截 new 操作符。为了使 new 操作符在生成的 Proxy 对象上生效,用于初始化代理的目标对象自身必须具有 [[Construct]] 内部方法(即 new target 必须是有效的)。

  • defineProperty(目标对象, 待检索其描述的属性名, 待定义或修改的属性的描述符)

    用于拦截对象的 Object.defineProperty() 操作。

  • deleteProperty(目标对象, 待删除的属性名)

    用于拦截对对象属性的 delete 操作。

  • get(目标对象, 被获取的属性名, Proxy 或者继承 Proxy 的对象)

    用于拦截对象的读取属性操作。

  • getOwnPropertyDescriptor(目标对象, 返回属性名称的描述)

    此方法是 Object.getOwnPropertyDescriptor() 的钩子函数。

  • getPrototypeOf(被代理的目标对象)

    当读取代理对象的原型时,该方法就会被调用。

  • has(目标对象, 需要检查是否存在的属性)

    in 操作符的代理方法。

  • isExtensible(目标对象)

    用于拦截对对象的 Object.isExtensible()。

  • ownKeys(目标对象)

    用于拦截 Reflect.ownKeys()。

  • preventExtensions()所要拦截的目标对象

    方法用于设置对 Object.preventExtensions()的拦截。

  • set(目标对象, 将被设置的属性名或 Symbol)

    设置属性值操作的捕获器。

  • setPrototypeOf(被拦截目标对象, 对象新原型或为 null)

    主要用来拦截 Object.setPrototypeOf()

Proxy 对象代码例子

通过上述的方法描述,我们结合如下几个方法来理解方法的使用。

1、基础例子

const handler = {
  get: (obj, prop) => {
    return prop in obj ? obj[prop] : 37;
  },
};

const _prosyObj = new Proxy({}, handler);

_prosyObj.a = 1;
_prosyObj.b = 2;

console.log(_prosyObj.a);
console.log(_prosyObj.b);
console.log(_prosyObj.c);

2、检测赋值

const validator = {
  set: (obj, pro, value) => {
    if (pro === "age") {
      if (!Number.isInteger(value)) {
        throw new TypeError("The age is not an integer");
      }

      if (value > 200) {
        throw new RangeError("The age seems invalid");
      }
    }

    obj[pro] = value;
    return true;
  },
};

const _prosyObj = new Proxy({}, validator);

_prosyObj.age = 123;
_prosyObj.age = 300; // RangeError: The age seems invalid
_prosyObj.age = "jhkdjs"; // TypeError: The age is not an integer

3、扩展构造函数

function extend(sup, base) {
  let descriptor = Object.getOwnPropertyDescriptor(
    base.prototype,
    "constructor"
  );

  base.prototype = Object.create(sup.prototype);

  let handler = {
    construct: function (target, args) {
      var obj = Object.create(base.prototype);
      this.apply(target, obj, args);
      return obj;
    },

    apply: function (target, that, args) {
      sup.apply(that, args);
      base.apply(that, args);
    },
  };

  let proxy = new Proxy(base, handler);
  descriptor.value = proxy;
  Object.defineProperty(base.prototype, "constructor", descriptor);

  return proxy;
}

const Person = function (name) {
  this.name = name;
};

const Boy = extend(Person, function (name, age) {
  this.age = age;
});

Boy.prototype.sex = "M";

const Peter = new Boy("Peter", 13);
console.log(Peter.sex); // "M"
console.log(Peter.name); // "Peter"
console.log(Peter.age); // 13

4、修改及附加属性

const _targetObj = {
  books: [
    "C++ Primer",
    "Javascript权威指南",
    "JavaScript高级程序设计",
    "编码:隐匿在计算机软硬件背后的语言",
  ],
};

const handler = {
  get: (target, prop) => {
    // 灵活扩展一个属性,通过此属性获取最后一个元素
    if (prop === "latestBrowser") {
      return target.books[target.books.length - 1];
    }

    return target[prop];
  },

  set: (target, prop, value) => {
    // 灵活扩展一个属性,把设置的值添加对代理对象中
    if (prop === "latestBrowser") {
      target.books.push(value);
      return;
    }

    // 如果不是数组,则进行转换
    if (typeof value === "string") {
      value = [value];
    }

    // 默认行为是保存属性值
    target[prop] = value;

    // 表示成功
    return true;
  },
};

const _proxyObj = new Proxy(_targetObj, handler);

console.log(_proxyObj.books); // ['C++ Primer','Javascript权威指南','JavaScript高级程序设计','编码:隐匿在计算机软硬件背后的语言']

_proxyObj.books = "重构改善既有代码的设计";
console.log(_proxyObj.books); // [ '重构改善既有代码的设计' ]

_proxyObj.latestBrowser = "代码整洁之道";
console.log(_proxyObj.books); // [ '重构改善既有代码的设计', '代码整洁之道' ]
console.log(_proxyObj.latestBrowser); // 代码整洁之道

5、通过属性查找数组中的特定对象

const _books = [
  { id: 1, name: "疯狂设计模式", price: 90, autor: "Tom" },
  { id: 2, name: "怎么编写Bug", price: 100, autor: "Mary" },
  { id: 3, name: "Bug修改技术", price: 19, autor: "Bean" },
  { id: 4, name: "Bug修改技术", price: 29, autor: "Jack" },
];

const handler = {
  get: (obj, prop) => {
    // 判断是否为数组下标或者数组相关属性
    if (prop in obj) {
      return obj[prop];
    }

    let result,
      names = {};

    // 重新整理出带有name属性的数据
    for (let product of obj) {
      if (product.name === prop) {
        result = product;
      }

      if (names[product.name]) {
        names[product.name].push(product);
      } else {
        names[product.name] = [product];
      }
    }

    // 通过 name 获取 product
    if (result) {
      return result;
    }

    // 通过 name 获取 products
    if (prop in names) {
      return names[prop];
    }

    // 获取 product name
    if (prop === "name") {
      return Object.keys(names);
    }

    return undefined;
  },
};

const _proxyObj = new Proxy(_books, handler);

console.log(_proxyObj[0]); // { id: 1, name: '疯狂设计模式', price: 90, autor: 'Tom' }
console.log(_proxyObj["怎么编写Bug"]); // { id: 2, name: '怎么编写Bug', price: 100, autor: 'Mary' }
console.log(_proxyObj.name); // [ '疯狂设计模式', '怎么编写Bug', 'Bug修改技术' ]
console.log(_proxyObj.price); // undefined
console.log(_proxyObj.Bug修改技术); // { id: 4, name: 'Bug修改技术', price: 29, autor: 'Jack' }
console.log(_proxyObj.length); // 4

总结

通过上述的例子,可以看出 Proxy 对象的作用主要是拦截代理对象的某些属性和运行期间的方法,从而对原对象进行功能上的扩展。

在日常业务代码编写时,我们遇到不能修改某个对象或者要对某个数组进行扩展方法时,我们就可以采用 Proxy 对象来进行扩展,从而不会污染之前的业务对象和方便对数组进行方法扩展。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值