我创建了一个带有“apply”陷阱的代理对象:
var target = {},
handler = { apply: () => 42 }
proxy = new Proxy(target, handler);
proxy(); // TypeError: proxy is not a function
因此,Proxy对象应该是可调用的。然而,这不起作用。
为什么?
根据Proxy对象的[[Call]]内部方法的定义它应该可以工作:
但是,有一个问题:并不是所有的Proxy对象都有[[Call]]方法:
- 令 trap 为 GetMethod(handler,
)。"apply"
- 返回 Call(trap, handler, «target, thisArgument, CreateArrayFromList(argumentsList)»).
代理外来对象只有一个 [[Call]] 内部方法,如果 其 [[ProxyTarget]]因此,目标必须是函数对象:internal slot 的初始值是 具有 [[Call]] 内部方法的对象。
var target = () => {},
handler = { apply: () => 42 }
proxy = new Proxy(target, handler);
proxy(); // 42
请注意,我使用箭头函数定义
target
是为了创建一个不是构造函数的函数对象。这样Proxy对象可以被调用但不能实例化。如果你也想添加一个“构造”陷阱,目标必须有一个 [[Construct]] 方法,所以用函数声明或函数表达式来定义它。
虽然现有的答案有效,但对于某些目的来说,还有另一种选择可能更“干净”:
class Callable extends Function {
constructor() {
super()
return new Proxy(this, {
apply: (target, thisArg, args) => target._call(...args)
})
}
_call(...args) {
console.log(this, args)
}
}
您可以像往常一样定义代理中的所有陷阱。
所有功劳都归于
arccoza 和他的要点。
您可以使用以下实用程序将调用功能添加到任何现有对象(不仅仅是函数)extendAsCallableProxy
。
type WrappedTarget<T extends object> = {
(...args: any[]): any;
originalTarget: T;
};
function createProxyHandlerForCallableAroundWrappedTarget<T extends object>(): ProxyHandler<WrappedTarget<T>> {
return {
// apply(target: WrappedTarget<T>, thisArg: any, argArray: any[]): any {
// return onApply(target, ...argArray);
// },
get(target: WrappedTarget<T>, p: string | symbol, receiver: any): any {
if (p !== 'apply') {
return Reflect.get(target.originalTarget, p, receiver);
} else {
return Reflect.get(target, p, receiver);
}
},
set(target: WrappedTarget<T>, p: string | symbol, value: any, receiver: any): boolean {
return Reflect.set(target.originalTarget, p, value, receiver);
},
construct(target: WrappedTarget<T>, argArray: any[], newTarget: any): object {
return Reflect.construct(target.originalTarget as any, argArray, newTarget);
},
has(target: WrappedTarget<T>, p: string | symbol): boolean {
return Reflect.has(target.originalTarget, p);
},
setPrototypeOf(target: WrappedTarget<T>, v: object | null): boolean {
return Reflect.setPrototypeOf(target.originalTarget, v);
},
deleteProperty(target: WrappedTarget<T>, p: string | symbol): boolean {
return Reflect.deleteProperty(target.originalTarget, p);
},
defineProperty(target: WrappedTarget<T>, property: string | symbol, attributes: PropertyDescriptor): boolean {
return Reflect.defineProperty(target.originalTarget, property, attributes);
},
getPrototypeOf(target: WrappedTarget<T>): object | null {
return Reflect.getPrototypeOf(target.originalTarget);
},
ownKeys(target: WrappedTarget<T>): ArrayLike<string | symbol> {
return Reflect.ownKeys(target.originalTarget);
},
isExtensible(target: WrappedTarget<T>): boolean {
return Reflect.isExtensible(target.originalTarget);
},
preventExtensions(target: WrappedTarget<T>): boolean {
return Reflect.preventExtensions(target.originalTarget);
},
getOwnPropertyDescriptor(target: WrappedTarget<T>, p: string | symbol): PropertyDescriptor | undefined {
return Reflect.getOwnPropertyDescriptor(target.originalTarget, p);
},
};
}
export function extendAsCallableProxy<T extends object>(target: T, onApply: (target: T, ...args: any[]) => any): T {
const wrappedTarget = function (...args: any[]) {
return onApply(target, ...args);
};
(wrappedTarget as any).originalTarget = target;
return new Proxy(wrappedTarget, createProxyHandlerForCallableAroundWrappedTarget()) as T;
}