一次性搞懂 ES6 的 Proxy 与 Reflect

2021-01-26  本文已影响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"

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

使用 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()

其它方法

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

上一篇下一篇

猜你喜欢

热点阅读