【es6进阶】vue3中的数据劫持的最新实现方案的proxy的详解

vuejs中实现数据的劫持,v2中使用的是Object.defineProperty()来实现的,在大版本v3中彻底重写了这部分,使用了proxy这个数据代理的方式,来修复了v2中对数组和对象的劫持的遗留问题。

proxy是什么

Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。
Proxy 可以理解成对源对象进行一个封装,在操作源对象之前,做了一系列额外的操作,最终返回我们需要的新数据对象。

基础使用

let obj = new Proxy(
  {},
  {
    get(target, prop, receiver) {
      console.log("get", prop);
      if (!target[prop]) target[prop] = 120;
      return Reflect.get(target, prop, receiver);
    },
    set(target, prop, value, receiver) {
      console.log("set", prop);
      return Reflect.set(target, prop, value, receiver);
    },
  }
);

obj.count = 1;
obj.count;
obj.count;
obj.count;
console.log(obj.count);

obj.age;
console.log(obj.age);

proxy实例有两个参数,一个是目标对象,一个是操作方法的hash集合
在这里插入图片描述
取值函数get,赋值函数set

对特定属性的劫持

const proxyObj = new Proxy(
  { name: "Tom", age: 18 },
  {
    get: function (target, prop) {
      if (prop === "age") return target[prop] - 1;
      return 35;
    },
  }
);

proxyObj.time;
console.log("🚀 ~ proxyObj.time:", proxyObj.time);
proxyObj.age;
console.log("🚀 ~ proxyObj.time:", proxyObj.age);

在这里插入图片描述

把实例方法封装在对象内部

const object = {
  name: "Tom",
  age: 18,
  sayHi() {
    console.log("sayHi");
  },
  proxy() {
    return new Proxy(this, {
      get(target, prop) {
        console.log("🚀 ~ get ~ prop:", prop);
        if (prop in target) {
          return Reflect.get(target, prop);
        } else {
          return "no prop";
        }
        return Reflect.get(target, prop);
      },
    });
  },
};
const newProxy = object.proxy();
// newObjj.age;
console.log("🚀 ~ newObjj.age;:", newProxy.age);
console.log("🚀 ~ newObjj.name;:", newProxy.sex);

在这里插入图片描述

对数组进行负值索引的操作

function createArray(...elements) {
  let handler = {
    get(target, prop, receiver) {
      let index = Number(prop);
      if (index < 0) {
        prop = String(target.length + index);
      }
      return Reflect.get(target, prop, receiver);
    },
  };

  let target = [];
  target.push(...elements);
  return new Proxy(target, handler);
}

let arr = createArray("a", "b", "c");
arr[-1];
console.log("🚀 ~ arr[-1]:", arr[-1]);
console.log("🚀 ~ arr[-1]:", arr[-2]);
console.log("🚀 ~ arr[-1]:", arr[-3]);

在这里插入图片描述

实现数据的链式调用

var double = (n) => n * 2;
var pow = (n) => Math.pow(n, 2);
var reverse = (n) => String(n).split("").reverse().join("");

const pipe = function (value) {
  var funcStack = [];
  var oProxy = new Proxy(
    {},
    {
      get: function (target, key) {
        console.log("🚀 ~ pipe ~ key:", key);
        if (key === "get") {
          return funcStack.reduce(function (val, func) {
            return func(val);
          }, value);
        }
        // 把方法存储到栈中
        funcStack.push(window[key]);
        console.log("🚀 ~ funcStack:", funcStack);
        return oProxy;
      },
    }
  );
  return oProxy;
};
const data1 = pipe(3).double.pow.reverse.get;
console.log("🚀 ~ data:", data1);

在这里插入图片描述

注意:三个方法必须是var声明的,let/const都会报错

在这里插入图片描述
在这里插入图片描述在这里插入图片描述
上面代码设置 Proxy 以后,达到了将函数名链式使用的效果。

利用get拦截,实现一个生成各种 DOM 节点的通用函数dom

