ES系列 ———— Object.defineProperty和Proxy的对比

简介

在前两篇文章中分别介绍了Object.definePropertyProxy两个新的特性,其实看起来Proxy更像是对Object.defineProperty的一种补充和完善(个人见解)。当然不是说Object.defineProperty很差,感觉更像是一开始的定位是为了处理对象的特定属性,但是在Vue等等框架的中被用来劫持整个对象属性,所以后面就出来了Proxy,更强大的劫持功能。

优缺点

根据上面两篇文章的介绍,大致优缺点如下:

Proxy 相对于 defineProperty 的优点

  • 对于对象已有属性:Object.defineProperty只能劫持对象的单个属性,如果想劫持整个对象就要循环递归调用Object.defineProperty。而Proxy拦截整个对象,并且返回一下新的对象。
  • 对于对象新增属性:Proxy劫持整个对象,对于新增的属性自动拦截。而Object.defineProperty需要重新劫持新增的属性
  • 对于数组操作: Object.defineProperty无法监控到数组下标的变化。而Proxy可以监听数组变化。
  • 拦截或劫持方法: Object.defineProperty描述符基本上分为两类数据描述符存取描述符通用描述符三种。而Proxy中有13traps方法供你选择。
  • 是否支持取消劫持: Object.defineProperty如果想取消劫持,只能重写描述符,但是configurable: false时就不能重写描述符了。而Proxy可以通过Proxy.revocable返回一个可取消的 Proxy 实例。
  • 浏览器对劫持或拦截的支持: Proxy在后续应该会有更好的支持,不然Vue也不会修改核心代码。
  • 性能: Proxy性能是比Object.defineProperty高的,在多个对象属性中。

Proxy 相对于 defineProperty 的缺点

  • this指向defineProperty因为只绑定对象的属性,一般不会涉及到this问题。而Proxy返回的对象的thistargetthis不相同。
  • 使用难度: 相对于Proxyapi,反而defineProperty上手更容易。

Proxy 和 defineProperty 的一些注意事项

  • 对象冻结:无论是definePropertyObject.freezeObject.seal(密封)都不是深度冻结,如果想深度冻结只能递归实现。
  • this问题Proxy在使用是要注意this指向问题。

各自实现双向绑定

现在的三大框架非常的流行,在数据流中分为两派React的单项数据流,Angluar/Vue的双向数据流。其实React也是实现了的双向数据绑定的,只不过要通过setState来触发。

在不同框架中实现双向数据绑定也是不相同的,大致如下图所示:

双向绑定

Object.definePropertyproxy都是Vue不同版本的重要组成部分,它们都是可以实现双向绑定中的数据劫持,其实也就是响应式对象,在以前的文章有深入 Vue 系列 Vue 中的响应式对象
深入 Vue 系列 Vue 中的依赖收集深入 Vue 系列 Vue 中的派发更新,如果感兴趣的可以去看看。

依照Vue代码中的双向绑定思路,大致分为以下三步:

  • 把普通对象通过Object.defineProperty变为响应式对象
  • 同时getter中收集依赖,也就是渲染wather
  • setter中派发更行

下面写的实例不会这么复杂,当然也会仿照Vue源码中的mvvm去写。

Object.defineProperty 实现双向绑定

简单实现

  • 劫持对象的get、set属性
  • input事件更新对象值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Static Template</title>
</head>
<body>
<main>
<p>请输入:</p>
<input type="text" id="mv" />
<p id="vm"></p>
</main>
<script src="index.js"></script>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 对象字面量
const obj = {};
// 对象配置描述符
Object.defineProperty(obj, 'value', {
get: function () {
console.log('get value');
},
set: function (newVal) {
console.log('set value');
document.getElementById('mv').value = newVal;
document.getElementById('vm').innerHTML = newVal;
}
});
// input绑定时间更行对象中的value值
const InputDom = document.getElementById('mv');
InputDom.addEventListener('input', function (event) {
console.log(event.target.value);
obj.value = event.target.value;
});

上面这个代码只是简单了实现最简单的效果,在input中输入代码,同时更新到p中。

发布订阅者模式

Proxy 实现双向绑定

总结

参考