JSLint for backwards for 循环

JSLint for backwards for loops

Douglas Crockford,因此,JSLint 真的不喜欢 for 循环。大多数时候我同意,并使用 [].forEachObject.keys(obj) 遍历数组或字典。

但是,在某些情况下,内置函数在任何合理的意义上都不比 for 循环好(据我所知),我不喜欢在我的 JSLint 上收到警告。

其中一个例子是,canvas 上有一堆物品。绘图时,你想向前遍历数组,但是当点击测试鼠标点击时,你需要向后迭代。例如

// ...
drawItems = function () {
    objects.forEach(drawItem);
},
hitTest = function (x, y) {
    var i;
    for (i = objects.length - 1; i >= 0; i -= 1) {
        if (test(objects[i], x, y)) {
            return objects[i];
        }
    }
}
// ...

我可以想出几种方法来通过测试,但它们都很笨拙。例如

hitTest = function (x, y) {
    var item;

    objects.reverse();
    objects.some(function (v) {
        if (test(v)) {
            item = v;
            return true;
        }
    });
    objects.reverse();
    return item;
}

hitTest = function (x, y) {
    return objects.reduceRight(
        function (prev, curr) {
            return prev || (
                test(curr, x, y)
                    ? curr
                    : prev
            );
        },
        undefined
    );
}

哪个会愉快的通过JSLint,但是,说实话,看着恶心。我不喜欢 "hacking" 我的代码只是为了让它通过测试的想法。

除了选择 "tolerate for statement" 选项(因为这会影响其余代码的 linting)之外,我将如何处理这个问题同时避免像反转或复制数组这样的浪费操作?

您可以 slice 数组,这样您的 hitTest 就没有任何潜在的副作用。

hitTest = function (x, y) {
    return objects.slice(0).reverse().find(function(v) {
        return test(v);
    });
}

这样就不用担心忘记重新反转数组了。

由于我认为您不应该仅仅为了让无意义的警告消失而取消优化您的代码,因此这里有一些处理此问题的选项:

  1. 忽略错误的警告。
  2. 切换到 jsHint,它允许您instruct jsHint to ignore certain sections of code(嵌入注释)。
  3. 将 jsLint 不满意的代码隔离在函数或方法中,这样警告只会在您的整个源代码中出现一次,而不是每次您想要进行反向遍历时都会出现。

例如,这里有一个反向遍历函数:

// traverse an array in reverse order
// if the callback returns true, iteration will be stopped
function reverseForEach(array, fn) {
    for (var i = array.length - 1; i >= 0; i--) {
        if (fn(array[i], i, array) === true) {
            return;
        }
    }
}

或者,作为 Array 原型上的方法:

// traverse an array in reverse order
// if the callback returns true, iteration will be stopped
Array.prototype.reverseForEach = function(fn) {
    for (var i = this.length - 1; i >= 0; i--) {
        if (fn(array[i], i, this) === true) {
            return;
        }
    }
}

运行 进入一个有趣的、reverse-免费的替代修复 here -- 参见第 3 和第 4 -- 今天在看其他东西。很容易从 jQuery 的 each 换成 forEachevery。无论如何,你必须得到原始 for 的长度,所以实际上很干净,虽然有点时髦。

/*jslint white:true, devel:true */
var a = [3,4,5,6], max = a.length-1;
a.forEach(function (ignore, i) {
    "use strict";
    console.log(a[max-i]);
});

我有点担心性能问题,因为每次迭代都会拉出元素两次,一次在 a[max-i],一次在 forEach's callback's (here, throwaway) third argument, though maybe that's a micro-optimization concern.

仅将 forEach 用于指标是引发时髦警告的原因。具有讽刺意味的是,因为 Crockford has shown a stark dislike for hipsters in the past, so I'm not sure he'd like this "fix" either -- none of us may have answered your "". Tempted to bug Crockford's list;这是一个有趣的好问题,但您并不总能在该列表上得到好的答案。

但这是一个漂亮(看似?)干净的解决方法。