我对这个问题有几个部分答案/评论:
1.首先:浏览器的行为似乎同时发生了变化。 Chrome 106 和 Firefox 105 都不再抛出初始示例中描述的类型错误,但它们仍然为toString()
方法比原始对象:
new Proxy(a=>a, {apply:a=>a}) + ''
导致以下结果toString()
返回值:
'function () { [native code] }'
而原始 lambda 表达式(a=>a) + ''
简单地产生
'a=>a'
2. OP 试图改变toString
代理对象的实际上正在改变toString
- 目标方法args[0]
(这是a=>a
)通过始终对其自身进行评估args[0] + ''
(从而消除任何this
)。因此,当重新定义toString
使用代理调用目标的函数,如下所示this
争论,已经没有什么区别了。
对于任何在搜索如何分配自定义的问题时绊倒的人toString
对于代理(不要求不可检测),也许应该提及在不更改目标的情况下执行此操作的标准方法:
可以用以下方法完成get https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy/get- 处理程序的陷阱,因此将处理程序扩展为:
{
apply: a=>a,
get(target, prop, receiver) {
if(prop == "toString") {
return target.toString.bind(target);
} else {
return Reflect.get(target, prop, receiver); // or your favorite forwarding
}
}
}
这也产生相同的结果toString()
代理和目标的答案。通过将其他函数属性一般绑定到目标,可以使“else”情况变得更有用,至少如果它们没有显式绑定到不同的接收器(请参阅私有财产转发 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy#no_private_property_forwarding).
3. OP的全局替换问题Function
不过,这是可以克服的。至少我是这么认为的。确实有参考原著的Function
其实例的构造函数:
(a=>a).constructor === Function; // true
但我们不需要替换 Function 本身,只需替换它的属性,或者更准确地说,甚至不需要替换这些属性,而只需替换其属性的属性之一。考虑以下简单的重新定义。它不是很复杂(仅针对一个代理),但至少可以完成这项工作toString
该代理的属性:
const proxy = new Proxy(a=>a,{apply:a=>a});
{
const nativeToString = Function.prototype.toString;
Function.prototype.toString = function() {
return this === proxy? "a=>a" : nativeToString.call(this);
}
}
现在至少这个测试是成功的:
(x=>x).constructor.prototype.toString.call(proxy); // returns "a=>a"
4.然而,作为@jmrk 在他的回答中指出 https://stackoverflow.com/questions/45688944/javascript-make-proxy-undetectable/45698826#45698826,代理可检测的原因是存在一些特殊的内部槽,这些槽无法通过代理处理程序陷阱拦截。后者仅拦截标准对象内部方法 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy#object_internal_methods。因此,任务是找到通过外部 javascript 调用触发这些内部槽的所有方法,然后覆盖相应的全局可访问函数,就像使用toString()
多于。我不确定触发内部插槽的进一步方法Function
,但是当尝试使用代理来替代代理时也会出现同样的问题HTMLElement https://stackoverflow.com/questions/56008415/how-to-create-a-proxy-for-htmlelement,我们称之为htmlProxy
。在这种情况下调用getComputedStyle(htmlProxy)
抛出类型错误,即使htmlProxy
标识为 HTMLElement。相似地element.insertNode(htmlElementProxy)
抛出异常。似乎需要重新定义所有这些全局可访问的触发函数,以使代理无法检测到。
内部槽可以看作是内置对象的私有属性。因此,非内置类/对象的私有属性出现同样的问题也就不足为奇了。可以使用上面(第 2 点)提到的私有财产转发 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy#no_private_property_forwarding当通过代理调用访问私有属性的方法时,获得与目标对象相同的代理行为。假设我们有someObject
类型的SomeClass
与私人成员#member
和一个公共吸气剂getMember
。然后我们可以做代理someObject
通过转发也成功调用此函数this
到目标,这样proxy.getMember()
只会给成员someObject
。但是,如果有人打电话SomeClass.prototype.getMember.call(proxy)
,又会出现异常,唯一的办法就是重新定义SomeClass.prototype.getMember
.