Proxy与Object.defineProperty的区别

71 阅读2分钟

每当说到ProxyObject.defineProperty,可能很多前端er都能脱口而出:Vue2的数据响应式基于Object.defineProperty,Vue3则是基于Proxy。这没错,但这总给人一种本末倒置的感觉,就好像这俩API就是为Vue服务的一样。今天就来看看这两个API的区别。

TL&DR;

废话少说,我们先一句话概括一下这两者的区别:

Proxy用来创建一个代理对象,它可以拦截和重定义对象的基本操作。而Object.definePropery的行为属于对象的基本操作之一。

对象的基本操作

上面的总结中提到了一个概念:对象的基本操作。什么是对象的基本操作呢?比如访问对象属性、修改对象属性、删除对象属性、判断对象是否拥有某个属性、甚至调用某个函数等,这些都是对象的基本操作,这些操作实际JS底层都是通过调用对象的内部方法完成的。比如访问对象属性实际是调用对象内部方法[[Get]],修改对象属性实际是调用对象内部方法[[Set]]Object.defineProperty实际是调用了对象内部方法[[DefineOwnProperty]]

ecma262文档规定了对象有哪些内部方法:

Snipaste_2025-01-03_14-29-28.png

函数也是对象,它是“可调用的对象”。函数调用分为普通函数调用构造函数调用。所以,函数对象额外有两个内部方法:

Snipaste_2025-01-03_14-30-15.png

Proxy

ProxyAPI出现之前,JS中并不能直接访问到对象的内部方法。有了Proxy之后,我们就能通过第二个参数handler来拦截对象内部方法,加上一些我们自己的逻辑。比如:

const target = {
    foo: 'foo',
    bar: 'bar'
}
const handler = {
    get(target, key, receiver) {
        console.log(`get了${key}属性`)
        Reflect.get(target, key, receiver)
    },
    set(target, key, value, receiver) {
        console.log(`set了${key}属性,value是${value}`)
        return Reflect.set(target, key, value, receiver)
    },
}
const proxy = new Proxy(target, handler)
proxy.foo
proxy.bar = 'bar1'

handler里的这些getset属性在Proxy的概念里叫陷进(trap),它们和对象的内部方法是一一对应的,总共有13个:

Internal methodCorresponding trap
[[GetPrototypeOf]]getPrototypeOf()
[[SetPrototypeOf]]setPrototypeOf()
[[IsExtensible]]isExtensible()
[[PreventExtensions]]preventExtensions()
[[GetOwnProperty]]getOwnPropertyDescriptor()
[[DefineOwnProperty]]defineProperty()
[[HasProperty]]has()
[[Get]]get()
[[Set]]set()
[[Delete]]deleteProperty()
[[OwnPropertyKeys]]ownKeys()

函数对象的:

Internal methodCorresponding trap
[[Call]]apply()
[[Construct]]construct()