Javascript 有没有办法在使用
new Function(...)
创建函数时指定用作全局范围的对象?
例如,考虑一个字符串
var str = "return foo()"; // user supplied, generated by a trusted program
我们可以用这个字符串创建一个函数,传入某些参数
var fn = new Function(str);
var result = fn()
如果
foo
在全局范围内定义(浏览器中的 window
或 Node.js 中的 GLOBAL.global
),则此方法有效。
但是有没有办法给这个新创建的函数一个对象,例如
{foo: function() { return 42; }}
并告诉它使用它作为全局范围?
这里的动机是可能假设存在于全局范围内的可能函数的范围是未知的。我想使用 ES6
Proxy
定义一个对象,该对象为意外函数提供默认实现,并将该对象传入以用作函数内的全局作用域。
我知道可以显式定义并传递特定参数,例如
var fn = new Function("foo", str);
var result = fn(function() { return "bar"; })
但这行不通,因为我想处理我们没有预料到的方法。
也可以传入一个对象,例如
var scope = { foo: function() { return 42; }
var fn = new Function("scope", str);
var result = fn(scope)
但这行不通,因为字符串说
"return foo()"
不是 "return scope.foo()"
编辑
根据@ChiragRavindra 和@RobG 的评论建议的答案...
var str = "return 'sand'+foo(); "
var o = {foo: function() { return "bar"}}
var fn = new Function("obj", "with (obj) { " + str + "}")
fn = fn.bind(o,o)
fn(str) //==> "sandbar"
是的,可以。定义一个
Proxy
,用函数解析每个属性。因此,要实现第二个示例,我们可以这样做:
function factory (string) {
const callable = new Function(`with(this){${string}}`);
return function fn (method) {
return callable.call(new Proxy({}, {
has () { return true; },
get (target, key) { if (typeof key !== 'symbol') return method; }
}));
};
}
const fn = factory('return foo()');
const result = fn(() => 42);
console.log(result);
在严格模式下已弃用并禁止使用
with
(MDN 文档)。
另一种方法是使用解构来创建应该可访问的变量。
createFunction()
创建一个 Function
,它将一些代码添加到用户定义的 fnBody
之前。前置代码将 obj
解构为由 vars
定义的动态创建的变量。 attrs
是与 fnBody
相关的属性。返回一个函数,调用时会注入 obj
。
function createFunction(obj, vars, attrs, fnBody) {
const fn = new Function(
'obj',
...attrs,
`
const \{ ${vars.join()} \} = obj;
obj = undefined;
${fnBody}
`
)
return (args = []) => fn(obj, ...args)
}
const outer = "Hello from the outside!"
const scope = {
get inner() {
return "Hello from the inside!"
}
}
createFunction(scope, ['inner'], [], `console.log('inner:', inner)`)()
createFunction(scope, ['inner'], [], `console.log('outer:', outer)`)()
虽然我们显式定义
vars
以使 obj
的属性可访问,但另一种方法是使用 Object.getOwnPropertyNames(). 隐式地使
obj
的所有属性可用。
function createFunction(obj, attrs, fnBody) {
const vars = Object.getOwnPropertyNames(obj)
const fn = new Function(
'obj',
...attrs,
`
const \{ ${vars.join()} \} = obj;
obj = undefined;
${fnBody}
`
)
return (args = []) => fn(obj, ...args)
}
const outer = "Hello from the outside!"
const scope = {
get inner() {
return "Hello from the inside!"
}
}
createFunction(scope, [], `console.log('inner:', inner)`)()
createFunction(scope, [], `console.log('outer:', outer)`)()