【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;

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

D73【 python 接口自动化学习】- python 基础之正则表达式

day73 正则表达式-元字符匹配 学习日期&#xff1a;20241119 学习目标&#xff1a;正则表达式--133 正则表达式-元字符匹配 学习笔记&#xff1a; 元字符匹配 数量匹配 实践操作 总结 字符串的r标记表示&#xff0c;字符串内转移字符无效&#xff0c;作为普通字符使用正则…

Python浪漫之画明亮的月亮

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

微信小程序包之加农炮游戏

微信小程序 - 气球射击游戏 项目简介 这是一个简单有趣的微信小程序射击游戏。玩家通过控制屏幕底部的加农炮&#xff0c;射击从上方降落的蓝色气球。游戏考验玩家的反应能力和瞄准技巧。 游戏规则 点击屏幕任意位置发射炮弹大炮会自动对准点击位置击中气球获得10分如果气球触…

autogen+ollama+litellm实现本地部署多代理智能体

autogen 是一个专门为大语言模型 (LLMs) 驱动的自治代理 (autonomous agents) 设计的 Python 库,由 Microsoft 开发和维护。它通过高度模块化和可扩展的架构,支持用户快速构建和运行多代理系统,这些代理可以在没有明确人类干预的情况下协作完成复杂任务。AutoGen 支持以最少…

分公司如何纳税

分公司不进行纳税由总公司汇总纳税“子公司具有法人资格&#xff0c;依法独立承担民事责任;分公司不具有法人资格&#xff0c;其民事责任由公司承担。”企业设立分支机构&#xff0c;使其不具有法人资格&#xff0c;且不实行独立核算&#xff0c;则可由总公司汇总缴纳企业所得税…

HashMap源码详解

提醒你一下&#xff0c;用电脑或者平板看&#xff0c;手机屏幕小&#xff0c;图片会看不清楚&#xff0c;源码不方便看 基础前置 看该篇文章之前先看看这篇基础文章 HashMap底层原理-CSDN博客 先来看HashMap里面的一些参数。 1.DEFAULT_INITIAL_CAPACITY 默认的数组初…

09 —— Webpack搭建开发环境

搭建开发环境 —— 使用webpack-dev-server 启动Web服务&#xff0c;自动检测代码变化&#xff0c;有变化后会自动重新打包&#xff0c;热更新到网页&#xff08;代码变化后&#xff0c;直接替换变化的代码&#xff0c;自动更新网页&#xff0c;不用手动刷新网页&#xff09; …

动态反馈控制器(DFC)和 服务率控制器(SRC);服务率和到达率简单理解

目录 服务率和到达率简单理解 服务率 到达率 排队论中的应用 论文解析:队列等待成本动态感知控制模型 动态反馈和队列等待成本意识: 服务速率调整算法: 动态反馈控制器(DFC)和 服务率控制器(SRC) SRC公式4的原理 算力资源分配系统中的调整消耗 举例说明 服务…

九、FOC原理详解

1、FOC简介 FOC&#xff08;field-oriented control&#xff09;为磁场定向控制&#xff0c;又称为矢量控制&#xff08;vectorcontrol&#xff09;&#xff0c;是目前无刷直流电机&#xff08;BLDC&#xff09;和永磁同步电机&#xff08;PMSM&#xff09;高效控制的最佳选择…

web-03

CSS回顾 选择器 标签选择器 标签{}ID选择器 标签中定义ID属性。 #ID值{}类选择器 标签中使用class属性 .类名{}关于DIV/span div任意的大小的长方形&#xff0c;大小css&#xff1a; width, height控制。—换行 span-- 一行内 CSS常用属性 width/height 宽度/高度 定义&…

Excel求和如何过滤错误值