const dom = new Proxy(
  {},
  {
    get(target, prop) {
      return function (arrts, ...children) {
        const el = document.createElement(prop);
        for (let prop of Object.keys(arrts)) {
          el.setAttribute(prop, arrts[prop]);
        }
        for (let child of children) {
          console.log("🚀 ~ get ~ child:", child);
          if (typeof child === "string") {
            child = document.createTextNode(child);
          }
          el.appendChild(child);
        }
        return el;
      };
    },
  }
);

const el = dom.div(
  {},
  "Hello, my name is ",
  dom.a({ href: "//example.com" }, "Mark"),
  ". I like:",
  dom.ul(
    {},
    dom.li({}, "The web"),
    dom.li({}, "Food"),
    dom.li({}, "…actually that's it")
  )
);
document.body.appendChild(el);

在这里插入图片描述

第三个参数,它总是指向原始的读操作所在的那个对象

const proxy = new Proxy(
  {},
  {
    get: function (target, prop, receiver) {
      console.log("🚀 ~ prop:", prop);
      return receiver;
    },
  }
);
const isSame = proxy.getReceiver === proxy;
console.log("🚀 ~ isSame:", isSame);

const d = Object.create(proxy);
console.log("ddd", d.a === d);

在这里插入图片描述

如果一个属性不可配置(configurable)且不可写(writable),则 Proxy 不能修改该属性

const target = Object.defineProperties(
  {},
  {
    foo: { value: "bar", enumerable: false, configurable: false },
  }
);

const handler = {
  get(target, prop) {
    return "abc";
  },
};

const proxy2 = new Proxy(target, handler);

proxy2.foo;

在这里插入图片描述
上面通过 Proxy 对象访问该属性会报错。

拦截方法的执行

上面的都是object对象的属性进行劫持,也可以作为方法调用时进行劫持。

var target = function () {
  return "I am the target";
};

var handler = {
  apply(target, thisArg, argumentsList) {
    console.log("🚀 ~ apply ~ argumentsList:", argumentsList);
    const res = target();
    console.log("🚀 ~ apply ~ res:", res);
    return "I am the proxy" + " " + argumentsList.join(",");
  },
};

const p = new Proxy(target, handler);

const a = p("a", "b");
console.log("🚀 ~ a:", a);

在这里插入图片描述
变量p是 Proxy 的实例,当它作为函数调用时(p()),就会被apply方法拦截,返回一个字符串

function sum(left, right) {
  return left + right;
}

var twice = {
  apply(target, context, args) {
    console.log("🚀 ~ apply ~ context:", context);
    console.log("🚀 ~ apply ~ args:", args);
    return Reflect.apply(target, context, args) * 2;
  },
};

const proxy = new Proxy(sum, twice);
const data = proxy(1, 2);
console.log("🚀 ~ data:", data);
const data2 = proxy.call(null, 2, 5);
console.log("🚀 ~ data2:", data2);
const data3 = proxy.apply(null, [5, 5]);
console.log("🚀 ~ data3:", data3);

当执行proxy函数(直接调用或call和apply调用),就会被apply方法拦截。

在这里插入图片描述

get和set方法,实现内部属性的保护机制

const proxy = new Proxy(
  {},
  {
    get(target, prop) {
      invariant(prop, "get");
      return Reflect.get(target, prop);
    },
    set(target, prop, value) {
      invariant(prop, "set");
      Reflect.set(target, prop, value);
      return true;
    },
  }
);

function invariant(key, action) {
  if (key[0] === "_") {
    throw new Error(`Invalid attempt to ${action} private "${key}" property`);
  }
}
// proxy._prop;
proxy._prop = "value";

在这里插入图片描述

在这里插入图片描述

拦截key in proxy的操作

var target = { _prop: "foo", prop: "foo" };
const proxy = new Proxy(target, {
  has(target, key) {
    if (key[0] === "_") {
      console.log("false");
      return false;
    }
    return key in target;
  },
});
"_prop" in proxy; // false

在这里插入图片描述

deleteProperty删除属性的劫持

const handler = {
  construct(target, args) {
    console.log("called: " + args.join(","));
    return new target(...args);
  },
  deleteProperty(target, prop) {
    if (prop === "age") return false;
    delete target[prop];
    return true;
  },
};

const P = new Proxy(function () {}, handler);
const p = new P(10);
P.value;

const p2 = new Proxy(
  {
    age: 20,
    name: "John",
    greet: () => console.log("hello"),
  },
  handler
);

delete p2.age;
delete p2.name;

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/921695.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python浪漫之画明亮的月亮

目录 1、效果展示 2、完整版代码 1、效果展示 2、完整版代码 import turtledef draw_moon():# 设置画布turtle.bgcolor("black") # 背景颜色为黑色turtle.speed(10) # 设置绘制速度# 绘制月亮的外圈turtle.penup()turtle.goto(0, -100) # 移动到起始…

《线性代数的本质》

之前收藏的一门课&#xff0c;刚好期末复习&#xff0c;顺便看一看哈哈 课程链接&#xff1a;【线性代数的本质】合集-转载于3Blue1Brown官方双语】 向量究竟是什么 线性代数中最基础、最根源的组成部分就是向量&#xff0c;需要先明白什么是向量 不同专业对向量的看法 物理专…

鸿蒙系统ubuntu开发环境搭建

在RISC-V等平台移植鸿蒙系统OpenHarmony&#xff0c;需要使用linux环境进行代码的编译&#xff0c;为兼顾日常办公需要&#xff0c;可采用WindowsUbuntu虚拟机的混合开发的环境&#xff0c;通过网络及文件夹共享&#xff0c;在主机和虚拟机之间共享文件数据。 工具准备&#x…

智能停车解决方案之停车场室内导航系统(二):核心技术与系统架构构建

hello~这里是维小帮&#xff0c;如有项目需求和技术交流欢迎大家私聊我们&#xff01;点击文章最下方获取智慧停车场方案~撒花&#xff01; 随着城市化进程的加速&#xff0c;停车难问题日益凸显。智能停车系统作为缓解停车压力的有效手段&#xff0c;其核心技术与架构的构建至…

(免费送源码)计算机毕业设计原创定制:Java+JSP+HTML+JQUERY+AJAX+MySQL springboot计算机类专业考研学习网站管理系统

摘 要 大数据时代下&#xff0c;数据呈爆炸式地增长。为了迎合信息化时代的潮流和信息化安全的要求&#xff0c;利用互联网服务于其他行业&#xff0c;促进生产&#xff0c;已经是成为一种势不可挡的趋势。在大学生在线计算机类专业考研学习网站管理的要求下&#xff0c;开发一…

IDEA2023版本中如何启动项目的多个实例

假设现在要启动多个服务&#xff0c;例如简单的客户端和服务端&#xff0c;默认的idea是只能启动一个的&#xff0c;那么我们需要进行配置允许多个项目的同时启动&#xff0c;现在进行多实例的配置。 第一步 点击Edit Configurations 第二步 点击Modify options 第三步 勾选…

图的邻接矩阵和邻接表存储

目录 邻接矩阵存储法 简介 ​编辑 邻接矩阵举例 无向图邻接矩阵 有向图邻接矩阵 当各条边带有权值时 邻接矩阵算法实现 结构体定义和函数声明 函数的实现 邻接表存储法 简介 邻接表的算法实现 结构体定义和函数声明 函数的实现 邻接矩阵和邻接表的差别 邻接矩阵存…

【Linux命令】grep

Linux命令-grep GREP命令&#xff1a;进行字符串数据的比对&#xff0c;并将符合指定模式的字符串行打印出来。1.命令介绍基础正则表达式原始文档如下&#xff1a; 2.练习题&#xff1a;2.1 练习&#xff08;一&#xff09;&#xff1a;2.1.1 读取加行号的文件内容&#xff1a;…

WMS 如何实现智能仓储与自动化物流的无缝对接

【大家好&#xff0c;我是唐Sun&#xff0c;唐Sun的唐&#xff0c;唐Sun的Sun。】 在当今高度竞争的商业环境中&#xff0c;企业对于物流效率和仓储管理的要求日益严苛。智能仓储和自动化物流作为现代物流领域的重要发展方向&#xff0c;能够显著提高物流运作的速度、准确性和成…

DevOps-Jenkins-新手入门级

