在JavaScript中,Object.defineProperty
和 Proxy
是两种不同的机制,用于拦截和自定义对象的行为。当你将它们结合使用时,可能会遇到一些意想不到的行为,比如 apply
方法执行两次的情况。
Object.defineProperty
的作用:
Object.defineProperty
用于在对象上定义一个新属性,或者修改一个已有属性。它可以用来定义属性的 getter
和 setter
,从而在访问或修改属性时执行自定义逻辑。Proxy
的作用:
Proxy
用于创建一个对象的代理,从而可以拦截并自定义该对象的基本操作(如属性访问、赋值、枚举等)。Proxy
的 apply
陷阱用于拦截函数的调用。结合使用时的行为:
Object.defineProperty
和 Proxy
时,可能会发生以下情况:
Proxy
的 apply
陷阱中调用了原始函数,而该函数又被 Object.defineProperty
拦截(例如通过 getter
),那么 apply
陷阱可能会被触发两次。Proxy
拦截了函数调用,第二次触发是因为 Object.defineProperty
的 getter
被调用,导致函数再次被调用。function myFunction() {
console.log('Function executed');
}
const handler = {
apply(target, thisArg, argumentsList) {
console.log('Proxy apply trap');
return Reflect.apply(target, thisArg, argumentsList);
}
};
const proxy = new Proxy(myFunction, handler);
Object.defineProperty(window, 'myFunction', {
get() {
console.log('Getter called');
return proxy;
}
});
myFunction(); // 这里会触发两次 apply 陷阱
Getter called
Proxy apply trap
Function executed
Proxy apply trap
Function executed
myFunction()
被调用时,首先会触发 Object.defineProperty
的 getter
,返回 proxy
。proxy
的 apply
陷阱被触发,执行 Reflect.apply
,调用原始函数 myFunction
。myFunction
执行过程中,如果再次访问 myFunction
,getter
会被再次触发,导致 apply
陷阱再次执行。为了避免 apply
方法执行两次,可以考虑以下方法:
避免在 getter
中返回 Proxy
:
Object.defineProperty
的 getter
中返回 Proxy
对象。使用标志位:
apply
陷阱中使用标志位来避免重复执行。let isExecuting = false;
const handler = {
apply(target, thisArg, argumentsList) {
if (isExecuting) return;
isExecuting = true;
console.log('Proxy apply trap');
const result = Reflect.apply(target, thisArg, argumentsList);
isExecuting = false;
return result;
}
};
const proxy = new Proxy(myFunction, handler);
Object.defineProperty(window, 'myFunction', {
get() {
console.log('Getter called');
return proxy;
}
});
myFunction(); // 现在只会触发一次 apply 陷阱
Getter called
Proxy apply trap
Function executed
通过这种方式,你可以确保 apply
陷阱只执行一次。