一、问题的提出 平时&#xff0c;我们在使用Excel时&#xff0c;最常用的功能就是求和了&#xff0c;一说到求和你可能想到用sum函数&#xff0c;但是如果sum的求和区域有#value #Div等错误值怎么办&#xff1f;如下图&#xff0c;记算C列中工资的总和。 直接用肯定会报错&…

蓝桥杯每日真题 - 第22天

题目&#xff1a;&#xff08;卡片&#xff09; 题目描述&#xff08;12届 C&C B组B题&#xff09; 解题思路&#xff1a; 该问题要求用数字卡片从 1 开始拼出整数&#xff0c;直到某一时刻不能拼出时停止。要确定拼到哪个最大整数&#xff0c;需要统计 每个数字“1”被用…

Ubuntu20.04 Rk3588 交叉编译ffmpeg7.0

firefly 公司出的rk3588的设备&#xff0c;其中已经安装了gcc 交叉编译工具&#xff0c;系统版本是Ubuntu20.04。 使用Ubuntu20.04 交叉编译ffmpeg_ubuntu下配置ffmpeg交叉编译器为arm-linux-gnueabihf-gcc-CSDN博客文章浏览阅读541次。ubuntu20.04 交叉编译ffmpeg_ubuntu下配…

python代码制作数据集的测试和数据质量检测思路

前言 本文指的数据集为通用数据集&#xff0c;并不单是给机器学习领域使用。包含科研和工业领域需要自己制作数据集的。 首先&#xff0c;在制作大型数据集时&#xff0c;代码错误和数据问题可能会非常复杂。 前期逻辑总是简单的&#xff0c;库库一顿写&#xff0c;等排查的时…

Windows系统编程 - 进程遍历

文章目录 前言进程的遍历CreateToolhelp32SnapshotProcess32FirstProcess32Next进程遍历 总结 前言 各位师傅好&#xff0c;我是qmx_07&#xff0c;今天给大家讲解进程遍历的相关知识点 进程的遍历 快照&#xff1a;使用vmware虚拟机的时候&#xff0c;经常需要配置环境服务…

【GD32】(三) ISP基本使用

0 前言 有一块GD32的板子不知道为啥用着用着就下载不了程序了&#xff0c;没办法&#xff0c;只能另寻他法。作为STM32的平替&#xff0c;GD32的功能和STM32基本是一致的&#xff0c;所以也可以使用ISP来下载程序。于是就开始复活这块板子。 1 BOOT模式 对于熟悉STM32开发的人…

【SKFramework框架核心模块】3-2、音频管理模块

推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享QQ群&#xff1a;398291828小红书小破站 大家好&#xff0c;我是佛系工程师☆恬静的小魔龙☆&#xff0c;不定时更新Unity开发技巧&#xff0c;觉得有用记得一键三连哦。 一、前言 【Unity3D框架】SKFramework框架完全教程《全…

开源项目-如何更好的参与开源项目开发

开源之谜-提升自我核心竞争力 一、寻找适合自己的开源项目二、像坐牢一样闭关修炼三、最后的实践 开源代码对所有人开放&#xff0c;开发者可以基于现有代码进行扩展和创新&#xff0c;而不是从零开始&#xff0c;参与开源项目可以提升自我的技术能力&#xff0c;丰富个人的经历…

利用c语言详细介绍下插入排序

插入排序&#xff0c;被称为直接插入排序。它的基本思想是将一个记录插入到已经排好序的有序表中&#xff0c;从而一个新的、记录数增 1 的有序表。 一、图文介绍 我们还是使用数组【10&#xff0c;5&#xff0c;3&#xff0c;20&#xff0c;1]&#xff0c;排序使用升序的方式&…

STL——string类常用接口说明

目录 一、string类的介绍 二、string类常用接口的使用说明 1.成员函数 ​编辑 2.迭代器 3.容量 一、string类的介绍 下面是string类的文档对string类的介绍 1.string类是表示字符序列的对象 2.标准字符串类通过类似于标准字符容器的接口为此类对象提供支持&#xff0c…