美文网首页
一次性搞懂 ES6 的 Proxy 与 Reflect

一次性搞懂 ES6 的 Proxy 与 Reflect

作者: 前端好有趣 | 来源:发表于2021-01-26 15:35 被阅读0次

Proxy

Proxy 意为 代理,通俗来说,就是在 目标数据对象 外围设置一层 拦截

什么是拦截?

举个栗子:老板有一个仓库(对象),刚开始大家可以任意在其中存放货物(获取、修改、存放值)。后来老板觉得自己的仓库每次被使用得太随意了,就请了一个管家(拦截对象),所有人存取货物都必须经过管家之手。老板说:我制定一个拦截方案,那就是每次存取货物,管家都要大声把物品的信息念出来(console.log 一下)。

据上所得三个关键词:

  • 仓库
  • 管家
  • 拦截方案

get(target, key, receiver)

下面用一段简单的代码来表述下:

// 原对象
let obj = {
    name:'steve',
    age:20
}

// data 是我们要拦截的 原对象
// dataProxy 是我们新生成的 拦截对象
let dataProxy = new Proxy(obj, {
  get(target, key, receiver) {
    // target 就是我们的原对象,obj
    console.log("target >>> ", target === obj);
    // key 就是操作的健
    console.log("key >>> ", key);
    // receiver 就是 dataProxy,拦截对象
    console.log("receiver >>> ", receiver);
    console.log(`当前值为:${target[key]}`);
    return target[key];
  },
});

// 使用拦截器
dataProxy.name;

// 打印结果:
//  -> 当前值为:steve
//  -> "steve"

然后,相应的关系就出来了:

  • 仓库 --- obj
  • 管家 --- dataProxy
  • 拦截方案 --- set(),get()
使用 proxy 实现 节点创建

在上面,我们已经学会了如何使用 proxy 实现对对象的拦截,接下来,我们用一个例子来说明 proxy 在实际项目中可以用来干些什么。

// 首先创建我们的 proxy 代理对象
const DOMR = new Proxy(
  {},
  {
    get(target, key) {
      // 每次访问 DOMR 的属性,都返回一个函数,该函数可以创建节点
      // attr 要创建的元素的属性对象
      // children 要创建的元素的内容
      return function (attr = {}, ...children) {
        const el = document.createElement(key);
        // 遍历 属性对象,为元素添加属性
        for (let key of Object.keys(attr)) {
          el.setAttribute(key, attr[key]);
        }
        for (let child of children) {
          // 遍历 内容,为元素添加 内容
          if (typeof child === "string") {
            child = document.createTextNode(child);
          }
          el.appendChild(child);
        }
        // 返回创建的元素
        return el;
      };
    },
  }
);

// 只要我们调用 DOMER.xxx 就创建一个 xxx 节点
let div = DOMR.div(
  { id: "div1", class: "aaa" },
  // 只要我们调用 DOMER.xxx 就创建一个 xxx 节点
  DOMR.a({ href: "https://www.baidu.com", class: "hello" }, "这是一个连接")
);

// 打印出节点
console.log(div);

// window.onload,必须等到页面内包括图片的所有元素加载完毕后才能执行。
window.onload = function () {
  // 添加节点到页面上
  document.body.appendChild(div);
};

set(target, key, value, receiver)

用来拦截对象的设置过程

let person = new Proxy(
  {},
  {
    // target 原对象 {}
    // key 操作的键
    // value 被设置的值
    // receiver 就是 person,拦截对象
    set(target, key, value, receiver) {
      if (key === "age") {
        if (!Number.isInteger(value)) {
          throw new TypeError("年龄必须为整数!");
        }
        if (value > 100) {
          throw new RangeError("年龄不能大于100!");
        }
      }
      target[key] = value;
    },
  }
);
person.age = 20;

apply(target, context, args)

拦截 Proxy 实例作为函数调用的操作,比如 proxy(...args)proxy.call(object, ...args)proxy.apply(...)

let obj = {name:'obj'}
function say() {
  console.log("hello world");
}
let newFn = new Proxy(say, {
  // target 目标函数
  // context 目标函数的上下文 this
  // args 函数参数
  apply(target, object, args) {
    return 'hello'
  },
});

// target:say
// context:obj
// args:[1,2,3]
newFn.call(obj,1,2,3)
// 打印结果:hello
// 并没有打印出 hello world,说明,原函数内部代码并不会执行,如需执行我们需要在 apply 中调用 say()

