Proxy 与 defineProperty 的理解、区别、优势、劣势

一、Object.defineProperty()

文档:Object.defineProperty() - JavaScript | MDN 

作用:对一个对象进行操作的方法。可以为一个对象增加一个属性,同时也可以对一个属性进行修改和删除。

它是在 ES5 中引入的,使用了 getter 和 setter 方法来实现 Vue2 的响应式。

1、劣势 

Object.defineProperty() 的问题主要有三个:

  • 不能监听数组的变化 

无法监控到数组下标的变化,导致通过数组下标添加元素,不能实时响应

  • 必须遍历对象的每个属性

只能劫持对象的属性,从而需要对每个对象,每个属性进行遍历。

如果属性值是对象,还需要深度遍历。Proxy 可以劫持整个对象,并返回一个新的对象

  • 必须深层遍历嵌套的对象

2、优势

 兼容性好,支持 IE9

而 Proxy 的存在浏览器兼容性问题,而且无法用 polyfill 磨平

3、代码 
      Object.defineProperty(obj, prop, descriptor);// obj 要定义属性的对象// prop 要定义或修改的属性的名称// descriptor 要定义或修改的属性描述符Object.defineProperty(obj, "name", {value: "小草莓", // 初始值writable: true, // 该属性是否可写入enumerable: true, // 该属性是否可被遍历得到(for...in, Object.keys等)configurable: true, // 定该属性是否可被删除,且除writable外的其他描述符是否可被修改get: function () {},set: function (newVal) {},});

二、proxy

1、对 Proxy 的理解

Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。

Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。

ES6 入门教程

var obj = new Proxy({}, {get: function (target, propKey, receiver) {console.log(`getting ${propKey}!`);return Reflect.get(target, propKey, receiver);},set: function (target, propKey, value, receiver) {console.log(`setting ${propKey}!`);return Reflect.set(target, propKey, value, receiver);}
});obj.count = 1
//  setting count!
++obj.count
//  getting count!
//  setting count!
//  2

上面代码对一个空对象架设了一层拦截,重定义了属性的读取(get)和设置(set)行为

2、语法

ES6 原生提供 Proxy 构造函数,用来生成 Proxy 实例

var proxy = new Proxy(target, handler);

第一个参数:target参数表示所要拦截的目标对象

第二个参数:handler参数也是一个对象,用来定制拦截行为它是一个配置对象,对于每一个被代理的操作,需要提供一个对应的处理函数,该函数将拦截对应的操作。

Proxy 对象可以拦截目标对象的任意属性,这使得它很合适用来写 Web 服务的客户端。

3、Proxy的优势
  • 针对对象

针对整个对象,而不是对象的某个属性 ,所以也就不需要对 keys 进行遍历

  • 支持数组

Proxy 不需要对数组的方法进行重载,省去了众多 hack,减少代码量等于减少了维护成本,而且标准的就是最好的

  • Proxy的第二个参数可以有 13 种拦截方法

不限于applyownKeysdeletePropertyhas等等,是Object.defineProperty不具备的

  • Proxy返回的是一个新对象

我们可以只操作新的对象达到目的。而Object.defineProperty只能遍历对象属性直接修改

  • Proxy作为新标准将受到浏览器厂商重点持续的性能优化

也就是传说中的新标准的性能红利

4、使用 proxy 创建一个响应式对象
        import { isObject } from "./util"; // 工具方法// 创建一个响应式对象export function reactive(target) {// 根据不同参数创建不同响应式对象return createReactiveObject(target, mutableHandlers);}// 根据不同参数创建不同响应式对象function createReactiveObject(target, baseHandler) {if (!isObject(target)) {return target;}const observed = new Proxy(target, baseHandler);return observed;}const get = createGetter();const set = createSetter();function createGetter() {return function get(target, key, receiver) {// 对获取的值进行放射const res = Reflect.get(target, key, receiver);console.log("属性获取", key);if (isObject(res)) {// 如果获取的值是对象类型,则返回当前对象的代理对象return reactive(res);}return res;};}function createSetter() {return function set(target, key, value, receiver) {const oldValue = target[key];const hadKey = hasOwn(target, key);const result = Reflect.set(target, key, value, receiver);if (!hadKey) {console.log("属性新增", key, value);} else if (hasChanged(value, oldValue)) {console.log("属性值被修改", key, value);}return result;};}export const mutableHandlers = {get, // 当获取属性时调用此方法set // 当修改属性时调用此方法};

三、问题

1、Proxy只会代理对象的第一层,那么 Vue3 又是怎样处理这个问题的呢?

判断当前 Reflect.get 的返回值是否为 Object  ,如果是则再通过 reactive 方法做代理, 这样就实现了深度观测。

2、监测数组的时候可能触发多次get/set,那么如何防止触发多次呢?

我们可以判断 key 是否为当前被代理对象 target 自身属性,也可以判断旧值与新值是否相等,只有满足以上两个条件之一时,才有可能执行 trigger

