6

这是一个简单的 JS 循环无法按预期运行的示例,因为循环变量不在单独的范围内。

经常提出的解决方案是构造一段看起来不愉快的循环代码,如下所示:

for (var i in obj) {
    (function() {
        ... obj[i] ... 
        // this new shadowed i here is now no longer getting changed by for loop
    })(i);
}

我的问题是,这可以改进吗?我可以用这个吗:

Object.prototype.each = function (f) {
    for (var i in this) {
        f(i,this[i]);
    }
};

// leading to this somewhat more straightforward invocation
obj.each(
    function(i,v) {
        ... v ...
        // alternatively, v is identical to
        ... obj[i] ...
    }
);

当我确定我需要一个“范围循环”时?它看起来更干净一些,并且应该具有与常规 for 循环相似的性能(因为它以相同的方式使用它)。

更新:似乎做事Object.prototype是一个巨大的禁忌,因为它几乎破坏了一切

这是一个侵入性较小的实现:

function each (obj,f) {
    for (var i in obj) {
        f(i,obj[i]);
    }
}

调用非常轻微地更改为

each(obj,
    function(i,v) {
        ... v ...
    }
);

所以我想我已经回答了我自己的问题,如果 jQuery 这样做,真的不会出错。我忽略的任何问题都需要一个答案。

4

2 回答 2

1

您的回答几乎涵盖了它,但我认为您的原始循环的更改值得注意each(),因为无论出于何种原因,当函数不方便时使用普通的 for 循环是合理的。

更新:更改为使用与问题引用的示例类似的示例来比较不同的方法。该示例必须进行调整,因为该each()函数需要一个填充的数组来迭代。

假设以下设置:

var vals = ['a', 'b', 'c', 'd'],
    max = vals.length,
    closures = [],
    i;

使用问题中的示例,原始循环最终创建了 2n 个函数(其中 n 是迭代次数),因为在每次迭代期间创建了两个函数:

for (i = 0; i < max; i++) {
    closures[i] = (function(idx, val) {  // 1st - factoryFn - captures the values as arguments 
        return function() {              // 2nd - alertFn   - uses the arguments instead
            alert(idx + ' -> ' + val);   //                   of the variables
        };
    })(i, vals[i]);
}

通过在循环开始之前创建一次工厂函数,然后重用它,可以将其简化为仅创建 n + 1 个函数:

var factoryFn = function(idx, val) {
    return function() {
        alert(idx + ' -> ' + val);
    };
};

for (i = 0; i < max; i++) {
    closures[i] = factoryFn(i, vals[i]); 
}

这几乎等同于each()在这种情况下如何使用该函数,这也将导致总共创建 n + 1 个函数。工厂函数创建一次并立即作为参数传递给each().

each(vals, function(idx, val) {
    closures[idx] = function() {
        alert(idx + ' -> ' + val);
    };
});

FWIW,我认为使用的好处each()是代码有点短,并且在将工厂函数传递给函数时正确创建工厂each()函数清楚地说明了这是它的唯一用途。循环版本 IMO的一个好处是执行for循环的代码就在那里,因此它的性质和行为是完全透明的,而each()函数可能定义在不同的文件中,由其他人编写等。

于 2013-01-01T07:41:03.023 回答
1

全球范围

当某些东西是全局的时,意味着它可以从代码中的任何地方访问。以此为例:

var monkey = "大猩猩";

function greetVisitor () {

return alert("Hello dear blog reader!");
}

如果该代码在 Web 浏览器中运行,则函数范围将是窗口,从而使其

可用于在该 Web 浏览器窗口中运行的所有内容。

本地范围

与全局作用域相反,局部作用域是指某些东西刚刚在一个

代码的某些部分,例如函数。例如;

函数talkDirty(){

var saying = "Oh, you little VB lover, you";
return alert(saying);
}

alert(saying); // Throws an error

如果您看一下上面的代码,变量saying 仅在talkDirty 中可用

功能。在它之外,它根本没有定义。注意事项:如果您要声明说没有

在它前面的 var 关键字,它会自动成为一个全局变量。

这也意味着如果你有嵌套函数,内部函数将可以访问

包含函数变量和函数:

函数保存名称(名字){

函数大写名称(){

return firstName.toUpperCase();

} var capitalized = capitalizeName();

return capitalized;

}

alert(saveName("Robert")); // Returns "ROBERT"

如你所见,内部函数 capitalizeName 不需要传入任何参数,但已经完成

访问外部 saveName 函数中的参数 firstName。为了清楚起见,让我们再看一个

例子:

函数兄弟姐妹(){

var siblings = ["John", "Liza", "Peter"];

函数兄弟计数(){

var siblingsLength = siblings.length;

return siblingsLength;

}

函数 joinSiblingNames () {

return "I have " + siblingCount() + " siblings:\n\n" + siblings.join("\n");

}

return joinSiblingNames();

}

alert(siblings()); // Outputs "I have 3 siblings: John Liza Peter"

如您所见,两个内部函数都可以访问包含函数中的兄弟数组,并且

每个内部函数都可以访问同一级别的其他内部函数(在这种情况下,

joinSiblingNames 可以访问兄弟计数)。但是,siblingCount 中的变量 SiblingsLength 是

仅在该功能内可用,即该范围。

于 2013-01-01T11:25:33.590 回答