javascript 如何将 Proxy 对象作为上下文传递给闭包

How to pass a Proxy object as context to a closure in javascript

注意:我现在相信这个问题是基于关于 javascript 规范的假设,这些规范实际上是特定于实现的。

我正在尝试为复杂的动态 javascript 应用程序构建运行时调试挂钩系统。一系列的选择让我选择使用 javascript 代理和反射元编程结构在我正在调试的应用程序中插入函数调用,将所有传入的函数参数包装在 Proxy/Reflect 结构中。

该方法涉及用代理替换高级应用程序功能,并使用陷阱和处理程序来提供调试功能,最终以透明的方式将参数传递给应用程序。所有 属性 get/set 和函数执行都正常。但是,通过将所有对象和函数包装在 Proxies 中,可以跟踪运行时。

我正在将这个挂钩系统安装到 Chrome。

(注意:请不要提供暗示调试挂钩的不同方法的答案 - 选项已经过广泛评估。)

问题是应用程序中的一些 javascript 方法调用闭包并传递 "this" 参数。当 "this" 参数包装在代理中时,运行时无法执行闭包,而是抛出 "Illegal Invocation" 异常。

我尝试重新设计调试挂钩系统,使其不为某些方法包装参数,或有选择地包装参数。我一直无法找到一种方法来判断参数是否打算用作上下文,从而使尝试这种方法的代码硬编码为许多可能的方法和调用约定。最终,这对于调用约定边缘案例来说太脆弱了,并且需要太多的案例语句。

我还删除了在传递参数之前包装参数的逻辑。这消除了调试挂钩系统的好处,因此我总是恢复包装所有传入参数的逻辑。

alert.apply(this, [1]);

p = new Proxy(this, {});

try {
    alert.apply(p, [1]);
} catch (e) {
    console.log(e);
}

这会引发 "Illegal Invocation" 异常。

typeof this === 'object'
true

但上下文似乎和其他一切一样都是对象。

我希望将 Proxy() 传递给上下文应该会成功调用。除此之外,我希望上下文的类型足够具体,以确定它是否应该包装在 Proxy() 中。

我有两个问题。

(1) javascript 中上下文绑定闭包的语义是什么,会导致绑定到代理(上下文)因非法调用而失败?

(2) 什么类型的对象是上下文,javascript 方法如何通过在运行时检查其属性来区分一个对象与其他 javascript 对象?

What type of object are contexts, and how can a javascript method tell one apart from other javascript objects by inspecting its properties at runtime?

没有特殊类型。每个对象都可以通过调用方法成为上下文。大多数将成为方法调用上下文的对象确实具有该方法作为(继承的)属性,但不能保证。

你无法区分它们。

What are the semantics of context binding in javascript that would cause binding to a Proxy(context) to fail with an illegal invocation?

当方法是本地方法时。在用户代码函数中,作为代理的 this 上下文没有任何区别,当您访问它时,它只会充当代理。

问题是本机方法期望它们的 this 参数是相应类型的本机对象。当然,这些对象仍然是 javascript 对象,但它们也可能包含有关 内部属性 的私有数据。代理的目标和处理程序引用也通过此类内部属性实现,例如 - 您有时可以在调试器中检查它们。本机方法不知道打开代理并改为使用它的目标,它们只是查看对象并注意到它不具有该方法完成其工作所需的内部属性。你也可以通过一个普通的 {}

此类方法的示例可以作为 ECMAScript 运行时的内置函数找到:

  • Map.prototype.has/get/set/
  • Number/String/Boolean prototype methods

而且(甚至更多)作为环境提供的宿主对象:

  • window.alert/prompt
  • EventTarget.prototype.addEventListener/removeEventListener
  • document.createElement
  • Element.prototype.appendChild/remove/
  • 真的只是特定于浏览器的任何东西
  • 但也适用于其他环境,例如

I have tried unwrapping Proxies in the right places by coding in edge cases and by blanket/heuristic policies.

我认为唯一合理的方法是 check whether the called function is a native one,并为它们展开所有参数(包括 this 参数)。

只有少数本机函数可以列入白名单,例如 Array.prototype 上的大部分函数,​​这些函数在语言标准中明确指定可用于任意对象。