每当说到Proxy与Object.defineProperty,可能很多前端er都能脱口而出:Vue2的数据响应式基于Object.defineProperty,Vue3则是基于Proxy。这没错,但这总给人一种本末倒置的感觉,就好像这俩API就是为Vue服务的一样。今天就来看看这两个API的区别。
TL&DR;
废话少说,我们先一句话概括一下这两者的区别:
Proxy用来创建一个代理对象,它可以拦截和重定义对象的基本操作。而Object.definePropery的行为属于对象的基本操作之一。
对象的基本操作
上面的总结中提到了一个概念:对象的基本操作。什么是对象的基本操作呢?比如访问对象属性、修改对象属性、删除对象属性、判断对象是否拥有某个属性、甚至调用某个函数等,这些都是对象的基本操作,这些操作实际JS底层都是通过调用对象的内部方法完成的。比如访问对象属性实际是调用对象内部方法[[Get]],修改对象属性实际是调用对象内部方法[[Set]],Object.defineProperty实际是调用了对象内部方法[[DefineOwnProperty]]。
ecma262文档规定了对象有哪些内部方法:
函数也是对象,它是“可调用的对象”。函数调用分为普通函数调用和构造函数调用。所以,函数对象额外有两个内部方法:
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里的这些get和set属性在Proxy的概念里叫陷进(trap),它们和对象的内部方法是一一对应的,总共有13个:
| Internal method | Corresponding trap |
|---|---|
[[GetPrototypeOf]] | getPrototypeOf() |
[[SetPrototypeOf]] | setPrototypeOf() |
[[IsExtensible]] | isExtensible() |
[[PreventExtensions]] | preventExtensions() |
[[GetOwnProperty]] | getOwnPropertyDescriptor() |
[[DefineOwnProperty]] | defineProperty() |
[[HasProperty]] | has() |
[[Get]] | get() |
[[Set]] | set() |
[[Delete]] | deleteProperty() |
[[OwnPropertyKeys]] | ownKeys() |
函数对象的:
| Internal method | Corresponding trap |
|---|---|
[[Call]] | apply() |
[[Construct]] | construct() |