Что означает ошибка JSLint «тело для for должно быть включено в оператор if»? - PullRequest
228 голосов
/ 26 декабря 2009

Я использовал JSLint в моем файле JavaScript. Выкинуло ошибку:

for( ind in evtListeners ) {

Проблема в строке 41, символ 9: Тело для in должно быть завернутый в оператор if для фильтрации нежелательных свойства от прототипа.

Что это значит?

Ответы [ 8 ]

405 голосов
/ 26 декабря 2009

Прежде всего, никогда использует цикл for in для перечисления по массиву. Никогда. Используйте старый добрый for(var i = 0; i<arr.length; i++).

Причина этого заключается в следующем: каждый объект в JavaScript имеет специальное поле с именем prototype. Все, что вы добавите в это поле, будет доступно для каждого объекта этого типа. Предположим, вы хотите, чтобы у всех массивов была новая классная функция с именем filter_0, которая будет фильтровать нули.

Array.prototype.filter_0 = function() {
    var res = [];
    for (var i = 0; i < this.length; i++) {
        if (this[i] != 0) {
            res.push(this[i]);
        }
    }
    return res;
};

console.log([0, 5, 0, 3, 0, 1, 0].filter_0());
//prints [5,3,1]

Это стандартный способ расширения объектов и добавления новых методов. Многие библиотеки делают это. Однако давайте посмотрим, как теперь работает for in:

var listeners = ["a", "b", "c"];
for (o in listeners) {
    console.log(o);
}
//prints:
//  0
//  1
//  2
//  filter_0

Вы видите? Он вдруг думает, что filter_0 - это еще один индекс массива. Конечно, это на самом деле не числовой индекс, но for in перечисляет через поля объекта, а не только числовые индексы. Итак, мы сейчас перечисляем все числовые индексы и filter_0. Но filter_0 не является полем какого-либо конкретного объекта массива, каждый объект массива теперь обладает этим свойством.

К счастью, у всех объектов есть метод hasOwnProperty, который проверяет, действительно ли это поле принадлежит самому объекту или оно просто унаследовано из цепочки прототипов и, следовательно, принадлежит всем объектам этого типа.

for (o in listeners) {
    if (listeners.hasOwnProperty(o)) {
       console.log(o);
    }
}
 //prints:
 //  0
 //  1
 //  2

Обратите внимание, что хотя этот код работает как положено для массивов, вы никогда не должны никогда использовать for in и for each in для массивов. Помните, что for in перечисляет поля объекта, а не индексы или значения массива.

var listeners = ["a", "b", "c"];
listeners.happy = "Happy debugging";

for (o in listeners) {
    if (listeners.hasOwnProperty(o)) {
       console.log(o);
    }
}

 //prints:
 //  0
 //  1
 //  2
 //  happy
85 голосов
/ 26 декабря 2009

Дуглас Крокфорд, автор jslint, писал (и говорил) об этой проблеме много раз. На этой странице его веб-сайта есть раздел, который охватывает это:

для выписки

А для класса утверждений должно иметь следующую форму:

for (initialization; condition; update) {
    statements
}

for (variable in object) {
    if (filter) {
        statements
    } 
}

Первая форма должна использоваться с массивы и с петлями предопределенное количество итераций.

Вторая форма должна использоваться с объекты. Помните, что члены, которые добавляются к прототипу объект будет включен в перечисление. Мудро программировать в обороне с помощью метод hasOwnProperty для различения истинные члены объекта:

for (variable in object) {
    if (object.hasOwnProperty(variable)) {
        statements
    } 
}

У Крокфорда также есть видеосериал о театре YUI, где он рассказывает об этом. Крокфордские серии видео / разговоров о javascript - это то, что нужно посмотреть, если вы хоть немного серьезно относитесь к javascript.

16 голосов
/ 10 сентября 2015

Плохо: (jsHint выдаст ошибку)

for (var name in item) {
    console.log(item[name]);
}

Хорошо:

for (var name in item) {
  if (item.hasOwnProperty(name)) {
    console.log(item[name]);
  }
}
9 голосов
/ 30 января 2010

Вава ответ на вопрос. Если вы используете jQuery, то об этом позаботится функция $.each(), поэтому ее безопаснее использовать.

$.each(evtListeners, function(index, elem) {
    // your code
});
7 голосов
/ 10 декабря 2010

@ all - все в JavaScript является объектом (), поэтому такие выражения, как «использовать только объекты», немного вводят в заблуждение. Кроме того, JavaScript не является строго типизированным, поэтому 1 == «1» - это правда (хотя 1 === «1» - нет, Крокфорд в этом хорош). Когда дело доходит до прогроматической концепции массивов в JS, в определении важна типизация.

@ Брентон - Не нужно быть диктатором терминологии; «ассоциативный массив», «словарь», «хеш», «объект», все эти концепции программирования применимы к одной структуре в JS. Это пары имя (ключ, индекс), где значением может быть любой другой объект (строки также являются объектами)

Итак, new Array() совпадает с []

new Object() примерно похож на {}

var myarray = [];

Создает структуру, которая является массивом с ограничением, что все индексы (или ключи) должны быть целыми числами. Это также позволяет автоматически назначать новые индексы через .push ()

var myarray = ["one","two","three"];

Действительно лучше всего справиться через for(initialization;condition;update){

А как же:

var myarray = [];
myarray[100] = "foo";
myarray.push("bar");

Попробуйте это:

var myarray = [], i;
myarray[100] = "foo";
myarray.push("bar");
myarray[150] = "baz";
myarray.push("qux");
alert(myarray.length);
for(i in myarray){
    if(myarray.hasOwnProperty(i)){  
        alert(i+" : "+myarray[i]);
    }
}

Возможно, не лучшее использование массива, но просто иллюстрация того, что вещи не всегда ясны.

Если вы знаете свои ключи и определенно, если они не являются целыми числами, ваш единственный вариант структуры массива - это объект.

var i, myarray= {
   "first":"john",
   "last":"doe",
   100:"foo",
   150:"baz"
};
for(i in myarray){
    if(myarray.hasOwnProperty(i)){  
        alert(i+" : "+myarray[i]);
    }
}
2 голосов
/ 19 апреля 2010

Конечно, это немного экстрим, чтобы сказать

... никогда не используйте for в цикле, чтобы перечислить по массиву. Никогда. использование старый добрый для (var i = 0; г

Стоит выделить раздел в выдержке Дугласа Крокфорда

... Вторая форма должна использоваться с объекты ...

Если вам требуется ассоциативный массив (он же hashtable / dictionary), в котором ключи имеют имена, а не численно индексируются, вам придется реализовать это как объект, например, var myAssocArray = {key1: "value1", key2: "value2"...};.

В этом случае myAssocArray.length будет иметь нулевое значение (поскольку у этого объекта нет свойства length), и ваш i < myAssocArray.length не даст вам очень далеко. В дополнение к обеспечению большего удобства, я ожидаю, что ассоциативные массивы обеспечат преимущества производительности во многих ситуациях, поскольку ключи массива могут быть полезными свойствами (т. Е. Свойством или именем идентификатора члена массива), то есть вам не нужно перебирать длинный массив повторно вычисляет операторы if для поиска нужной вам записи массива.

В любом случае, спасибо также за объяснение сообщений об ошибках JSLint, теперь я буду использовать проверку «isOwnProperty» при работе с моими бесчисленными ассоциативными массивами!

0 голосов
/ 20 мая 2011

Просто чтобы добавить к теме для in / for / $. Каждый, я добавил тестовый пример jsperf для использования $ .each против for in: http://jsperf.com/each-vs-for-in/2

Различные браузеры / версии обрабатывают его по-разному, но кажется, что $ .each и сразу же - самые дешевые варианты с точки зрения производительности.

Если вы используете for для итерации по ассоциативному массиву / объекту, зная, что вам нужно, и игнорируя все остальное, используйте $ .each, если вы используете jQuery, или просто для in (и затем разрыв; один раз вы достигли того, что, как вы знаете, должно быть последним элементом)

Если вы перебираете массив для выполнения чего-либо с каждой парой ключей в нем, следует использовать метод hasOwnProperty, если вы НЕ используете jQuery, и использовать $ .each, если вы действительно используете jQuery.

Всегда используйте for(i=0;i<o.length;i++), если вам не нужен ассоциативный массив, хотя ... LOL Chrome выполнил это на 97% быстрее, чем для in или $.each

0 голосов
/ 26 декабря 2009

Это означает, что вы должны фильтровать свойства evtListeners с помощью метода hasOwnProperty .

...