1. Jenkins概述 1. Jenkins是一个开源持续集成的工具&#xff0c;是由JAVA开发而成 2. Jenkins是一个调度平台&#xff0c;本身不处理任何事情&#xff0c;调用插件来完成所有的工作 1.1 什么是代码部署 代码发布/部署>开发书写的程序代码---->部署测试/生产环境 web服务…

WEB APIS(DOM对象,操作元素内容,属性,表单属性,自定义属性,定时器)

js基础基本语法&#xff1a; 变量&#xff0c;数据类型&#xff0c;循环&#xff0c;函数&#xff0c;对象等(主要是控制台打印&#xff09; WEB APIS 操作DOM BOM &#xff1a; 控制网页元素&#xff0c;交互等各种网页交互效果 js高级 语法&#xff1a; js新增语法&#xff0…

cs144(一)

cs144(一) 1、osi 当应用程序有数据要发送时&#xff0c;应用层将数据交给传输层&#xff0c; 传输层负责将数据可靠或不可靠地传送到另外一端&#xff0c;传输层通过将数据交给网络层来发送数据 网络层负责将数据分成数据包&#xff0c;每个数据包都有正确的目的地址 最后…

IEC61850读服务器目录命令——GetServerDirectory介绍

IEC61850标准中的GetServerDirectory命令是变电站自动化系统中非常重要的一个功能&#xff0c;它主要用于读取服务器的目录信息&#xff0c;特别是服务器的逻辑设备节点&#xff08;LDevice&#xff09;信息。以下是对GetServerDirectory命令的详细介绍。 目录 一、命令功能 …

如何使用AWS Lambda构建一个云端工具(超详细)

首发地址&#xff08;欢迎大家访问&#xff09;&#xff1a;如何使用AWS Lambda构建一个云端工具&#xff08;超详细&#xff09; 1 前言 1.1 无服务器架构 无服务器架构&#xff08;Serverless Computing&#xff09;是一种云计算服务模型&#xff0c;它允许开发者构建和运行…

力扣-位运算-1【算法学习day.41】

前言 ###我做这类文档一个重要的目的还是给正在学习的大家提供方向&#xff08;例如想要掌握基础用法&#xff0c;该刷哪些题&#xff1f;&#xff09;我的解析也不会做的非常详细&#xff0c;只会提供思路和一些关键点&#xff0c;力扣上的大佬们的题解质量是非常非常高滴&am…

MySQL数据库学习(持续更新ing)

1. 什么是数据库&#xff1f;什么是数据库管理系统&#xff1f;什么是SQL&#xff1f;他们之间的关系是什么&#xff1f; 数据库&#xff1a;Database&#xff0c; 简称DB。按照一定格式存储数据&#xff0c;一些文件的组合。 数据库管理系统&#xff1a;DataBaseManagement&…

【Python · PyTorch】循环神经网络 RNN(基础概念)

【Python PyTorch】循环神经网络 RNN&#xff08;基础概念&#xff09; 0. 生物学相似性1. 概念2. 延时神经网络&#xff08;TDNN&#xff09;3. 简单循环神经网络&#xff08;Simple RNN&#xff09;3.1 BiRNN 双向循环神经网络3.2 特点记忆性参数共享图灵完备 3.3 网络结构3…

【Isaac Sim】相关问题汇总

目录 一、安装点击Install时报错二、启动时报 Failed to create any GPU devices三、加载Isaac Sim自带模型或示例时报 Isaac Sim is not responding 一、安装点击Install时报错 报错&#xff1a; request to https://asset.launcher.omniverse.nvidia.com/… failed, reason:…

接口上传视频和oss直传视频到阿里云组件

接口视频上传 <template><div class"component-upload-video"><el-uploadclass"avatar-uploader":action"uploadImgUrl":on-progress"uploadVideoProcess":on-success"handleUploadSuccess":limit"lim…

DataWorks快速入门

DataWorks基于MaxCompute、Hologres、EMR、AnalyticDB、CDP等大数据引擎&#xff0c;为数据仓库、数据湖、湖仓一体等解决方案提供统一的全链路大数据开发治理平台。本文以DataWorks的部分核心功能为例&#xff0c;指导您使用DataWorks接入数据并进行业务处理、周期调度以及数据…