浅析 Proxy 和 Reflect

Proxy是ES6中提供的新的API,可以用来定义对象各种基本操作的自定义行为 (在文档中被称为traps,我觉得可以理解为一个针对对象各种行为的钩子),拿它可以做很多有意思的事情,在我们需要对一些对象的行为进行控制时可以使用 Reflect 将变得非常有效。

定义

ProxyReflect 都属于 JavaScript 标准内置对象 的 反射 分类,不同的是:

Proxy 对象用于定义基本操作的自定义行为(如属性查找、赋值、枚举、函数调用等)。

Proxy 是 一个 function

Reflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法。这些方法与proxy handlers的方法相同。Reflect不是一个函数对象,因此它是不可构造的。

Reflect 是 一个对象

术语

包含捕捉器(trap)的占位符对象,可译为处理器对象。

  • traps(各种行为的代理)

提供属性访问的方法。这类似于操作系统中捕获器的概念。

  • target

被 Proxy 代理虚拟化的对象。它常被作为代理的存储后端。根据目标验证关于对象不可扩展性或不可配置属性的不变量(保持不变的语义)。

注:

被代理的对象, 都是浅拷贝(及传址)。

语法

const p = new Proxy(target, handler)

参数

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

  • handler
    一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为。

适用场景

  • 代理getter
const handler = {
    get(obj, prop) {
        return prop in obj ? Reflect.get(obj, prop) : 37;  
    }
};

const p = new Proxy({}, handler);
p.a = 1;
p.b = undefined;

console.log(p.a, p.b);      // 1, undefined
console.log('c' in p, p.c); // false, 37

通过 反射对象 来获取属性,如果没有则返回 37

  • 无操作转发代理
let target = {};
let p = new Proxy(target, {});

p.a = 37;   // 操作转发到目标

console.log(target.a);    // 37. 操作已经被正确地转发
  • 验证

通过代理,你可以轻松地验证向一个对象的传值。下面的代码借此展示了 set handler 的作用。

let validator = {
  set(obj, prop, value)  {
    if (prop === 'age') {
      if (!Number.isInteger(value)) {
        throw new TypeError('The age is not an integer');
      }
      if (value > 200) {
        throw new RangeError('The age seems invalid');
      }
    }
    // 利用反射类来设置属性
    Reflect.set(obj, prop, value);
    // 表示成功
    return true;
  }
};

let person = new Proxy({}, validator);

person.age = 100;

console.log(person.age); 
// 100

person.age = 'young'; 
// 抛出异常: Uncaught TypeError: The age is not an integer

person.age = 300; 
// 抛出异常: Uncaught RangeError: The age seems invalid
  • 扩展构造函数

方法代理可以轻松地通过一个新构造函数来扩展一个已有的构造函数。这个例子使用了constructapply

function extend(sup, base) {
  var descriptor = Object.getOwnPropertyDescriptor(
    base.prototype, "constructor"
  );
  base.prototype = Object.create(sup.prototype);
  var handler = {
    construct(target, args) {
      var obj = Object.create(base.prototype);
      this.apply(target, obj, args);
      return obj;
    },
    apply(target, that, args) {
      sup.apply(that, args);
      base.apply(that, args);
    }
  };
  var proxy = new Proxy(base, handler);
  descriptor.value = proxy;
  Object.defineProperty(base.prototype, "constructor", descriptor);
  return proxy;
}

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

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

Boy.prototype.sex = "M";

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

解决对象属性为undefined的问题

在实际应用中,我们调用对象的多级属性时,当属性的父级不存在时经常会报 VM241:2 Uncaught TypeError: Cannot read property 'c' of undefined 的错误,如下:

VM241:2 Uncaught TypeError: Cannot read property 'c' of undefined

那么,我们可以使用 递归的Proxy 来代理 traps 的 get 方法, 来解决这个问题:

let handlers = {
    get (target, property) {
        if (Reflect.has(target, property)) {
            return Reflect.get(target, property);
        } else {
            if (typeof target === 'undefined') {
                target = {};
            }
            if (typeof target[property] === 'undefined') {
                target[property] = {};
            }
            return new Proxy(target[property], handlers);
        }
    }
}
let a = {};
let proxy = new Proxy(a, handlers)
console.log(proxy.a.b.c);

结果如下:

proxy.a.b.c

可以看到,并未报错,而是返回了一个 Proxy 的实例对象。

他山之石

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 前言 今天我们要来讲一下Javascript中一个很有趣的类 -- Proxy类,类名翻译成中文是代理的意思。在真...
    SF_1316阅读 4,090评论 0 1
  • 一、Proxy1、概述Proxy取其英文意思即“代理”。所谓代理,是你要取得某样东西或对其进行某些操作的中间媒介,...
    贵在随心阅读 13,181评论 2 11
  • 一、Proxy 1.什么是proxy Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,...
    钱罗罗_阅读 4,059评论 0 0
  • mdn上的解释: Proxy 对象用于定义基本操作的自定义行为(如属性查找,赋值,枚举,函数调用等)。 Refle...
    天驱丶阅读 4,461评论 0 1
  • 久违的晴天,家长会。 家长大会开好到教室时,离放学已经没多少时间了。班主任说已经安排了三个家长分享经验。 放学铃声...
    飘雪儿5阅读 12,184评论 16 22