与JavaScript范围链交互

时间:2009-11-27 23:06:45

标签: javascript scope

考虑到范围内的以下javascript片段:

var x = 10;

function sayx() {
  alert(x);
}

sayx();

你当然希望一个消息框打印'10',你可以做多个函数嵌套来与'x'的确​​定方式进行交互,因为当解析x是什么时,环境会走向范围链。

您甚至可以使用eval执行“重新编译”级别,以便在运行时注入新的范围...例如:

var x = 10;

function sayx() {
  alert(x);
}

function wrap(f) {
  return eval('(function() { var x = 20;(' + f + ')(); })');
}

wrap(sayx)();

这是有效的,因为函数将调用其toString函数,它将返回'原始'源代码。因此我们实际上创建了一个函数的包装版本,该函数具有覆盖x的新范围..结果将是一个警报打印'20'而不是'10'的框。

然而,从表面上看,这似乎可行,但范围链被打破,链中的下一个项目不再相同,因为函数'f'现在定义在不同的位置..更糟糕的是它继承的作用域链可能包含许多调用函数无法访问的引用。

那么,是否有更多支持,可行的方式来注入范围项?类似的东西:

function withScope(f, scope) { ??? }

---

var x = 10, y = 10;

function dothemath() {
  alert(x + y);
}

var haxthemath = withScope(dothemath, { x: 9000 });
haxthemath(); // 9010 not 20

我猜测答案是'不',有些人可能会说这种范围注射存在'安全'问题,但考虑到你无论如何都可以做到这一点(尽管严重受损)我不认为它是......

这样做的好处是你可以伪造自己的伪变量。

提前致谢。


编辑,只是澄清一点,想象我想'withScope'一个具有以下范围链的函数:

Window - window properties
Object - { var x = 10 }
Object - { var y = 5 + x }

我希望能够得到一个有效的链接+我提供的范围的函数..即:

withScope(somefunc, { foo: 'bar' })

会给我

Window - window properties
Object - { var x = 10 }
Object - { var y = 5 + x }
Ext scope - { foo = 'bar' }

所有以前的常设变量都会被找到,因为我的扩展范围没有说明任何内容。

4 个答案:

答案 0 :(得分:4)

如果你按范围引用函数和/或闭包中的局部变量,我认为答案是否定的。

您可以使用functionName.call(scope,arg1,...)或functionName.apply(scope,[arg1,...])更改this关键字的范围;这可以和原型一起用来创建你描述的相似链 - 如果在对象自己的属性中找不到某些东西,它会在原型中查找。如果属性不存在,则使用链中的下一个原型,依此类推。

答案 1 :(得分:0)

我认为你正在寻找的答案是内置的“with”声明。

但是,我不会推荐使用它,因为它已被弃用,很可能不会存在于ecmascript 6中

我认为你可以做这种事情的另一种方式是从宿主应用程序本身内部,从外部操纵javascript环境。或者,如果您正在使用rhino,您实际上可以从javascript内部执行此操作,因为Java apis和Javascript之间的链接在rhino中是无缝的。当史蒂夫第一次指出它时,它引起了我的注意。从javascript以外操作javascript,但是从javascript里面!这是天才!

如果您被困在浏览器环境中,也许您可​​以使用javascript编写的javascript解释器,例如narcissus。

答案 2 :(得分:0)

也许使用javascript的内置toString方法来实现功能?

function withScope(f, scope) {
    var toExec = "";
    for (var prop in scope) {
        toExec += "var " + prop + " = scope['" + prop + "'];"
    }
    toExec += "f = (" + f.toString() + ")";
    eval(toExec);
    return f;
}
var x = 10;
var f = function() {
    console.log(x);
};
withScope(f, {x: 20})();

这似乎是一个坏主意......

答案 3 :(得分:0)

如果您想要使用范围,您可能会喜欢Javascript 1.7中提供的let语句。 Recent versions of Firefox support let