阅读 188

ECMAscript新特性 - 代理对象 Proxy

如果我们要监视某个对象当中的属性读写,可以使用ES5所提供的 Object.defineProperty 这样的方法来去为我们的对象添加属性,这样的话我们就可以捕获到对象当中属性的读写过程。这种方法应用的非常广泛,在 vue 3.0 以前的版本就是使用这样一个方法来去实现的数据双向绑定。在 ES2015 当中全新设计了一个叫做 Proxy 的类型, 它就是专门用来为对象设置访问代理器的。可以把代理想像成门卫,也就是说不管你进去拿东西还是往里放东西都必须要经过这样一个代理。通过 Proxy 就可以轻松监视到对象的读写过程,相比于 defineProperty Proxy 它的功能要更为强大,使用起来也更为方便。Proxy 的构造函数第一个参数就是需要代理的目标对象;第二个参数也是一个对象可以把这个对象称之为代理的处理对象,在这个对象中可以通过get 方法来去监视属性的访问,set 方法监视对象当中属性的写入。

const person = {
    name: 'zce',
    age: 20,
}
const personProxy = new Proxy(person, {
    get(target, property) { // 接收两个参数,第一个是所代理的目标对象;第二个是外部所访问的属性名
        // 这个方法的返回值可以作为外部访问这个属性得到的结果
        return property in target ? target[property] : undefined
    },
    set(target, property, value) { // 默认接收三个参数:代理目标对象、写入的属性名称、写入的属性值
        if (property === 'age') {
            if (!Number.isInteger(value)) {
                throw new TypeError(`${value} is not a int`)
            }
        }
        target[property] = value
    }
})
复制代码

了解了Proxy 的基本用法过后,接下来在深入的探索一下相比于 Object.defineProperty Proxy 到底有哪些优势。

首先最明显的优势就是 Proxy 要更为强大一些,这个强大具体体现在 defineProperty 只能监视属性的读写,Proxy 能够监视到更多的对象操作,例如 delete 操作或者对像当中方法的调用等等。

const personProxy = new Proxy(person, {
    deleteProperty (target, property) { // 这个方法会在外部对当前代理对象进行 delete 操作时会自动执行
        delete target[property]
    }
})
delete personProxy.age
复制代码

下图是Proxy的操作文档

其次 Proxy 更好的支持数组对象的监视,以往我们想要通过 defineProperty 去监视数组的操作最常见的一种的方式就是通过重新数组的操作方法,这也是 vue 当中所使用的方式大体的思路就是通过自定义的方法去覆盖掉数组原型对象上的 push、shift 之类的方法,一次来去劫持对应的方法调用过程。

const list = [];
const listProxy = new Proxy(list, {
    set (target, property, value) { // 调用 push 方法时, property 对应的是数组的下标,value是下标所对应的值
        target[property] = value
        return true // 表示写入成功
    }
})
listProxy.push(100)
复制代码

最后 Proxy 相比于 Object.defineProperty 的一点优势就是 Proxy 是以非侵入的方式监管了整个对象的读写,也就是说一个已经定义好的对象,我们不需要对对象本身去做任何的操作就可以监视到它内部成员的读写。而 Object.definePropery 的方式就要求我们必须要通过特定的方式单独去定义对像当中那些需要被监视的属性,对于一个已经存在的对象我们要想去监视它的属性,我们需要去做很多额外的操作。

const person = {};
Object.defineProperty(person, 'name', {
    get () {
        console.log('name 被访问');
        return person._name
    },
    set (value) {
        console.log('name 被设置')  
        person._name = value
    }
})
person.name = 'jack'
console.log(person.name)
复制代码
文章分类
前端
文章标签