Monkeypatched代码出现在...循环数组中

时间:2012-07-14 12:34:44

标签: javascript

我在我的网络应用程序的主JavaScript中有以下代码:

    // uniq for arrays
if (!Array.prototype.getUnique) {
    Array.prototype.getUnique = function () {
        var u = {}, a = [];
        for (var i = 0, l = this.length; i < l; ++i) {
            if (u.hasOwnProperty(this[i])) {
                continue;
            }
            a.push(this[i]);
            u[this[i]] = 1;
        }
        return a;
    }
}

它是javascript的简单uniq克隆,monkeypatched到基本的Array类中。 (请不要在这里讨论monkeypatching,请点击其他地方......)

getUnique()按预期工作,但现在每当我使用for...in循环迭代一个数组时,另一个名为getUnique的索引将传递给迭代器主体,并且当此为false {查找{1}},n的代码是值。 (换句话说:getUnique正在循环遍历数组,但在最后一次迭代时追加for...in

这里发生了什么?还有另一个monkeypatched函数在这个函数的正上方工作正常(getUnique)并且没有出现在迭代器中。以下是一些触发此问题的示例代码:

indexOf()

此代码段的调试输出如下所示:

for (var r in result) {
    //tags = tags + result[r]["tags"].split(" ").join(", ")
    if (result[r]["tags"]) {
        var newtags = result[r]["tags"].split(" ");
        debug.log(newtags);
        for (var n in newtags) {
            debug.log("n is " + n);
            tags.push(newtags[n]);
        }
    }
}

这里发生了什么?重构monkeypatch并将其放在实用程序类中很容易,但对我来说这是一个非常奇怪的边缘情况。我脑子里想到了这里发生的事情,但这些只是猜测。谁能解释一下?

4 个答案:

答案 0 :(得分:1)

这是hasOwnProperty旨在解决的问题:它告诉您迭代的名称是在对象本身上还是从原型继承。

试试这个:

for (var n in newtags) {
    if (newtags.hasOwnProperty(n)) {
        debug.log("n is " + n);
        tags.push(newtags[n]);
    }
}

答案 1 :(得分:1)

不要使用for-in来迭代数字属性。

使用for循环:

 for (var i = 0; i < newtags.length; i++) {

for-in语句几乎从来都不是这项工作的合适工具,因为......

  • 它包括所有属性,包括非数字,超出范围和原型属性。

  • 它不保证枚举的顺序。


虽然你可以进行hasOwnProperty检查,但没有真正的好处,并且有一些缺点......

  • 它会减慢你的迭代次数

  • 它对枚举问题的顺序没有帮助

  • 有时你想要一个原型索引(很少见,但它会发生)。使用hasOwnProperty会使这变得不可能。

答案 2 :(得分:1)

您不应使用for…in循环遍历数组,该循环用于枚举对象的属性。因此,例如,您无法保证索引的顺序,也没有 - 如您所见 - 您只迭代数字索引。如果其他人以这种方式向Array的原型添加属性,您也将迭代该属性。请参阅https://developer.mozilla.org/en/JavaScript/Reference/Statements/for...in以获得完整的图片。

但是,在您的情况下,您可以执行以下操作:

for (var n in newtags) {
    if (newtags.hasOwnProperty(n)) {
        debug.log("n is " + n);
        tags.push(newtags[n]);
    }
}

这也阻止了迭代由其他脚本添加到原型的属性。此外,您可以将您的功能定义为不可枚举(在支持ES5的浏览器中):

Object.defineProperty(Array.prototype, "getUnique", {
    value: function () { /*.. your function ..*/}
})

请参阅:https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/defineProperty

答案 3 :(得分:1)

常见的误解是,在...中运行for ...是为了迭代数组。这是错的。它的用法是迭代对象的任何可枚举属性。在javascript中,一切都是对象(包括数组)。但是你可能会注意到,当你在数组中执行...时,你不会得到像length,slice等的值。这是因为那些不是对象的可枚举属性。

这个SO问题提供了一些关于for ... in Why is using "for...in" with array iteration a bad idea?

的使用情况的非常好的信息

如果你想坚持使用for ... in,你可以像上面建议的那样做,并检查你的forOwnProperty ...

for ( var v in myArray ) {
    if ( myArray.hasOwnProperty(v) ) {
        // Do Something
    } 
}

然而,我建议使用普通的无聊循环...

for ( var i = 0; i <= myArray.length - 1; i++ ) {
    // Do Something
}

没有疯狂的细节,这可以在没有达到你添加的方法的情况下工作,因为数组的“索引”根本不是真正的索引,而是具有与其相应索引匹配的名称的属性:即1,0,4等等。

他们感觉像是索引,因为在javascript中,如果该属性是一个数字(即:myArray.0将无效),则无法使用点表示法访问该属性。所以你做myArray [0],感觉就像一个数组。