四、Vue3.0 里为什么要用 Proxy 替代 defineProperty ?

主要是从性能方面考量 

defineProperty:该 API 存在一些局限性,比如对于数组的拦截有问题,为此 Vue 需要专门为数组响应式做一套实现。另外不能拦截那些新增、删除属性。最后 defineProperty 方案在初始化时需要深度递归遍历待处理的对象才能对它进行完全拦截,明显增加了初始化的时间

以上两点在 Proxy 出现之后迎刃而解。

Proxy不仅可以对数组实现拦截,还能对 Map、Set 实现拦截。另外 Proxy 的拦截也是懒处理行为。如果用户没有访问嵌套对象,那么也不会实施拦截,这就让初始化的速度和内存占用都改善了。

Vue的代理也是最开始只代理最外层的对象,在访问的时候去判断是否为一个 object,然后再去用 proxy 包裹

当然 Proxy 是有兼容性问题的,IE 完全不支持,所以如果需要 IE 兼容就不合适 

五、底层拦截原理

六、Proxy 和 Object.defineProperty 的区别?

Vue2 和 Vue3 响应式上有什么区别? / 使用 Object.defineProperty() 来进行数据劫持有什么缺点?_vue 2响应式和vue 3响应式区别-CSDN博客

都可以用来实现 JavaScript 对象的响应式,但是它们有一些区别:

① 实现方式

Proxy 是 ES6 新增的一种特性,使用了一种代理机制来实现响应式。

Object.defineProperty 是在 ES5 中引入的,使用了 getter 和 setter 方法来实现。

② 作用对象

Proxy 可以代理整个对象,包括对象的所有属性、数组的所有元素以及类似数组对象的所有元素。

Object.defineProperty 只能代理对象上定义的属性

③ 监听属性

Proxy 可以监听到新增属性和删除属性的操作

Object.defineProperty 只能监听到已经定义的属性的变化。

④ 性能

由于 Proxy 是 ES6 新增特性,其内部实现采用了更加高效的算法,相对于 Object.defineProperty来说在性能方面有一定的优势。

综上所述,虽然 Object.defineProperty 在 Vue.js 2.x 中用来实现响应式,但是在 Vue.js 3.0 中已经采用了 Proxy 来替代。

这是因为 Proxy 相对于 Object.defineProperty 拥有更优异的性能和更强大的能力。

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

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

相关文章

P10 RV1126推流项目——ffmpeg输出参数初始化

前言 从本章开始我们将要学习嵌入式音视频的学习了 ,使用的瑞芯微的开发板 🎬 个人主页:ChenPi 🐻推荐专栏1: 《C_ChenPi的博客-CSDN博客》✨✨✨ 🔥 推荐专栏2: 《Linux C应用编程(概念类)_C…

1.3 金融数据可视化

跳转到根目录:知行合一:投资篇 已完成: 1.1 编程基础   1.1.1 投资-编程基础-numpy   1.1.2 投资-编程基础-pandas 1.2 金融数据处理 1.3 金融数据可视化 文章目录 1. 金融数据可视化1.1. matplotlib1.1.1. 沪深300走势图1.1.2. 日线均线…

C++算法学习五.二叉树(1)

1.二叉树理论基础 二叉树的种类: 满二叉树:一棵二叉树只有度为0的结点和度为2的结点,并且度为0的结点在同一层上,则这棵二叉树为满二叉树。深度为k,总共有2的k次幂-1个节点。 完全二叉树:在完全二叉树中…

LeetCode 27.移除元素

移除元素 给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。 不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。 元素的顺序可以改变。你不需要考虑数组中超出新…

公司新招了个字节拿36K的人,让我见识到了什么才是测试扛把子......

5年测试,应该是能达到资深测试的水准,即不仅能熟练地开发业务,而且还能熟悉项目开发,测试,调试和发布的流程,而且还应该能全面掌握数据库等方面的技能,如果技能再高些的话,甚至熟悉分…

[Javaweb/LayUI/上机考试作业/开源]学生/图书/课程/仓库等管理系统六合一基础功能通用模板

展示 考试要求 给定用户表和六张图书/教师/顾客/仓库....的表(随机给每人抽选),要求实现用户登录注册,异步更新,对物品增删改查,精确/模糊查询等。 环境 tomcat 9 mysql 8 java 17 项目结构 项目类图 写前…

Java amr格式转mp3格式

1.问题描述 微信返回的语音是amr格式的,浏览器不能直接使用,所以需要转为mp3 注意:不能直接使用IO流转为mp3,不然H5还是用不了。转换之后的语音只能在播放器上播放,内里的文件格式其实还是amr 2.使用以下方式转换 音…

自制c++题目《抽象类与虚函数》

1.题目要求:构建一个抽象类car,由两个基类track和bike 继承,而它们共同的继承者是people类,要求构建上述类并撰写可辨别的语句 2.题解《抽象类与虚函数》的答案 王赫辰/c语言 - Gitee.com

