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

news/2024/11/24 17:13:42/

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.ppmy.cn/news/1549592.html

相关文章

CSS3_媒体查询(十一)

CSS3_响应式布局 1、媒体样式 在不同媒体上显示不同的样式。 常用阈值&#xff1a; 小于768px&#xff1a;小屏幕&#xff1b;768px-992px&#xff1a;中等屏幕&#xff1b;992px-1200px&#xff1a;大屏幕&#xff1b;大于1200px&#xff1a;超大屏幕。 <!DOCTYPE html>…

聚焦AI存储,联想凌拓全力奔赴

【全球存储观察 &#xff5c; 科技热点关注】 每一个时代&#xff0c;都有每一个时代的骄傲。 在信息化时代&#xff0c;NAS文件存储肩负着非结构化数据管理与存储的重任&#xff0c;NetApp以其创新实力&#xff0c;赢得了全球存储市场的极高声誉。 在数智化时代&#xff0c;…

webgl threejs 云渲染(服务器渲染、后端渲染)解决方案

云渲染和流式传输共享三维模型场景 1、本地无需高端GPU设备即可提供三维项目渲染 云渲染和云流化媒体都可以让3D模型共享变得简单便捷。配备强大GPU的远程服务器早就可以处理密集的处理工作&#xff0c;而专有应用程序&#xff0c;用户也可以从任何个人设备查看全保真模型并与…

ubuntu 安装 yum 无法定位问题

前言&#xff1a;yum安装方法其实很简单&#xff0c;知识使用apt install yum 即可&#xff0c;但是会遇到了各种问题&#xff0c;报‘E: 无法定位软件包 yum’&#xff0c;apt下载源问题。 1.问题 系统&#xff1a;ubuntu22.04 yum报错&#xff1a;E: 无法定位软件包 yum …

java基础知识(常用类)

一、包装类&#xff08;Wrapper) &#xff08;1&#xff09;包装类与基本数据的转换 装箱&#xff1a;基本类型->包装类型 拆箱&#xff1a;包装类型->基本类型 java5以后是自动装箱和拆箱的方式&#xff0c;自动装箱底层调用的是valueOf方法&#xff0c;比如Integer.…

某车企ASW面试笔试题

01--背景 去年由于工作岗位的动荡&#xff0c;于是面试了一家知名车企&#xff0c;上来进行了一番简单的介绍之后&#xff0c;被告知需要进入笔试环节&#xff0c;以往单位面试都是简单聊聊技术问题&#xff0c;比如对软件开发的流程或者使用的工具等待问题的交流&#xff0c;…

基于Java Springboot高校师资管理系统

一、作品包含 源码数据库设计文档万字PPT全套环境和工具资源部署教程 二、项目技术 前端技术&#xff1a;Html、Css、Js、Vue、Element-ui 数据库&#xff1a;MySQL 后端技术&#xff1a;Java、Spring Boot、MyBatis 三、运行环境 开发工具&#xff1a;IDEA/eclipse 数据…

基于Spark3.4.4开发StructuredStreaming读取socket数据

maven依赖文件如下&#xff1a; <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven…