Как стать автором
Обновить

Комментарии 21

Формально — да, баг. Но у себя, например, очень давно не помню, чтобы был где-то нужен перебор кроме как по объектам jQuery — для каких-то подготовительных штук это делает либо шаблонизатор (благо XSLT позволяет избавиться от части велосипедов на клиентской стороне), либо вообще серверная сторона.
Формально — да, баг

На самом деле нифига не баг. Есть такая штука, как, скажем, arguments, которая является объектом, но должна обходится как массив. Именно для подобных случаев и используется подход из топика.
Вот единственно верная проверка на Array, у typeof и constructor были какие-то свои минусы. Она же используется в $.isArray в несколько более общем виде:

isArray: function( obj ){
       return Object.prototype.toString.call( obj ) === '[object Array]';
}


Проверять результат лучше не на фолс, а на строгое равенство undefined. И если не, то возвращать то что вышло, это покрывает некоторые интересные кейсы типа нахождения первого элемента по какому-то условию.
typeof и constructor не сработают в том случае, если проверяемый объект находится в чужом фрейме, вы правы.
А чем не подходит obj instanceof Array?
См. выше. :)
Проблема instanceof, как сказали выше в других фреймах, НО:
  • редкая нужда в дополнительных фреймах (в моей ситуации, разработка хтмл5 приложений)
  • если и нужен фрэйм, при его инициализации, передаю нужные объекты в него — получаем работающий instanceof
  • скуданя скорость jsperf ( и учитем, что проверок на «Array» бывает много)

В виду этих пунктов, я отказался от подобных (isArray, isFunction) функций. Хотя могу где-то ошибаться.
Способ проверки на массив по косвенным признакам:

isArray:  function(value) 
{
     var __object__ = Object.prototype;

     return value && typeof(value) === 'object' && value instanceof Array && !__object__.propertyIsEnumerable.call(value, 'length') && __object__.hasOwnProperty.call(value, 'length');
};

Именно так работает и jQuery.type(obj) === 'array'
В трекер jQuery уже закинули?
Не стоит: 1) это не баг, а возможность использовать интерирование по своим объектам; 2) формальная проверка легче, а значит в данном случае вернее.
Имхо, в доке сказано, что each работает для array-like объектов, а не просто объектов. А вы попытались подсунуть незнамо что с пропертью lenght. Отсюда duck-typing и не сработал.
1) Этот метод не будет работать для коллекций jQuery :) у них свойство constructor = jQuery, соответственно нужно добавить проверку obj.constructor === jQuery.
2) В багтрекере этот баг уже 2 года висит bugs.jquery.com/ticket/7260
Для псевдомассива arguments тоже не сработает. А наверняка кто-нибудь уже пользуется $.each для его обхода.
«- Вжжжжжжжж-КРЯК! — сказала японская бензопила.»
Любопытно взглянуть на реализацию из underscore.js. Они несильно ушли от варианта с jQuery, но, во-первых, проверяют тип length (obj.length === +obj.length) а во-вторых, по возможности делегируют вызов нативному forEach, если браузер его поддерживает.
Это не баг. Это возможность проходить правильно по любым array-like коллекциям.

Например, по NodeList.

Иначе он будет воспринимать объект как хэш и проходить по остальным методам (у которых enumerable = true), например по length (что совершенно не нужно).
(что касается length, имеются в виду не нативные, а свои коллекции)
1. Давайте рассмотрим ситуацию когда свойство length унаследовано:

var object = {

};

console.log(object.length) // undefined

object.constructor.prototype.length = 1;

console.log(object.length) // true

delete object.length; // false

console.log(object.length) // 1



Делаем выводы…

2. А теперь о проверке конструктора:

var array = function() {

};

array.prototype.constructor = Array;

var object = new array;

console.log(object.constructor); // Array
console.log(object.length);      // undefuned


Делаем выводы…

3. Относительно массиво-подобных объектов:

Перед тем как нести чушь, говоря что массиво-подобный объект это любой объект у которого есть свойство length, позволю себе привести пример того как можно создать такой объект:

var array_like = {
	0: 1,
	1: 2,
	length: 2
};

console.log(array_like.constructor);  // Object


Вспомним что Function.prototype.apply в ES5 вторым аргументом может принимать массиво-подобный объект, проверим является наш объект таковым:

console.log(Math.max.apply(null, array_like));  // 2


Чтобы убудиться давайте посотрим на следующие примеры:

void function() {
	console.log(Math.max.apply(null, arguments)); // -Infinity
}();


var not_array_like = {
	length: 2
};

console.log(Math.max.apply(null, not_array_like)); // NaN


4. Как можно проверить является объект массивом:

var isArray = function (object) {
	return Object.prototype.toString.call( object ) === '[object Array]';
};


И по косвенным признакам:

isArray:  function(value) {
	var __object__ = Object.prototype;

	return value && typeof(value) === 'object' && value instanceof Array && !__object__.propertyIsEnumerable.call(value, 'length') && __object__.hasOwnProperty.call(value, 'length');
};


Делаем выводы…
Зарегистрируйтесь на Хабре , чтобы оставить комментарий

Публикации