Proxy
defineProperty的问题
虽然
Object.defineProperty
能够劫持对象的属性,但是需要对对象的每一个属性进行遍历劫持;如果对象上有新增的属性,则需要对新增的属性再次进行劫持;如果属性是对象,还需要深度遍历。这也是为什么Vue给对象新增属性需要通过$set
的原因,其原理也是通过Object.defineProperty
对新增的属性再次进行劫持。Object.defineProperty
除了能够劫持对象的属性,还可以劫持数组;虽然数组没有属性,但是我们可以把数组的索引看成是属性: 虽然我们监听到了数组中元素的变化,但是和监听对象属性面临着同样的问题,就是新增的元素并不会触发监听事件,除此之外,直接修改数组的length
属性也会导致Object.defineProperty
的监听失败。
为此,Vue的解决方案是劫持Array.property
原型链上的7个函数,我们通过下面的函数简单进行劫持:
Proxy
相较于Object.defineProperty劫持某个属性,Proxy则更彻底,不在局限某个属性,而是直接对整个对象进行代理。
ES6中对Proxy的描述为:Proxy
可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。
Proxy本身是一个构造函数,通过new Proxy
生成拦截的实例对象,让外界进行访问;构造函数中的target
就是我们需要代理的目标对象,可以是对象或者数组;handler
和Object.defineProperty
中的descriptor描述符有些类似,也是一个对象,用来定制代理规则。
可以看到Proxy直接代理了target
整个对象,并且返回了一个新的对象,通过监听代理对象上属性的变化来获取目标对象属性的变化;而且我们发现Proxy不仅能够监听到属性的增加,还能监听属性的删除,比Object.defineProperty
的功能更为强大。
不管是数组下标或者数组长度的变化,还是通过函数调用,Proxy都能很好的监听到变化;而且除了我们常用的get、set,Proxy更是支持13种拦截操作。
可以看到Proxy相较于Object.defineProperty在语法和功能上都有着明显的优势;而且Object.defineProperty存在的缺陷,Proxy也都很好地解决了。