其它方法

  • has(target, key):拦截key in proxy的操作,返回一个布尔值。
  • deleteProperty(target, key):拦截delete proxy[key]的操作,返回一个布尔值。
  • ownKeys(target):拦截Object.getOwnPropertyNames(proxy)Object.getOwnPropertySymbols(proxy)Object.keys(proxy)for...in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性。
  • getOwnPropertyDescriptor(target, key):拦截Object.getOwnPropertyDescriptor(proxy, key),返回属性的描述对象。
  • defineProperty(target, key, propDesc):拦截Object.defineProperty(proxy, key, propDesc)Object.defineProperties(proxy, propDescs),返回一个布尔值。
  • preventExtensions(target):拦截Object.preventExtensions(proxy),返回一个布尔值。
  • getPrototypeOf(target):拦截Object.getPrototypeOf(proxy),返回一个对象。
  • isExtensible(target):拦截Object.isExtensible(proxy),返回一个布尔值。
  • setPrototypeOf(target, proto):拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。
  • construct(target, args):拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)

proxy 与 Object.defineproperty 的区别

Proxy 提供了13种拦截方法,包括拦截 constructorapplydeleteProperty 等等,而 Object.defineProperty 只有 getset

Reflect

reflect:反射,一般用来将语言内部的一些方法,直接暴露到这个对象上,供人们使用。比如 Function 的 apply,Object 的 设置抽成 set 等等。

Reflect.apply(func, context, args)

效果跟 Function 的 apply 很相似,可以用任意对象作为上下文调用函数

function show(...args) {
  console.log(this);
  console.log(args);
}

Reflect.apply(show, null, [1, 2, 3, 4]);
// 打印结果:
// window
// [1,2,3,4]

Reflect.set(target, name, value, receiver)

设置target对象的name属性等于value

var myObject = {
  foo: 1,
};
Reflect.set(myObject, "foo", 2);
myObject.foo; // 2

Proxy 与 Reflect 结合使用案例

实现数据的双向绑定

let vm = {
  list: [1, 2, 3, 4]
}

let vmProxy = new Proxy(vm.list, {
  set (target, prop, value) {
    console.log(`Setting: ${value}`);
    Reflect.set(target, prop, value);
    return true;
  }
})

vmProxy[0] = 3

// 打印结果:[3, 2, 3, 4]
console.log(vm.list)

如果有同学想要系统学习,请移步 ECMAScript 6 - Proxy

参考文献

https://segmentfault.com/a/1190000015483195
https://www.jianshu.com/p/77eaaf34e732
https://juejin.cn/post/6844904088119853063
https://juejin.cn/post/6844904090116292616#heading-6
https://es6.ruanyifeng.com/#docs/proxy
https://my.oschina.net/u/4333555/blog/4329035
https://blog.csdn.net/XuM222222/article/details/98846376
https://blog.csdn.net/weixin_43574780/article/details/108042951

相关文章

  • ES6中的Reflect与Proxy(个人笔记)

    概述 Proxy 与 Reflect 是 ES6 为了操作对象引入的 API 。 Proxy:Proxy 可以对目...

  • ES6——Reflect 与 Proxy

    ES6 之 Proxy 介绍深入实践 ES6 Proxy & Reflect 1.Proxy Proxy 可以对目...

  • ES6标准入门读书笔记12(Reflect)未完待续

    概述 § ⇧ Reflect对象与Proxy对象一样,也是 ES6 为了操作对象而提供的新 API。Reflect...

  • Reflect

    概述 § ⇧ Reflect对象与Proxy对象一样,也是 ES6 为了操作对象而提供的新 API。Reflect...

  • ES6 Proxy和Reflect

    Proxy 与 Reflect 是 ES6 为了操作对象引入的 API 。Proxy 可以对目标对象的读取、函数调...

  • Reflect 对象

    Reflect 对象与 Proxy 对象一样,也是 ES6 为了操作对象而提供的新的API。Reflect 对象的...

  • ES6 Reflect

    一、概述 Reflect对象与Proxy对象一样,也是 ES6 为了操作对象而提供的新 API。Reflect对象...

  • 一次性搞懂 ES6 的 Proxy 与 Reflect

    Proxy Proxy 意为 代理,通俗来说,就是在 目标数据对象 外围设置一层 拦截。 什么是拦截? 举个栗子:...

  • Reflect对象

    Reflect对象与Proxy对象一样,也是 ES6 为了操作对象而提供的新 API。Reflect对象的设计目的...

  • Reflect用法详解

    概述 Reflect对象与Proxy对象一样,也是 ES6 为了操作对象而提供的新 API。Reflect对象的设...

网友评论

      本文标题:一次性搞懂 ES6 的 Proxy 与 Reflect

      本文链接:https://www.haomeiwen.com/subject/vqigzktx.html