Різні типи даних в одному масиві
Так, у JavaScript в одному масиві можна зберігати дані різних типів. Це одна з ключових особливостей мови: масив не типізований, тому його елементами можуть бути числа, рядки, об'єкти, функції і навіть інші масиви.
Теорія
TL;DR
- Масив у JavaScript не типізований: обмежень на типи елементів немає.
- Причина в тому, що масив, це об'єкт, де індекси, це ключі (
"0","1","2"), а значення можуть бути будь-якими. - JavaScript динамічно типізований, тому тип елемента не фіксується при створенні масиву.
- Технічна можливість не означає, що так варто робити: зазвичай краще зберігати однорідні дані.
- Для різнорідних даних природнішою структурою є об'єкт з іменованими полями.
- Якщо змішаний масив таки потрібен, перед обробкою перевіряйте тип кожного елемента.
Швидкий приклад
const mixed = [
42, // число
'hello', // рядок
true, // булеве значення
{ name: 'Tim' }, // об'єкт
[1, 2, 3], // вкладений масив
function () { return 'JS'; } // функція
];
console.log(mixed[0]); // 42
console.log(mixed[1]); // "hello"
console.log(mixed[3].name); // "Tim"
console.log(mixed[4][1]); // 2
console.log(mixed[5]()); // "JS"Кожен елемент живе за своїм індексом і поводиться відповідно до власного типу: до об'єкта звертаємось через крапку, до вкладеного масиву через другий індекс, функцію викликаємо дужками.
Чому це можливо
- У JavaScript масив, це об'єкт, у якого індекси, це ключі (
"0","1","2", ...), а значення можуть бути будь-якого типу. - JavaScript, це динамічно типізована мова, тому тип елемента не фіксується в момент створення масиву.
Це легко перевірити: типи можна змінювати вже після створення, і масив не заперечуватиме.
const items = [1, 2, 3];
items[0] = 'now a string';
items.push(null, undefined, Symbol('id'));
console.log(items.length); // 6Але варто пам'ятати
- Хоча так робити можна, для зручності та читабельності коду зазвичай намагаються зберігати однорідні дані, наприклад список чисел або список об'єктів.
- Якщо в масиві багато різних типів, працювати з ним стає складніше: методи
map,filter,sortіreduceдоводиться обвішувати перевірками типу. - Однорідний масив ще й швидший: рушії оптимізують масиви, у яких усі елементи одного виду, а змішування типів переводить масив у повільніше внутрішнє представлення.
- Коли елементи мають різний зміст, а не різний тип, замість масиву краще взяти об'єкт з іменованими полями:
{ id: 1, title: 'text', active: true }.
Як безпечно обробляти змішаний масив
Якщо змішаний масив таки потрібен, наприклад це розібрані дані з форми або зовнішньої відповіді, перевіряйте тип перед обробкою.
const mixed = [42, 'hello', true, { name: 'Tim' }, [1, 2, 3]];
const numbers = mixed.filter((item) => typeof item === 'number');
const arrays = mixed.filter((item) => Array.isArray(item));
console.log(numbers); // [42]
console.log(arrays); // [[1, 2, 3]]Пам'ятайте, що typeof не розрізняє масив і об'єкт (обидва дають "object"), тому для масиву потрібен Array.isArray().
Коротко
| Питання | Відповідь |
|---|---|
| Чи можна зберігати різні типи? | Так |
| Чому? | Масив, це об'єкт, а JavaScript динамічно типізований |
| Чи рекомендується? | Тільки якщо це справді потрібно |
| Приклад | [1, 'text', true, { id: 1 }, [5, 6], () => {}] |
Типові помилки
- Викликати методи, не перевіривши тип.
mixed.map((x) => x.toUpperCase())впаде на першому ж числі або наnull. - Сортувати змішаний масив.
sort()без компаратора порівнює рядкові представлення, тож[10, 'a', true].sort()дасть несподіваний порядок. - Плутати масив і об'єкт усередині масиву. Для елемента-масиву
typeofповерне"object", тому потрібенArray.isArray(item). - Зберігати різнорідні поля одного запису як масив.
['Tim', 25, true]читається гірше, ніж{ name: 'Tim', age: 25, active: true }, і ламається при зміні порядку. - Розраховувати на однакову поведінку в TypeScript. Там масив типізований, і для змішаних значень доведеться описувати union або кортеж:
(number | string)[]чи[string, number].
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.