Массивоподобные объекты и работа с ними

Массивоподобным объектом является любой объект, содержащий числовые индексы и свойство length. Например объект arguments является массивоподобным объектом. Объект описанный следующим образом:

var arrayLikeObject = {
    0: 'one',
    1: 'two',
    length: 2
};

является массивоподобным. Коллекции элементов, возвращенных из методов document.querySelectorAll, document.getElementsByTagName а так же объеты, возвращаемые библиотекой jQuery являются массивоподобными объектами. Этот список того, где можно встретить массивоподобные объекты, не полон. Все примеры ниже применимы к любым массивоподобным объектам.

В консоли браузера массивоподобные объекты могут отображаться так же как и массивы. При этом массивоподобные объекты не являются массивами: у массивоподобных объектов нет методов, которые присущи массивам (indexOf, filter, forEach и другие). Поэтому при попытке вызвать метод массива у массивоподобного объекта такой код завершится исключением:

function hasTwo() {
    return arguments.indexOf('two') !== -1;
}
hasTwo('one', 'two', 'three'); // Error

Как работать с массивоподобными объектами

Цикл for

Цикл for корректно работает с массивоподобными объектами. Например функция, определяющая есть ли среди переданных ей аргументов значение 'two' может выглядеть следующим образом:

function hasTwo() {
    for (var i = 0; i < arguments.length; i += 1) {
        if (arguments[i] === 'two') {
            return true;
        }
    }
    return false;
}
console.log(hasTwo('uno', 'tuo', 'tre')); // false
console.log(hasTwo('one', 'two', 'three')); // true

Вызов методов массивов в контексте массивоподобного объекта (одалживание метода)

Вызов методов массивов с помощью call и apply, и передачей контектом вызова ссылки на массивоподобный объект. Например функция, определяющая есть ли среди переданных ей аргументов значение 'two' будет выглядеть следующим образом:

function hasTwo() {
    return Array.prototype.indexOf.call(arguments, 'two') !== -1
}
console.log(hasTwo('uno', 'tuo', 'tre'));
console.log(hasTwo('one', 'two', 'three'));

Методы массивов отрабатывают в контексте массивоподобных объектов потому что реализация методов зависит только от числовых индекстов и свойства length. Обращение к индексам и length внутри метода происходит через this.

Преобразование массивоподобного объекта в массив, и работа уже с массивом

Если при работе с массивоподобным объектом хотелось бы использовать методы массивов, можно из элементов массивоподобного объекта создать массив, и продолжать работу с массивом. Создать массив из элементов массивоподобного объекта можно с помощью цикла for. Тогда функция, определяющая есть ли среди переданных ей аргументов значение 'two' будет выглядеть следующим образом:

function hasTwo() {
    var args = [];
    for (var i = 0; i < arguments.length; i += 1) {
        args.push(arguments[i])
    }
    return args.indexOf('two') !== -1
}
console.log(hasTwo('uno', 'tuo', 'tre')); // false
console.log(hasTwo('one', 'two', 'three')); // true

Либо создание массива можно выполнить с помощью метода slice. Если метод вызывать без аргументов, он создает новый массив, в который входят все элементы массива, на котором метод был вызван. Если вызвать метод в контексте массивоподобного объекта, в результате вызова получается массив с элементами из массивоподобного объекта. Тогда функция, определяющая есть ли среди переданных ей аргументов значение 'two' будет выглядеть следующим образом:

function hasTwo() {
    var args = Array.prototype.slice.call(arguments)
    return args.indexOf('two') !== -1
}
console.log(hasTwo('uno', 'tuo', 'tre')); // false
console.log(hasTwo('one', 'two', 'three')); // true