「Vue系列」之为什么用Proxy取代Object.defineProperty?

1,546 阅读3分钟

这是我参与11月更文挑战的第2天,活动详情查看:2021最后一次更文挑战


老规矩总结放前面:

defineProperty本身是可以监控到数组下标的变化的,只是Vue开发团队通过对性能/体验的性价比考虑放弃了这个特性,而且因为他只能进入对象代理已存在的属性不如Proxy能直接代理整个对象,所以才被替换了。     相信不用我说,各位大佬也都知道vue2.x就是通过Object.defineProperty实现数据劫持,然后结合订阅发布者模式实现响应式。不清楚的同学可以看看相关的文章,我就不嫌丑了。同时我们也知道,vue2.x通过defineProperty方法只是劫持了对象,但是对于数组的处理却是通过重写原生方法实现的响应式,那为什么会这样呐?
    我也看了很多的文章,基本都是说defineProperty本身存在有缺陷,没有办法监听数组的变化,但是实际上defineProperty是可以通过数组下标来实现劫持的,毕竟数组对象是一家嘛,下面就是我做的一点验证:

const arr = ['a','b','c','d'];
function defineReactive(data, key) {
    Object.defineProperty(data, key, {
        get: function() {
            console.log('key:' + key)
        },
        set: function(value) {
            console.log('value:' + value)
        }
    });
};
function Observe(data) {
    Object.keys(data).forEach(function(key) {    
        defineReactive(data, key, data[key])  
    })
};
Observe(arr);

arr[1];
arr[2] = '3';

控制台的输出结果是这样的: image.png     所以说defineProperty本身是可以监控到数组下标的变化的,只是Vue 从通过对性能/体验的性价比考虑放弃了这个特性,defineProperty简直委屈死了。

    关于defineProperty实现劫持为什么在性能和体验上不如重写数组方法也有人在github上问过尤大大,但是尤大大的回答属实太过精炼而我也不知道怎么去比较两者的性能,所以只能硬记了。😅

image.png     同样的vue3以上的版本是用了proxy替换了defineProperty,那这又是为什么类?

  1. defineProperty是一个代价很高的操作,因为他需要遍历整个对象,然后直接操作对象的属性。所以只能监听这个属性值的变化,而不能去监听对象属性的新增和删除
  2. proxy代理的是目标对象,而不是属性减少了对原始对象的操作,保持了对象的稳定性

Tipes:Proxy只会对对象的第一层做代理,Vue3是怎么处理深层对象的代理?
    判断当前Reflect.get的返回值是否为Object,如果是则再通过reactive方法做代理, 这样就实现了深度观测。

    但是还有一些文章说还有一部分原因是 proxy在性能上比defineProperty好。可proxy真的比defineProperty性能更好速度更快吗?仁者见仁智者见智实际上 Proxy 在性能上是要比 Object.defineProperty 差的Thoughts on ES6 Proxies Performance 这篇文章,也可以参考黄轶大佬的 repo。

    总结下来就是defineProperty还是挺好一个方法了,还有就是纸上得来终觉浅,觉知此事要躬行。一定要多看多实践,要有自己的理解和认知,也感谢当时我在沸点提问时候几位大佬的解答,加油!!

最后祝各位大佬学习进步,事业有成!🎆🎆🎆

Tipes:往期内容
面试的时候面试官是这样问我Js基础的,角度真刁钻
「算法基础」之二叉树的遍历和搜索
「Vue系列」使用Teleport封装一个弹框组件
「Vue系列」之面试官问NextTick是想考察什么?