zoukankan      html  css  js  c++  java
  • Proxy 代理的使用和介绍

    Proxy 代理的使用和介绍

    简介: Proxy是在ES2015就有语法,但是自己一直都没有用过,但是觉得这个东西就跟promise一样,是个很好的东西。所以整理一下,后续拓展。
    使用的目的就是在一些相关的操作上面进行代理拦截,比如访问数据之前的处理,进行拦截操作

    Vue3 已经用 Proxy 代替了 Object.defineProperty 实现响应式。
    mobx 也从 5.x 版本开始使用 Proxy 进行代理。

    实现的基本结构:

    /**
     * target: 表示要代理的目标,可以是object, array, function类型
     * handler: 是一个对象,可以编写各种代理的方法
     */
    const proxy = new Proxy(target, handler);
    

    举例子:
    访问-操作-对象的时候,进行拦截处理在进行返回

    const person = {
      name: 'smallTanks',
      age: 20,
    };
    const personProxy = new Proxy(person, {
      get(target, key, receiver) {
        console.log(`get value by ${key}`);
        return target[key];
      },
      set(target, key, value) {
        console.log(`set ${key}, old value ${target[key]} to ${value}`);
        target[key] = value;
      },
    });
    //读取:
    console.log(person.name)
    //输出--- smallTanks
    console.log(personProxy.name)
    //输出--- 
    //get value by name
    //smallTanks
    
    //修改:
    personProxy.name = 'bigTanks';
    // 输出--- set name, old value wenzi to bigTanks
    

    并且通过 personProxy 设置数据时,代理的原结构里的数据也会发生变化。打印 person,字段 name 的值 变成bigTanks:

    所以本质上代理修改就是修改像原型链的类似的东西,在他修改之前去做了一些处理,建议一定要去自己试一试。

    Proxy的第二个参数:

    在这里插入图片描述
    handelr官方文档
    我现在的理解就是类似于原型链的操作 就跟那个 Object.defineProperty 是的

    Proxy 的第 2 个参数 handler 除了可以设置 get 和 set 方法外,这些是我参考别人的:

    1. get(target, propKey, receiver):拦截对象属性的读取,比如 proxy.foo 和 proxy['foo']。

    2. set(target, propKey, value, receiver):拦截对象属性的设置,比如 proxy.foo = v 或 proxy['foo'] = v,返回一个布尔值。

    3. has(target, propKey):拦截 propKey in proxy 的操作,返回一个布尔值。

    4. deleteProperty(target, propKey):拦截 delete proxy[propKey]的操作,返回一个布尔值。

    5. ownKeys(target):拦截 Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for...in 循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而 Object.keys()的返回结果仅包括目标对象自身的可遍历属性。

    6. getOwnPropertyDescriptor(target, propKey):拦截 Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。

    7. defineProperty(target, propKey, propDesc):拦截 Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一个布尔值。

    8. preventExtensions(target):拦截 Object.preventExtensions(proxy),返回一个布尔值。

    9. getPrototypeOf(target):拦截 Object.getPrototypeOf(proxy),返回一个对象。

    10. isExtensible(target):拦截 Object.isExtensible(proxy),返回一个布尔值。

    11. setPrototypeOf(target, proto):拦截 Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。

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

    13. construct(target, args):拦截 Proxy 实例作为构造函数调用的操作,比如 new proxy(...args)。

    写一个删除拦截的:

    const person = {
      name: 'smallTanks',
      age: 20,
    };
    const personProxy = new Proxy(person, {
      // 忽略get和set方法,与上面一样
      // ...
      deleteProperty(target, key, receiver) {
        console.log(`delete key ${key}`);
        delete target[key];
      },
    });
    
    delete personProxy['age'];
    // 输出--- delete key age
    

    拓展:Reflect

    在别人的案例上面会发现有很多使用 Reflect 简单介绍一下:
    下一篇写这个

    Reflect是ES6为了操作对象而新增的API, 为什么要添加Reflect对象呢?它这样设计的目的是为了什么?

    1)将Object对象的一些明显属于语言内部的方法(比如Object.defineProperty),放到Reflect对象上,那么以后我们就可以从Reflect对象上可以拿到语言内部的方法。

    2)在使用对象的 Object.defineProperty(obj, name, {})时,如果出现异常的话,会抛出一个错误,需要使用try catch去捕获,但是使用 Reflect.defineProperty(obj, name, desc) 则会返回false。

    所以规范起来就是:

    const personProxy = new Proxy(person, {
      get(target, key, receiver) {
        console.log(`get value by ${key}`);
        return Reflect.get(target, key, receiver);
      },
      set(target, key, value, receiver) {
        console.log(`set ${key}, old value ${target[key]} to ${value}`);
        return Reflect.set(target, key, value, receiver);
      },
      deleteProperty(target, key, receiver) {
        console.log(`delete key ${key}`);
        return Reflect.deleteProperty(target, key, receiver);
      },
    });
    

    代理数组的操作:

    vue在劫持数组数据的时候 是重新写了原型链上的几个方法 但是在proxy上可以直接使用修改
    至于原因我还没有搞明白,反正就是下面的代理数组操作的当做参考就可以。

    const arr = [1, 2, 3, 4];
    const arrProxy = new Proxy(arr, {
      get(target, key, receiver) {
        console.log('arrProxy.get', target, key);
        return Reflect.get(target, key, receiver);
      },
      set(target, key, value, receiver) {
        console.log('arrProxy.set', target, key, value);
        return Reflect.set(target, key, value, receiver);
      },
      deleteProperty(target, key) {
        console.log('arrProxy.deleteProperty', target, key);
        return Reflect.deleteProperty(target, key);
      },
    });
    
    
    arrProxy[2] = 22; // arrProxy.set (4) [1, 2, 3, 4] 2 22
    arrProxy[3]; // arrProxy.get (4) [1, 2, 22, 4] 3
    delete arrProxy[2]; // arrProxy.deleteProperty (4) [1, 2, 22, 4] 2
    arrProxy.push(5); // push操作比较复杂,这里进行了多个get()和set()操作
    arrProxy.length; // arrProxy.get (5) [1, 2, empty, 4, 5] length
    

    代理函数:

    前边都是在代理数据 现在看些代理函数:

    const getSum = (...args) => {
      if (!args.every((item) => typeof item === 'number')) {
        throw new TypeError('参数应当均为number类型');
      }
      return args.reduce((sum, item) => sum + item, 0);
    };
    const fnProxy = new Proxy(getSum, {
      /**
       * @params {Fuction} target 代理的对象
       * @params {any} ctx 执行的上下文
       * @params {any} args 参数
       */
      apply(target, ctx, args) {
        console.log('ctx', ctx);
        console.log(`execute fn ${getSum.name}, args: ${args}`);
        return Reflect.apply(target, ctx, args);
      },
    });
    

    代理函数的相应操作,将进行输入的值进行筛选操作

    // 10, ctx为undefined, log: execute fn getSum, args: 1,2,3,4
    fnProxy(1, 2, 3, 4);
    
    // ctx为undefined, Uncaught TypeError: 参数应当均为number类型
    fnProxy(1, 2, 3, '4');
    
    // 10, ctx为window, log: execute fn getSum, args: 1,2,3,4
    fnProxy.apply(window, [1, 2, 3, 4]);
    
    // 6, ctx为window, log: execute fn getSum, args: 1,2,3
    fnProxy.call(window, 1, 2, 3);
    
    // 6, ctx为person, log: execute fn getSum, args: 1,2,3
    fnProxy.apply(person, [1, 2, 3]);
    

    应用的场景:案例

    防抖的函数应用:

    const throttleByProxy = (fn, rate) => {
      let lastTime = 0;
      return new Proxy(fn, {
        apply(target, ctx, args) {
          const now = Date.now();
          if (now - lastTime > rate) {
            lastTime = now;
            return Reflect.apply(target, ctx, args);
          }
        },
      });
    };
    
    const logTimeStamp = () => console.log(Date.now());
    window.addEventListener('scroll', throttleByProxy(logTimeStamp, 300));
    

    后续再写

    咫尺远近却无法靠近的那个你,才敢让你发觉你并不孤寂
  • 相关阅读:
    2016 年青岛网络赛---Sort(k叉哈夫曼)
    Gym 100703G---Game of numbers(DP)
    棋盘覆盖(分治法)
    大整数乘法(分治法)
    博客编辑---数学公式
    《程序员代码面试指南》第八章 数组和矩阵问题 子数组的最大累加和问题
    《程序员代码面试指南》第八章 数组和矩阵问题 奇数下标都是奇数或者偶数下标都是偶数
    《程序员代码面试指南》第八章 数组和矩阵问题 自然数数组的排序
    《程序员代码面试指南》第八章 数组和矩阵问题 计算数组的小和
    《程序员代码面试指南》第八章 数组和矩阵问题 未排序数组中累加和小于或等于给定值的最长子数组长度
  • 原文地址:https://www.cnblogs.com/tcz1018/p/14736145.html
Copyright © 2011-2022 走看看