es6 Proxy和Reflect

Proxy

receiver

  • 改变 this
    详情
  • 能够获取实际调用的对象
    • 在大多数情况下,receiver 就是代理对象本身。然而,如果代理对象是另一个对象的原型,那么 receiver 就变成了调用属性的那个对象。
const target = {
  message: 'Hello, world'
};

const proxy = new Proxy(target, {
  get(targ, prop, receiver) {
    // ... 
  }
});

// 原型继承 proxy
const child = Object.create(proxy);

// 结果显示
console.log(proxy.message); // 这里 receiver 是 proxy 对象
console.log(child.message); // 这里 receiver 是 child 对象,而非 proxy 对象

优点

  • 功能强大:Proxy 提供了多种拦截场景,包括读写属性、删除属性、函数调用、原型链操作等。
  • 拦截细致:Proxy 可以有效地代理和拦截数组对象,包括索引访问和修改等。
  • 拦截简单:对一个对象应用拦截行为时,只需创建一个 Proxy
    • Object.defineProperty 需要为每个属性创建单独的属性描述符。
  • 不污染原目标对象 :Proxy 返回的是一个新对象,可以在不影响原始对象的前提下对代理对象操作。原始对象和代理对象可互相独立存在。而 Object.defineProperty 直接作用于原始对象。代理对象也不影响 instanceof 操作
  • 不需要事先知道对象的结构,代理是在对象外层的抽象层。代理可以在运行时决定,而不用在事前就定义
  • 拦截原型链:Proxy 可以拦截对原型链上的操作,而 Object.defineProperty 只作用于单个对象
  • 传递性:如果代理对象再次被代理,后面的代理能够完整地响应之前代理上的拦截行为。而 Object.defineProperty 不具备这样的特性
  • 可撤销性:使用 Proxy.revocable() 创建的代理对象,可以随时取消(撤销)代理的行为,恢复到普通的对象操作,而 Object.defineProperty 一旦定义了就不能这么轻易撤销

Proxy 懒代理

  • 只有当访问到对象的某个层级时,那个特定的层级对象才会被代理。这意味着代理创建是按需进行的
function createLazyProxy(target, handler) {
  // 仅当实际访问对象的某个属性时,创建该属性的代理
  return new Proxy(target, {
    get(target, property, receiver) {
      const value = Reflect.get(target, property, receiver);
      if (value && typeof value === 'object' && !value._isProxy) { // 检测对象是否已经是一个代理,避免重复代理
        const proxy = createLazyProxy(value, handler); // 递归创建新的代理
        proxy._isProxy = true; // 标记新的代理对象,防止重复代理
        return proxy;
      }
      return value;
    },
    set(target, property, value, receiver) {
      // 当值改变时,也可以为新的值创建代理
      if (value && typeof value === 'object') {
        value = createLazyProxy(value, handler);
      }
      return Reflect.set(target, property, value, receiver);
    },
    ...handler
    // 其他陷阱(traps)可以视需求添加
  });
}

// Handler定义了代理行为
const handler = {
  set(target, property, value) {
    console.log(`Property ${property} set to ${value}`);
    target[property] = value;
    return true;
  }
};

const obj = { level1: { level2: { level3: 'initial' } } };
const proxyObj = createLazyProxy(obj, handler);

// 这次访问时,会为`level1`创建代理
console.log(proxyObj.level1.level2.level3);

// 这次改变时,`level2`对象被代理,并且`set`会被调用
proxyObj.level1.level2.level3 = 'changed';

缺点

  • 性能影响:由于 Proxy 拦截操作涉及额外的计算,可能导致程序速度略有降低
  • 兼容性问题:即使使用 Polyfill,也无法完全模拟 Proxy 的原生行为。

Reflect

  • 使用 Reflect 有助于确保 Proxy 的处理器方法正确地模拟了 JavaScript 默认的操作,从而避免了由于自定义操作引入的可能的行为差异或者错误
const strictModeProxy = new Proxy(obj, {
  set(target, prop, value) {
    if (!Reflect.set(target, prop, value)) { // 如果赋值失败会返回 false(无论是否是严格模式下)
      // 根据返回情况自定义后续操作,如果属性不可写,直接通过 obj.x=xx 进行赋值,严格模式下会报错,非严格模式下静默
      // 通过 Reflect.set 操作可以统一行为
      throw new Error(`Property ${prop} is not writable.`);
    }
    return true;
  }
});

strictModeProxy.readOnly = 2; // Error: Property readOnly is not writable.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值