typeof для масиву
typeof [] повертає "object", тому що масив у JavaScript, це не окремий тип даних, а спеціалізований об'єкт. Оператор typeof повертає назву типу у вигляді рядка, і серед його можливих відповідей просто немає значення "array".
Теорія
TL;DR
typeof []дорівнює"object", а не"array".- Масив, це об'єкт, ключами якого є числові індекси у вигляді рядків, а
lengthзберігає кількість елементів. - Список відповідей
typeofфіксований:"undefined","boolean","number","bigint","string","symbol","function","object". - Правильна перевірка на масив, це
Array.isArray(value). value instanceof Arrayтеж працює, але ламається, коли масив прийшов з іншого realm (iframe, worker, окремий контекст).Object.prototype.toString.call([])повертає"[object Array]", це старий, але досі робочий прийом.
Швидкий приклад
console.log(typeof []); // "object"
console.log(typeof {}); // "object"
console.log(typeof null); // "object"
console.log(Array.isArray([])); // true
console.log([] instanceof Array); // trueТри різні за змістом значення дають однакову відповідь "object", тому сам по собі typeof не розрізняє масив, звичайний об'єкт і null.
Чому масив, це об'єкт
У специфікації мови масив, це екзотичний об'єкт (exotic object) з особливою поведінкою властивості length. Усередині він влаштований так:
- ключі, це числові індекси у вигляді рядків (
"0","1","2"); lengthавтоматично оновлюється і завжди на одиницю більший за найбільший індекс;- прототипом є
Array.prototype, звідки берутьсяmap,filter,pushта інші методи.
const arr = ['a', 'b'];
console.log(Object.keys(arr)); // ["0", "1"]
console.log(arr.length); // 2
console.log(Object.getPrototypeOf(arr) === Array.prototype); // trueЦе видно і з протилежного боку: звичайному об'єкту можна вручну дати індекси й length, і він стане схожим на масив, але справжнім масивом не буде.
const arrayLike = { 0: 'a', 1: 'b', length: 2 };
console.log(Array.isArray(arrayLike)); // false
console.log(Array.from(arrayLike)); // ["a", "b"]Як коректно перевірити масив
Основний спосіб, це Array.isArray(). Він перевіряє внутрішній слот об'єкта, а не прототип, тому не залежить від контексту виконання.
Array.isArray([]); // true
Array.isArray([1, 2, 3]); // true
Array.isArray('abc'); // false
Array.isArray({ length: 0 }); // falseАльтернатива, це instanceof, який перевіряє ланцюжок прототипів:
[] instanceof Array; // trueВін дає правильну відповідь у межах одного вікна, але масив, створений в iframe або в іншому контексті, має власний Array.prototype, і перевірка провалюється:
const frame = document.createElement('iframe');
document.body.appendChild(frame);
const foreignArray = new frame.contentWindow.Array(1, 2, 3);
console.log(foreignArray instanceof Array); // false
console.log(Array.isArray(foreignArray)); // trueТретій варіант, це виклик стандартного toString, який повертає внутрішній тег об'єкта:
Object.prototype.toString.call([]); // "[object Array]"
Object.prototype.toString.call({}); // "[object Object]"
Object.prototype.toString.call(null); // "[object Null]"Порівняння способів перевірки
| Перевірка | Повертає | Коментар |
|---|---|---|
typeof [] | "object" | Масив, це об'єкт, окремої назви типу немає |
Array.isArray([]) | true | Правильний спосіб, працює між realm |
[] instanceof Array | true | Теж вірно, але залежить від контексту виконання |
Object.prototype.toString.call([]) | "[object Array]" | Працює скрізь, але багатослівно |
[].constructor === Array | true | Легко зламати, перезаписавши constructor |
Типові помилки
- Очікувати від
typeofзначення"array". Такого результату не існує: набір відповідей оператора фіксований специфікацією, і масив потрапляє в"object". - Перевіряти масив через
typeof value === 'object'. Під цю умову підпадають такожnull,{},Date,Mapі будь-який інший об'єкт. - Забувати про
null.typeof nullтеж дорівнює"object", це давня помилка мови, яку залишили заради сумісності. Перед зверненням до властивостей перевіряйте значення окремо. - Покладатися на
instanceofу коді, що працює зiframe,workerабо серверним контекстом. Там значення приходить з іншого realm, іinstanceofдастьfalseна справжньому масиві. - Плутати масив і array-like.
arguments,NodeListі об'єкт з полемlengthповодяться схоже, алеArray.isArray()для них повернеfalse. Щоб отримати справжній масив, використовуйтеArray.from(value)або[...value].
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.