tmux用法

tmux 安装 tmux sudo apt install tmux 运行 tmux tmux 退出 tmux exit 分离 tmux ctrl b, d 查看 tmux tmux ls 恢复 tmux tmux at -t <ID> 配置 tmux 在 ~/ 下面建一个 .tmux.conf 文件 set -g history-limit 9999 # 设置历史缓存长度 查看历史输出 …

Python学习笔记(五)函数、异常处理

目录 函数 函数的参数与传递方式 异常处理 函数 函数是将代码封装起来&#xff0c;实现代码复用的目的 函数的命名规则——同变量命名规则&#xff1a; 不能中文、数字不能开头、不能使用空格、不能使用关键字 #最简单的定义函数 user_list[] def fun(): #定义一个函数&…

Python正则表达式急速入门~正则居然这么容易掌握!

正则表达式在程序开发中会经常用到&#xff0c;比如数据&#xff08;格式&#xff09;验证、替换字符内容以及提取字符串内容等等情况都会用到&#xff0c;但是目前许多开发人员对于正则表达式只是处于了解或者是基本会用的阶段。一旦遇到大批量使用正则表达式的情况&#xff0…

Python初探:从零开始的编程奇妙之旅

一、Python是什么 Python是一门多用途的高级编程语言&#xff0c;以其简洁、易读的语法而脱颖而出。在深度学习领域&#xff0c;Python扮演着至关重要的角色。其丰富的科学计算库&#xff08;如NumPy、Pandas、Matplotlib&#xff09;和强大的深度学习框架&#xff08;如Tenso…

xshell设置终端类型为xterm-256color (解决oh-my-tmux颜色失真问题)

文章目录 问题描述解法效果检验 问题描述 在xshell远程连接服务器时&#xff0c;tmux色彩有问题&#xff08;tmux配置为Oh my tmux&#xff09;&#xff0c;如下&#xff1a; 这色彩明显是8位的色彩。 现在终端的标配就是类型为 xterm-256color&#xff0c;其支持256位的真彩…

CSU计算机学院2021年C语言期末题目思路分享(后两道题)

文章目录 E: 实数相加——大数加法的拓展原题题目描述输入输出样例输入样例输出 题目思路实现步骤代码和注释 F: 谍影寻踪——链表的思想和运用原题题目描述输入输出样例输入样例输出 题目思路 一点感想 E: 实数相加——大数加法的拓展 原题 题目描述 C语言就要期末考试了&a…

【操作系统习题】存储器与虚拟存储器

存储器 一、单选题 每次分配时总是从低地址到高地址顺序查找空闲区表&#xff0c;找到第一个能满足作业长度要求的空闲区&#xff0c;此种分配算法称为&#xff08;&#xff09; D A、最坏适应分配算法 B、随机适应分配算法 C、最优适应分配算法 D、首次适应分配算法一台计算…

深入探究:使用大型AI模型的实战指南

&#x1f482; 个人网站:【 海拥】【神级代码资源网站】【办公神器】&#x1f91f; 基于Web端打造的&#xff1a;&#x1f449;轻量化工具创作平台&#x1f485; 想寻找共同学习交流的小伙伴&#xff0c;请点击【全栈技术交流群】 在今天的技术领域&#xff0c;大型AI模型已成为…

2下载Spring,第一个Spring程序+引用Log4j2

https://www.yuque.com/dujubin/ltckqu/kipzgd#&#xff0c;注意的是&#xff0c;现在&#xff08;202401&#xff09;SpringFramework从release搬到了snapshot下&#xff0c;在这下面找到6.0.2下载. 下载后解压到文件夹&#xff0c;整个框架包含非常多jar包。 然后就可以在p…

C++:类和对象(2)

目录 1.strcut和class的区别 2.将成员属性设置为私有 3.对象的初始化和清理 3.1 构造函数和析构函数 3.1.1 构造函数语法 3.1.2 析构函数语法 3.1.3 检验 3.2 构造函数的分类和调用 3.3 拷贝构造函数调用 1.strcut和class的区别 struct和class的唯一区别在于默认的访问…

3个.NET开源简单易用的任务调度框架

前言 今天分享3个.NET开源、简单、易用的任务调度框架&#xff0c;帮助大家在做定时任务调度框架技术选型的时候有一个参考。 Quartz.Net Quartz.NET是一个功能齐全的开源作业调度系统&#xff0c;可用于从最小的应用程序到大规模企业系统。 Quartz.NetUI Quartz.NetUI是一…

leetcode03-简单的数组模拟

题目链接&#xff1a; https://leetcode.cn/problems/plus-one/description/?envTypestudy-plan-v2&envIdprogramming-skills 思路&#xff1a; 因为题目1是从后面加&#xff0c;所以考虑从后往前遍历数组。 在遍历过程中&#xff1a; 如果当前位为9&#xff1a; 则将该位…