与JavaScript作用域链交互

与JavaScript作用域链交互,javascript,scope,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

给定作用域中的以下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
我猜答案是“不”,有些人可能会说这种作用域注入存在“安全”问题,但考虑到你无论如何都可以做到这一点(尽管严重破坏),我不认为是这样

这样做的好处是,您可以从本质上伪造自己的伪变量

提前谢谢


编辑,只是稍微澄清一下,想象一下我想要“使用范围”一个具有以下范围链的函数:

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' }

所有先前的固定变量都会被找到,因为我的扩展范围没有说明任何关于它们的内容。

我想你要寻找的答案是内置的“with”语句

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

我认为您可以做这类事情的唯一其他方法是从主机应用程序本身内部,从外部操纵javascript环境。或者,如果您使用的是rhino,实际上可以从javascript内部实现,因为Java API和javascript之间的链接在rhino中是无缝的。当史蒂夫·耶格第一次指出这一点时,我大吃一惊。从javascript外部操作javascript,但从javascript内部操作!真是天才


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

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


您可以使用functionName.call(作用域,arg1,…)或functionName.apply(作用域,[arg1,…]),更改
关键字的作用域;这可以与原型一起使用,以创建您描述的类似链-如果在对象自身属性中找不到某个对象,则会在其原型中查找该对象。如果属性不存在,则使用链中的下一个原型,依此类推。

是否可以使用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})();

这似乎是个坏主意,但是…

如果您想使用scope,您可能会喜欢Javascript 1.7中提供的
let
语句

是的,第二个代码剪接实现了这个技巧(只是为了演示的目的更简单),它的工作原理是最接近的作用域按预期工作,但作用域链现在完全不同了,不再与原始作用域相连。是的,这是关于闭包上下文与调用上下文。谢谢Jani,我同意你的回答“否”,我不认为有什么办法,但我确实认为,如果有任何有趣的“黑客”存在的话,就值得扔掉它。我不认为with语句可以让你以我想要的方式推断范围,但是的,这可能超出了javascript的范围。。看起来令人沮丧的是,您可以完全取消关闭范围并提供自己的(谁知道会造成什么混乱),但您不能在现有关闭的基础上添加一点。。添加您自己的伪变量等合法场景将非常有用。with语句将对象作为范围直接插入现有范围链的头部,保留其余范围。他们放弃了它,因为必须支持使得优化javascript解释器变得困难。是的,但是它将作用域插入到定义它的作用域的头部,而我希望创建一个函数的副本,将一个新的作用域添加到它已有的作用域中。