Что означает "тело ошибки JSLint" для in должно быть завершено в выражении if?

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

for( ind in evtListeners ) {

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

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

Ответ 1

Прежде всего, никогда использовать цикл 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

Ответ 2

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

для оператора

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

for (initialization; condition; update) {
    statements
}

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

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

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

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

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

Ответ 3

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

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

Хорошо:

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

Ответ 4

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

$.each(evtListeners, function(index, elem) {
    // your code
});

Ответ 5

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

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

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

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

var myarray = [];

Создает структуру, которая представляет собой массив с ограничением на то, что все индексы (aka keys) должны быть целыми числами. Он также позволяет автоматически назначать новые индексы с помощью .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]);
    }
}

Ответ 6

Конечно, немного крайнее сказать

... никогда не использовать a для цикла while перечислить массив. Никогда. использование старый добрый для (var я = 0; я < arr.length; я ++)

?

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

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

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

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

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

Ответ 7

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

Ответ 8

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

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

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

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

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