3
function a(){ return x++; }
function b(){ var x=0; return a(); /*call a using b's scope*/ };
console.log(b(),b(),b());

所需输出:1、2、3

问题:有没有办法调用ainside b,强制它使用b's 范围?

4

6 回答 6

3

不完全是,但你可以在它周围包裹一个闭包:

(function() {
    var x = 0;
    window.a = function() {return x++;};
    window.b = function() {return a();};
    console.log(b(),b(),b());
})();
于 2013-03-24T13:20:07.060 回答
1

您的问题可以分解为两部分:

  1. 动态范围 - http://en.wikipedia.org/wiki/Scope_(computer_science)#Dynamic_scoping
  2. 跨调用的变量保留

第一部分可以使用eval. 阅读以下 StackOverflow 问题以获取更多详细信息:是否可以在不使用 eval 的情况下在 JavaScript 中实现动态范围?

第二部分要求您将变量的中间值存储在x某处,并在每次函数调用后恢复它。在这种情况下存储它的最佳位置是函数b本身。

这就是您的代码的样子:

function a() {
    return ++x; // I believe you want prefix increment, not postfix
}

function b() {
    var x = b.x;
    eval(String(a)); // dynamic scoping
    b.x = a();
    return x;
}

b.x = 0;

console.log(b(), b(), b());

你可以在这里看到演示:http: //jsfiddle.net/ucGxD/1/

于 2013-03-24T14:07:38.637 回答
1

不,范围仅来自函数的定义方式。

您必须使用参数(和闭包)传递数据才能将 b 范围内的某些内容放入 a 中。

于 2013-03-24T13:19:43.560 回答
1

您的确切示例不起作用(因为x每次调用时都会重新初始化b),但请考虑:

x = 100;
function a() { return x++; }
function b() { 
    var x = 0; 
    console.log(a(), a(), a()); // 100 101 102
    eval(a.toString())
    console.log(a(), a(), a()); // 0 1 2
}

不用说,不要在工作中尝试。

于 2013-03-24T13:45:32.523 回答
1

不,没有。这绝对是一个功能。

更新

由于这里的一些答案和评论建议使用function.toStringeval模拟动态范围,让我总结一下为什么我认为这不是一个合适的解决方案(无论如何都是一个坏主意):

  1. 语言标准没有规定调用toString函数的结果,也不打算用于这种用途。它恰好适用于大多数(用户定义的)功能的当前浏览器,但不能保证这一点,我强烈建议不要编写依赖它的生产代码。

  2. 函数远不止字符串化可以给你的一段文本。特别是,函数是具有标识和携带状态的对象,字符串化不能保留这两者。更确切地说:

    • 函数可以具有函数体可以访问的属性(特别是prototype属性)。它们将在字符串化过程中丢失。

    • 函数有一个闭包环境,封装了它们使用的周围范围的变量。同样,这将丢失,导致悬空或错误绑定变量引用(即使这可能是其中一些练习的目的)。

    • 函数还有其他可能相关的属性,比如它们的身份、修改__proto__的 或配置状态,比如不可扩展。这些都没有被保留。

    • 并非所有函数都是简单的 JavaScript 源函数。它们可能是原生的,或者从 ES6 开始,它们可能是函数代理。在这两种情况下,字符串化都不会有用。

  3. 具有未绑定变量引用的函数在严格模式下是不合法的(因此在 ES6 中的许多地方都是非法的)。

因此,虽然字符串化可能在一些简单的情况下起作用,但这是偶然的,而不是设计使然。这是一个脆弱的黑客。避开它。

于 2013-03-24T15:19:24.273 回答
-1

是的。上下文可以通过callapply转移。您可以通过调用bind来将上下文设置为范围。看这个例子:

function a() {
    return ++this.x;
}

function b() {
    this.x = this.x || 0;
    return a.call(this);  // a works with x in b's context
}

b = b.bind(b); // now the b's context is the b's scope :-)

console.log(b(),b(),b()); // 1 2 3
console.log(x); // error: x is not defined - global scope is clear

正如 Quentin 所指出的,请参见此处的上下文和范围之间的区别。

于 2013-03-24T14:18:25.890 回答