无法为代理对象设置“应用”陷阱

问题描述 投票:0回答:3

我创建了一个带有“apply”陷阱的代理对象:

var target = {},
    handler = { apply: () => 42 }
    proxy = new Proxy(target, handler);
proxy(); // TypeError: proxy is not a function

因此,Proxy对象应该是可调用的。然而,这不起作用。

为什么?

javascript function ecmascript-6 proxy-classes
3个回答
25
投票

根据Proxy对象的[[Call]]内部方法的定义它应该可以工作:

但是,有一个问题:并不是所有的Proxy对象都有[[Call]]方法:

代理外来对象只有一个 [[Call]] 内部方法,如果 其 [[ProxyTarget]]

internal slot 的初始值是 具有 [[Call]] 内部方法的对象。

因此,目标必须是函数对象:

var target = () => {}, handler = { apply: () => 42 } proxy = new Proxy(target, handler); proxy(); // 42

请注意,我使用箭头函数定义

target

 是为了创建一个不是构造函数的函数对象。这样Proxy对象可以被调用但不能实例化。

如果你也想添加一个“构造”陷阱,目标必须有一个 [[Construct]] 方法,所以用函数声明或函数表达式来定义它。


1
投票
我从 Google 来到这里搜索“可调用代理”。

虽然现有的答案有效,但对于某些目的来说,还有另一种选择可能更“干净”:

class Callable extends Function { constructor() { super() return new Proxy(this, { apply: (target, thisArg, args) => target._call(...args) }) } _call(...args) { console.log(this, args) } }
您可以像往常一样定义代理中的所有陷阱。

所有功劳都归于
arccoza 和他的要点


0
投票
使用其他答案的信息,我快速拼凑了一个通用实用程序。

您可以使用以下实用程序将调用功能添加到任何现有对象(不仅仅是函数)
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; }
    
© www.soinside.com 2019 - 2024. All rights reserved.