Как исключить добавленные в массив методы из обработки в цикле for..in? (javascript)

Я добавил несколько полезных помощников в Array (например, toSource() для Opera). И теперь for..in возвращает функции с нормальными свойствами.

Сейчас я использую for..in, потому что с ним легче читать код. И это нативный функционал js, так что должно быть быстрее.

Но добавление проверок типов в цикл упрощает использование классического for(;;).

Есть ли способы избежать for..in функций перечисления?

Кроссбраузерность не очень нужна (должна работать в Опере), но важна скорость.

Спасибо.


Изменить:
Есть ли возможность избежать for..in перечисления функций или пользовательских свойств любого объекта?


person zxcat    schedule 27.11.2009    source источник
comment
связаны, если даже не дублируются: Javascript: скрытие методов прототипа в цикле for?. См. также этот итоговый ответ   -  person Bergi    schedule 09.11.2012


Ответы (6)


Вам не следует никогда использовать циклы for..in для перебора элементов массива. for..in предназначен для перебора свойств и должен использоваться только для этого, именно по той причине, которую вы только что описали. Многие библиотеки изменяют прототипы массива, даты и т. д., поэтому вам не следует полагаться на for..in итерацию только элементов массива. Используйте метод for(;;), он гарантированно сделает то, что вы хотите. И это не быстрее, чем цикл for..in, потому что он также является родным для javascript.

Дополнительные сведения см. в библиотеке prototype.js.

person jvenema    schedule 27.11.2009
comment
Или используйте jQuery.each jquery-howto .blogspot.com/2009/06/ - person Anton Kuzmin; 27.11.2009
comment
Спасибо за ответ. Одна из причин, по которой я пытался использовать for..in, заключается в том, что я не люблю писать много одного и того же кода. - person zxcat; 27.11.2009
comment
Каждый @HappyCoder Jquery также использует добавленные методы прототипа, поэтому у них все еще будет та же проблема. - person luisZavaleta; 07.06.2015

Да, но это только JavaScript 1.7+. Opera имеет только ограниченную поддержку JavaScript 1.7, которая включает в себя очень базовую поддержку присваивания деструктурирования, поэтому это не будет работать в Opera, но будет работать в Firefox.

Эта реализация позволяет безопасно использовать for [each](item in array):

Array.prototype.__iterator__ = function (flag) {
    var len = this.length, i = 0;

    for (; i < len; i++) {
        yield flag ? i : this[i];
    }
};
person Eli Grey    schedule 27.11.2009
comment
Полумесяц: flag равно true, если массив использовался в цикле for..in, и false, если он использовался в цикле for each..in. - person Eli Grey; 28.11.2009

Как уже говорилось, вы НЕ должны использовать for..in для перебора массивов. Это медленнее, чем использование for(;;).

Вы также должны кэшировать длину массива, если вас интересует производительность, например:

for (var i=0, len=arr.length; i<len; i++) 
{
  ... 
}

Используйте только for..in для перебора свойств объектов. Чтобы исключить унаследованные свойства, используйте метод hasOwnProperty():

for (var prop in object)
{
  // Don't include properties inherited from the prototype chain
  if (object.hasOwnProperty(prop))
  {
    ...
  }
}
person Pedro Simonetti    schedule 27.11.2009
comment
Спасибо. Да, про кеширование я знаю, но это другое дело, писать не хочется, а хочется иметь :) Не хочется в каждом цикле писать один и тот же код. Я знаю, я могу использовать что-то вроде arr.MyFor(function() {..}), но не знаю, как это написать и будет ли это лучше. Насчет .hasOwnProperty — да, я знаю, но проверки внутри цикла — это то, чего я хочу избежать. - person zxcat; 27.11.2009
comment
Привет, zxcat, Если вас действительно заботит производительность (я предполагаю, что, как только вы не сделаете дополнительную проверку внутри цикла), лучший способ сделать это — написать весь цикл вручную. Вызовы функций в JavaScript очень затратны по сравнению с скомпилированными языками. Любая функция each() / myFor() будет работать НАМНОГО МЕДЛЕННЕЕ, потому что обычно вы будете использовать 2 функции (генератор цикла и итератор цикла). Чтобы каждый раз не вводить один и тот же код, лучше всего использовать макросы/шаблоны/сниппеты или другие функции из редактора исходного кода по вашему выбору. - person Pedro Simonetti; 28.11.2009

Теперь есть еще один вариант с поддержкой ES5, он позволяет вам определять неперечисляемые свойства! Должно быть возможно (хотя я не проверял) сделать что-то вроде этого:

Object.defineProperty( Array.prototype, "myNewMethod", {
   value: function(){},
   writable: true,
   enumerable: false, /* this controls for..in visibility */
   configurable: true
});
person hallvors    schedule 03.01.2012

То, что вы описываете, именно поэтому вы используете

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

вместо:

for (var item in arr) { ... }

потому что они разные.

Если вам не нужны все члены, которые вы получите во второй форме, и только проиндексированные элементы, используйте первую форму.

person cletus    schedule 27.11.2009
comment
Спасибо. У меня есть массив, поэтому for(;;) работает нормально. Но Array содержит дыры (например, a[0]=some, a[100]=other и a[1..99] не используется), поэтому необходимы проверки существования элемента. С for..in этой проверки нет, но она возвращает больше, чем мне нужно. Я хочу использовать быструю и легкую конструкцию для обработки элементов. Я новичок в js, поэтому, возможно, для моей задачи лучше использовать Object вместо Array) - person zxcat; 27.11.2009
comment
Пожалуйста. › Я новичок в js, поэтому, возможно, для моей задачи лучше использовать Object вместо Array Дело в том, что Array — это Object. For-in просматривает свойства объекта. И Функция может быть свойством Объекта. Просто не создавайте дыр в массивах, и все будет в порядке. Я рекомендую вам правильно изучать Javascript. Это вам очень поможет -- crockford.com/javascript - person Anton Kuzmin; 27.11.2009

Изменить: исправлено неправильное использование typeof благодаря комментарию @Bergi

хороший момент о производительности при переборе (очень) разреженных массивов - я бы предположил, что, возможно, использование isNaN(parseInt()) в цикле позволяет вам находить только элементы массива:

for( var propertyName in myArray){
    if( !isNaN(parseInt(propertyName)) ){ /* probably array element */ }
}

Я не знаю, что лучше работает с hasOwnProperty() и описанным выше подходом. Вам просто нужно измерить это с помощью очень большого массива, повторяя его 10000 раз или что-то в этом роде. Если вы проведете некоторые измерения производительности, результаты будут интересными, поэтому, пожалуйста, поделитесь! :)

person hallvors    schedule 06.12.2009
comment
имена свойств всегда являются строками. Всегда. - person Bergi; 09.11.2012