Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «typeof для масиву». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**`typeof []` повертає рядок `"object"`, бо в JavaScript масив, це окремий випадок об'єкта, а не самостійний примітивний тип.** Усередині масив реалізований як об'єкт, у якого ключами є числові індекси (`"0"`, `"1"`, `"2"`), а властивість `length` відстежує кількість елементів. Оператор `typeof` просто не має окремої назви для масиву, тому для перевірки беруть `Array.isArray(value)`, це надійний спосіб, що працює і між різними realm (iframe, worker). ```javascript console.log(typeof []); // "object" console.log(Array.isArray([])); // true ``` **Ключове:** `typeof []` завжди дає `"object"`, а справжню перевірку на масив робить `Array.isArray()`.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**`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]"`, це старий, але досі робочий прийом. ### Швидкий приклад ```javascript 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` та інші методи. ```javascript 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`, і він стане схожим на масив, але справжнім масивом не буде. ```javascript const arrayLike = { 0: 'a', 1: 'b', length: 2 }; console.log(Array.isArray(arrayLike)); // false console.log(Array.from(arrayLike)); // ["a", "b"] ``` ### Як коректно перевірити масив Основний спосіб, це `Array.isArray()`. Він перевіряє внутрішній слот об'єкта, а не прототип, тому не залежить від контексту виконання. ```javascript Array.isArray([]); // true Array.isArray([1, 2, 3]); // true Array.isArray('abc'); // false Array.isArray({ length: 0 }); // false ``` Альтернатива, це `instanceof`, який перевіряє ланцюжок прототипів: ```javascript [] instanceof Array; // true ``` Він дає правильну відповідь у межах одного вікна, але масив, створений в `iframe` або в іншому контексті, має власний `Array.prototype`, і перевірка провалюється: ```javascript 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`, який повертає внутрішній тег об'єкта: ```javascript 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]`.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.