使用 ES6 Proxy 和 node.js 的非法调用错误
Illegal invocation error using ES6 Proxy and node.js
我不明白为什么以下代码不起作用:
var os = new Proxy(require('os'), {});
console.log( os.cpus() ); // TypeError: Illegal invocation
而
var os = require('os');
console.log(Reflect.apply(os.cpus, os, []));
或
var os = new Proxy(require('os'), {});
console.log( os.platform() );
按预期工作。
刚刚浏览了 os
package in the Node repo, it appears that the cpus()
is exported from binding.getCPUs
的源代码,它是 Node 运行time 环境中的一个 C hook。
cpus()
因此将 binding
对象作为函数上下文,然后通过代理丢失,给你 IllegalInvocation
错误,因为当你这么称呼它——尽管我不清楚细节。
另一方面,platform()
导出为 function () { return process.platform; }
,因此它只是一个 returns 对象的函数,不需要 运行在特定的上下文中,因为节点函数上下文将默认指定 process
变量(除非它已被覆盖)。
以下行为表明将 os
作为上下文应用到 cpus
函数将起作用——函数对象上的代理在调用属性时显然会丢失函数上下文。
const os = require('os');
const proxy = new Proxy(os, {}); // proxy of object, functions called get proxy context rather than os context
const cpus = new Proxy(os.cpus, {}); // proxy of function, still has os context
console.log(os.cpus()); // works (duh)
console.log(cpus()); // works
console.log(proxy.cpus.apply(os, [])); // works
console.log(proxy.cpus()); // fails with IllegalInvocation
注意:如果有人能把JS函数上下文的细节弄清楚,我也很想看看答案。
构图如何:
const os = require('os');
const proxy = new Proxy(os, {});
Object.getOwnPropertyNames(os).forEach(k => {
var v = os[k];
if(typeof v === "function") proxy[k] = v.bind(os);
});
//the `!!` because I don't want the actual print
//only a `true` or an `Error`
console.log(!!os.cpus());
console.log(!!proxy.cpus());
console.log(!!proxy.cpus.apply(proxy, []));
所有这些作为 "replace" new Proxy()
的效用函数,其中 handler.bindTargetFunctions
可以是
- 要绑定的 keyNames 数组(这样你就可以具体)
- 或任何真值或假值,以确定是否目标上的所有函数都应该被绑定
代码:
function proxy(target, handler){
const _proxy = new Proxy(target, handler);
if(handler.bindTargetFunctions){
let bindTargetFunctions = handler.bindTargetFunctions;
if(!Array.isArray(bindTargetFunctions)){
bindTargetFunctions = Object.getOwnPropertyNames(target)
.filter(key => typeof target[key] === "function");
}
bindTargetFunctions.forEach(key => {
_proxy[key] = target[key].bind(target);
});
}
return _proxy;
}
const os = proxy(require('os'), { bindTargetFunctions: true });
//or
//const os = proxy(require('os'), { bindTargetFunctions: ["cpus"] });
console.log(os.cpus());
编辑:
Currently I try to bind functions directly in my get handler (see github.com/FranckFreiburger/module-invalidate/blob/master/…), the drawback of my solution is that each access to a function returns a new binding.
我在评论中提到了缓存。这是这个缓存的样子:
function createProxy(mod){
var cache = Object.create(null);
return new Proxy(function(){}, {
get(target, property, receiver) {
var val = Reflect.get(mod._exports, property, receiver);
if(typeof val === "function"){
if(!(property in cache) || cache[property].original !== val){
cache[property] = {
original: val,
bound: bal.bind(mod._exports)
}
}
val = cache[property].bound;
}else if(property in cache){
delete cache[property];
}
return val;
}
});
}
不,我不认为此缓存是常规对象。不是因为它继承自 null,而是因为在逻辑上,对我来说这是一个 dictionary/map。而且我不知道您为什么会扩展或代理特定词典。
我不明白为什么以下代码不起作用:
var os = new Proxy(require('os'), {});
console.log( os.cpus() ); // TypeError: Illegal invocation
而
var os = require('os');
console.log(Reflect.apply(os.cpus, os, []));
或
var os = new Proxy(require('os'), {});
console.log( os.platform() );
按预期工作。
刚刚浏览了 os
package in the Node repo, it appears that the cpus()
is exported from binding.getCPUs
的源代码,它是 Node 运行time 环境中的一个 C hook。
cpus()
因此将 binding
对象作为函数上下文,然后通过代理丢失,给你 IllegalInvocation
错误,因为当你这么称呼它——尽管我不清楚细节。
platform()
导出为 function () { return process.platform; }
,因此它只是一个 returns 对象的函数,不需要 运行在特定的上下文中,因为节点函数上下文将默认指定 process
变量(除非它已被覆盖)。
以下行为表明将 os
作为上下文应用到 cpus
函数将起作用——函数对象上的代理在调用属性时显然会丢失函数上下文。
const os = require('os');
const proxy = new Proxy(os, {}); // proxy of object, functions called get proxy context rather than os context
const cpus = new Proxy(os.cpus, {}); // proxy of function, still has os context
console.log(os.cpus()); // works (duh)
console.log(cpus()); // works
console.log(proxy.cpus.apply(os, [])); // works
console.log(proxy.cpus()); // fails with IllegalInvocation
注意:如果有人能把JS函数上下文的细节弄清楚,我也很想看看答案。
构图如何:
const os = require('os');
const proxy = new Proxy(os, {});
Object.getOwnPropertyNames(os).forEach(k => {
var v = os[k];
if(typeof v === "function") proxy[k] = v.bind(os);
});
//the `!!` because I don't want the actual print
//only a `true` or an `Error`
console.log(!!os.cpus());
console.log(!!proxy.cpus());
console.log(!!proxy.cpus.apply(proxy, []));
所有这些作为 "replace" new Proxy()
的效用函数,其中 handler.bindTargetFunctions
可以是
- 要绑定的 keyNames 数组(这样你就可以具体)
- 或任何真值或假值,以确定是否目标上的所有函数都应该被绑定
代码:
function proxy(target, handler){
const _proxy = new Proxy(target, handler);
if(handler.bindTargetFunctions){
let bindTargetFunctions = handler.bindTargetFunctions;
if(!Array.isArray(bindTargetFunctions)){
bindTargetFunctions = Object.getOwnPropertyNames(target)
.filter(key => typeof target[key] === "function");
}
bindTargetFunctions.forEach(key => {
_proxy[key] = target[key].bind(target);
});
}
return _proxy;
}
const os = proxy(require('os'), { bindTargetFunctions: true });
//or
//const os = proxy(require('os'), { bindTargetFunctions: ["cpus"] });
console.log(os.cpus());
编辑:
Currently I try to bind functions directly in my get handler (see github.com/FranckFreiburger/module-invalidate/blob/master/…), the drawback of my solution is that each access to a function returns a new binding.
我在评论中提到了缓存。这是这个缓存的样子:
function createProxy(mod){
var cache = Object.create(null);
return new Proxy(function(){}, {
get(target, property, receiver) {
var val = Reflect.get(mod._exports, property, receiver);
if(typeof val === "function"){
if(!(property in cache) || cache[property].original !== val){
cache[property] = {
original: val,
bound: bal.bind(mod._exports)
}
}
val = cache[property].bound;
}else if(property in cache){
delete cache[property];
}
return val;
}
});
}
不,我不认为此缓存是常规对象。不是因为它继承自 null,而是因为在逻辑上,对我来说这是一个 dictionary/map。而且我不知道您为什么会扩展或代理特定词典。