- PVSM.RU - https://www.pvsm.ru -
В этой статье я расскажу про псевдомассивы: «Что это?», «Как с ними работать?», «Чем они отличаются от массива?», «Как преобразовать их в массив?».
Псевдомассив — это объект, который структурно похож на массив. То есть у него есть числовые свойства (индексы) и свойство length
.
Пример:
{0: 'Значение 1', 1: 'Значение 2', 2: 'Значение 3', length: 3};
Главным отличием псевдомассива от массива является его наследование прототипа, то есть свойство __proto__
.
Когда мы посмотрим в свойства массива, то увидим, что он наследует прототип Array объекта. То есть, все свойства, которые есть в объекте Array.prototype будут доступны для любого массива. Если же посмотреть в свойства какого-либо псевдомассива, то можно заметить, что он наследует прототип другого объекта вместе с другими свойствами.
Псевдомассив может наследовать прототипы различных объектов. Вот маленький список типов объектов — псевдомассивов:
DOMTokenList
NamedNodeMap
DOMStringMap
HTMLCollection
NodeList
HTMLAllCollection
StyleSheetList
DOMStringList
Map
HTMLMapElement
CSSRuleList
Это маленькая часть из полного списка типов псевдомассивов, который имеет в себе более пятидесяти типов. Из этого вытекает следующий пункт статьи.
Я дня три разбирал данный вопрос читая различные статьи и в итоге составил всего одно условие: если объект является псевдомассивом, то у него должно быть свойство length
, которое должно являться целым числом и быть больше либо равно нулю.
Number.isInteger(Number(object.length)) && Number(object.length) >= 0
Это условие я составил откинув следующие пукнты:
undefined
Collection
, Map
либо List
. Но данная идея сразу развеялась, так как псевдомассив может иметь тип обычного объекта — Object
,
Но JavaScript «сказал», что и моё условие слишком жестокое. Когда я проанализировал варианты конвертирования псевдомассива в массив, то понял, что JavaScript «съест» псевдомассив, в котором свойство length
равно числу, которое больше либо равно нулю.
typeof object.length === 'number' && Number(object.length) >= 0
И не обязательно, чтобы число было целым (кроме некоторых случаев). JavaScript просто переведёт дробное число в наибольшее целое число, меньшее, либо равное указанному.
Пример:
Array.from({0: 'Значение 1', 1: 'Значение 2', length: 1.6}); // ['Значение 1']
Array.from({0: 'Значение 1', 1: 'Значение 2', 2: 'Значение 3', length: 2.3}); // ['Значение 1', 'Значение 2']
Чтобы преобразить псевдомассив в массив, есть несколько вариантов:
var object = {0: 1, 1: 2, 2: 3, length: 3}
var array = [];
// Преобразуем псевдомассив в массив
for (var i = 0; i < object.length; i++) {
array.push(object[i]);
};
console.log( array ); // [1, 2, 3]
Array.from()
[1]var object = {0: 1, 1: 2, 2: 3, length: 3}
// Преобразуем псевдомассив в массив
var array = Array.from(object);
console.log( array ); // [1, 2, 3]
Array.prototype.slice.call()
[2] ( [].slice.call()
)var object = {0: 1, 1: 2, 2: 3, length: 3}
// Преобразуем псевдомассив в массив
var array = Array.prototype.slice.call(object); // Или сокращённая форма: [].slice.call(object);
console.log( array ); // [1, 2, 3]
NodeList
, HTMLCollection
и прочее).
var object = document.querySelectorAll(selector);
// Преобразуем псевдомассив в массив
var array = [...object];
console.log( array ); // [element, element, element]
__proto__
[4]__proto__
объекта на Array.prototype
, то псевдомассив преобразуется в массив. Но этот метод входит в те самые «кроме некоторых случаев», про которые я писал више, так как, для полного преображения в массив, свойство length
должно является целым числом.
var object = {0: 'a', 1: 'b', 2: 'c', length: 3}
// Меняем __proto__ объекта
object.__proto__ = Array.prototype;
console.log(object); // ['a', 'b', 'c']
Так же тут есть одна особенность: если мы укажем length
число, которое будет меньше чем количество записей в псевдомассиве, то у нас получится массив с количеством записей указанных в length
и с дополнительными свойствами из остатка записей.
var object = {0: 'a', 1: 'b', 2: 'c', 3: 'd', 4: 'e', length: 3}
// Меняем __proto__ объекта
object.__proto__ = Array.prototype;
console.log(object); // ['a', 'b', 'c', 3: 'd', 4: 'e']
И ещё одна заметка: данный метод не сделает объект настоящим массивом, хоть и даст ему нужные параметры. В этом можно убедиться, если проверить объект с помощью функции Array.isArray();
.
var object = {0: 'a', 1: 'b', 2: 'c', 3: 'd', 4: 'e', length: 3}
// Меняем __proto__ объекта
object.__proto__ = Array.prototype;
console.log( Array.isArray(object) ); // false
Это самые популярные методы преображения. Так же нужно сказать, что эти все методы можно не использовать, если вам нужно, к примеру, перебрать псевдомассив с помощью forEach
или отфильтровать его функцией filter
. Для таких целей в функциях есть дополнительная функция .call()
, которая даёт возможность работать с псевдомассивами.
Пример:
var object = {0: 'a', 1: 'b', 2: 'c', length: 3}
// Создаём массив из значений псевдомассива
object = Array.prototype.map.call(object, v => 'Буква: ' + v); // Или сокращённо: [].map.call(object, v => 'Буква: ' + v)
console.log(object); // ['Буква: a', 'Буква: b', 'Буква: c']
На этом я закончу данную статью. Рассказал что хотел, а было ли это полезно для вас, решать вам.
Автор: yuri_spivak
Источник [5]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/javascript/262611
Ссылки в тексте:
[1] Array.from()
: https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Array/from
[2] Array.prototype.slice.call()
: https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Array/slice#Array-like
[3] spread оператора: https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Operators/Spread_operator
[4] __proto__
: https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Object/proto
[5] Источник: https://habrahabr.ru/post/336136/
Нажмите здесь для печати.