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

2024-01-05 03:28

本文主要是介绍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 拥有更优异的性能和更强大的能力。

这篇关于Proxy 与 defineProperty 的理解、区别、优势、劣势的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/571517

相关文章

一文带你迅速搞懂路由器/交换机/光猫三者概念区别

《一文带你迅速搞懂路由器/交换机/光猫三者概念区别》讨论网络设备时,常提及路由器、交换机及光猫等词汇,日常生活、工作中,这些设备至关重要,居家上网、企业内部沟通乃至互联网冲浪皆无法脱离其影响力,本文将... 当谈论网络设备时,我们常常会听到路由器、交换机和光猫这几个名词。它们是构建现代网络基础设施的关键组成

redis和redission分布式锁原理及区别说明

《redis和redission分布式锁原理及区别说明》文章对比了synchronized、乐观锁、Redis分布式锁及Redission锁的原理与区别,指出在集群环境下synchronized失效,... 目录Redis和redission分布式锁原理及区别1、有的同伴想到了synchronized关键字

深入理解go中interface机制

《深入理解go中interface机制》本文主要介绍了深入理解go中interface机制,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学... 目录前言interface使用类型判断总结前言go的interface是一组method的集合,不

Java Spring的依赖注入理解及@Autowired用法示例详解

《JavaSpring的依赖注入理解及@Autowired用法示例详解》文章介绍了Spring依赖注入(DI)的概念、三种实现方式(构造器、Setter、字段注入),区分了@Autowired(注入... 目录一、什么是依赖注入(DI)?1. 定义2. 举个例子二、依赖注入的几种方式1. 构造器注入(Con

JAVA覆盖和重写的区别及说明

《JAVA覆盖和重写的区别及说明》非静态方法的覆盖即重写,具有多态性;静态方法无法被覆盖,但可被重写(仅通过类名调用),二者区别在于绑定时机与引用类型关联性... 目录Java覆盖和重写的区别经常听到两种话认真读完上面两份代码JAVA覆盖和重写的区别经常听到两种话1.覆盖=重写。2.静态方法可andro

C++中全局变量和局部变量的区别

《C++中全局变量和局部变量的区别》本文主要介绍了C++中全局变量和局部变量的区别,全局变量和局部变量在作用域和生命周期上有显著的区别,下面就来介绍一下,感兴趣的可以了解一下... 目录一、全局变量定义生命周期存储位置代码示例输出二、局部变量定义生命周期存储位置代码示例输出三、全局变量和局部变量的区别作用域

MyBatis中$与#的区别解析

《MyBatis中$与#的区别解析》文章浏览阅读314次,点赞4次,收藏6次。MyBatis使用#{}作为参数占位符时,会创建预处理语句(PreparedStatement),并将参数值作为预处理语句... 目录一、介绍二、sql注入风险实例一、介绍#(井号):MyBATis使用#{}作为参数占位符时,会

深入理解Go语言中二维切片的使用

《深入理解Go语言中二维切片的使用》本文深入讲解了Go语言中二维切片的概念与应用,用于表示矩阵、表格等二维数据结构,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧... 目录引言二维切片的基本概念定义创建二维切片二维切片的操作访问元素修改元素遍历二维切片二维切片的动态调整追加行动态

Android kotlin中 Channel 和 Flow 的区别和选择使用场景分析

《Androidkotlin中Channel和Flow的区别和选择使用场景分析》Kotlin协程中,Flow是冷数据流,按需触发,适合响应式数据处理;Channel是热数据流,持续发送,支持... 目录一、基本概念界定FlowChannel二、核心特性对比数据生产触发条件生产与消费的关系背压处理机制生命周期

Javaee多线程之进程和线程之间的区别和联系(最新整理)

《Javaee多线程之进程和线程之间的区别和联系(最新整理)》进程是资源分配单位,线程是调度执行单位,共享资源更高效,创建线程五种方式:继承Thread、Runnable接口、匿名类、lambda,r... 目录进程和线程进程线程进程和线程的区别创建线程的五种写法继承Thread,重写run实现Runnab