如何通过函数的函数传递变量

时间:2014-01-01 00:38:00

标签: javascript function

<!DOCTYPE HTML>
<html><head><script>
function test() {
    array = [];
    for (i = 0; i < 10; i += 1) {
        array.push(
            {a:'start' + i, b:function() {return 'end' + i;}}
        );
    }
    return array;
}
window.onload = function () {
      alert(test()[5].a + ' ' + test()[5].b());
}
 </script></head><body></body></html>

在上面的代码中,此警报'start5 end10'。

我正在尝试将i的值传递给属性b中的函数,但无法弄清楚如何执行此操作。

我希望它能通过闭包访问i并提醒'start5 end5'。如何将i的值传递给属性b的函数,以便在执行test()[5] .b()时可以正确解析?

4 个答案:

答案 0 :(得分:2)

你成了封闭怪物的受害者。没什么好害怕的,你不是第一个也不会是最后一个。封闭怪物总是很饿:)

您的解决方案无效,因为存储在对象数组中的未命名函数始终引用i的实时值(在循环结束时将为10)。

在JS中避免这种情况的方法是为b的每个值创建不同的函数上下文。 通常的方法是创建一个“工厂”函数,返回实际的函数调用,返回正确的b值。

function b_factory (b)
{
    return function ()
    {
        return 'end'+b;
    }
}

注意返回的函数如何引用超出其范围的b变量。 正是这种能力为给定函数范围之外的变量提供了一个值,称为词法闭包。 在这种情况下,闭包位于b_factory的参数中。

现在使用它:

function test() {
    array = [];
    for (i = 0; i < 10; i += 1) {
        array.push(
            {a:'start' + i, b:b_factory(i)}
        );
    }
    return array;
}

这里发生的是:

  • b_factory返回的未命名函数由array[i].b引用,因此垃圾收集器不会销毁它
  • 由于b的{​​{1}}参数依次由未命名的函数引用,因此b_factory的执行上下文(其中要找到b_factory)也在内存中保持活动状态。
  • 从未命名函数中看到的b的值是传递给b的参数的值,即调用时b_factory 的值到i

一旦熟悉了这一点,就可以省去工厂函数的创建,并使用一个未命名的函数来提供闭包,如下所示:

b_factory

请注意,在这种情况下,内部未命名的函数有点过分。由于对象的b字段将是执行其余部分的常量,因此未命名的函数根本没有任何计算(从其上下文看,一切都是常量)。它与定义像这样的函数数组一样有用:

    array.push(
        {a:'start' + i, b:function(b) {return function() { return 'end'+b;}}(i)}

您可以将b字段定义为字符串,然后使用此代码:

function give_me_one () { return 1; }
function give_me_two () { return 2; }
// ;)

这里未命名的函数仍然创建相同的闭包,但返回一个常量字符串而不是函数。由于没有持久化对象来引用b参数,因此在未命名的函数执行后,闭包会立即被垃圾收集。

b字段现在是一个字符串,因此您应该在没有括号的情况下访问它,如下所示:

    array.push(
        {a:'start' + i, b:function(b) { return 'end'+b; }(i)}

当然,这只是一个演示封闭使用的练习。初始化对象的最有效方法就是处理b,就像处理a一样,即

console.log(test()[5].a + ' ' + test()[5].b);

答案 1 :(得分:1)

尝试这样的事情:

var getB = function(i) {
    return function() {return 'end' + i;}
}
for (i = 0; i < 10; i += 1) {
    array.push(
        {a:'start' + i, b:getB(i)}
    );
}

Demonstration

答案 2 :(得分:1)

它是理解JS中的范围的标准问题之一 - 你的函数引用了for循环中使用的i(引用变量,而不是它的值),所以你在函数时获得该变量的最终值在循环结束后调用。它足以从另一个函数中创建该函数并将i传递给工厂函数(JS中的变量按值传递)。另外,我没有在代码中看到变量i的任何定义,你只是开始使用i而没有初始化它 - 它将被绑定到顶级对象(本例中的窗口对象) ,变量将是可见的全局) - 避免在两个函数将开始修改全局变量时遇到一些奇怪问题的情况,它可能导致无限循环或循环结束到早期。

function test() {
    array = [];
    for (var i = 0; i < 10; i += 1) {
        array.push({
            a: 'start' + i,
            b: (function (i) {
                return function() {return 'end' + i;};
            }(i))
        });
    }
    return array;
}

答案 3 :(得分:0)

<!doctype html>
<html lang="zh">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="description" content="">
    <meta name="author" content="">

    <title>Template Index</title>


</head>
<body>

  <script>
function test() {
    array = [];
    for (i = 0; i < 10; i += 1) {
        array.push(
          (function (start, end) {
            return {a:'start' + start, b:function() {return 'end' + end;}};
          } (i, i))

        );
    }
    return array;
}
window.onload = function () {
      alert(test()[5].a + ' ' + test()[5].b());
}

  </script>
</body>
</html>
代码{a:'start' + i, b:function() {return 'end' + i;}}

中的

a将获得正确的数字,因为当它循环通过0--9时它与字符串'start'连接

但是b生成抛出一个与闭包有效的函数,

所有函数都将在循环中访问相同的i,循环后为10

你需要创建一个新函数来保存i的快照,这样每个对象都有自己的副本

循环中i的值