Хакі для створення масивів JavaScript

Проникливі поради щодо створення та клонування масивів у JavaScript.

Дуже важливим аспектом кожної мови програмування є типи даних та структури, доступні в цій мові. Більшість мов програмування надають типи даних для представлення та роботи зі складними даними. Якщо ви працювали з такими мовами, як Python або Ruby, ви мали бачити типи даних, такі як списки , набори , кортежі , хеші , дикти тощо.

У JavaScript не так багато складних типів даних - у вас просто є масиви та об'єкти . Однак у ES6 до мови було додано кілька типів даних та структур, таких як символи , набори та карти .

Масиви в JavaScript - це подібні до списку об'єкти високого рівня із властивістю length та цілочисельними властивостями як індекси.

У цій статті я поділяю пару хак для створення нових масивів JavaScript або клонування вже існуючих.

Створення масивів: конструктор масивів

Найпопулярнішим методом створення масивів є використання буквального синтаксису масиву , який є дуже простим. Однак, коли ви хочете динамічно створювати масиви, синтаксис літералу масиву не завжди може бути найкращим методом. Альтернативний метод - використання Arrayконструктора.

Ось простий фрагмент коду, що демонструє використання Arrayконструктора.

З попереднього фрагмента ми бачимо, що Arrayконструктор створює масиви по-різному залежно від отриманих аргументів.

Нові масиви: із визначеною довжиною

Давайте детальніше розглянемо, що відбувається при створенні нової Arrayзаданої довжини. Конструктор просто встановлює lengthвластивість масиву на задану довжину, не встановлюючи ключів.

З наведеного фрагмента може виникнути спокуса думати, що для кожного ключа в масиві встановлено значення undefined. Але реальність така, що ці клавіші ніколи не були встановлені (вони не існують).

Наступна ілюстрація робить це зрозумілішим:

Це робить марною спробу використовувати будь-який із методів ітерації масиву, наприклад map(), filter()або reduce()маніпулювати ним. Скажімо, ми хочемо заповнити кожен індекс масиву числом 5як значенням. Ми спробуємо наступне:

Ми бачимо, що тут map()це не спрацювало, оскільки властивості індексу не існують у масиві - lengthіснує лише властивість.

Давайте розглянемо різні способи вирішення цієї проблеми.

1. Використання Array.prototype.fill ()

fill()Метод заповнює всі елементи масиву від індексу початкового до кінцевого індексу зі значенням статичного. Кінцевий індекс не включається. Ви можете дізнатись більше про це fill()тут.

Зверніть увагу, що це fill()буде працювати лише у браузерах з підтримкою ES6.

Ось проста ілюстрація:

Тут ми змогли заповнити всі елементи створеного нами масиву 5. За допомогою fill()методу можна встановити будь-яке статичне значення для різних індексів масиву .

2. Використання Array.from ()

Array.from()Метод створює новий, неглибоко-скопійований Arrayекземпляр з масиву або як Iterable об'єкта. Ви можете дізнатись більше про це Array.from()тут.

Зверніть увагу, що це Array.from()буде працювати лише у браузерах з підтримкою ES6.

Ось проста ілюстрація:

Тут ми тепер маємо справжні undefinedзначення, встановлені для кожного елемента масиву, що використовує Array.from(). Це означає , що тепер ми можемо йти вперед і використовувати методи , як .map()і .filter()в масиві, так як властивості індексу в даний час існують.

Ще одне, на що варто звернути увагу, Array.from()- це те, що він може приймати другий аргумент, який є функцією відображення. Він буде викликаний для кожного елемента масиву. Це робить зайвим дзвінок .map()після Array.from().

Ось простий приклад:

3. Використання оператора розповсюдження

Оператор розповсюдження( ...), доданий у ES6, може використовуватися для розповсюдження елементів масиву, встановлюючи відсутнім елементам значення undefined. Це дасть той самий результат, що і просто виклик Array.from()лише масиву як єдиного аргументу.

Ось проста ілюстрація того, як використовувати оператор поширення:

Ви можете продовжувати використовувати такі методи, як .map()і .filter()в масиві, оскільки властивості індексу тепер існують.

Використання Array.of ()

Подібно до того, як ми спостерігали при створенні нових масивів за допомогою Arrayконструктора або функції, Array.of()поводиться дуже подібним чином. Насправді, єдина різниця між Array.of()і Arrayполягає в тому, як вони обробляють один цілочисельний аргумент, переданий їм.

Поки Array.of(5)створює новий масив з одним елементом, 5і властивість length 1, Array(5)створює новий порожній масив з 5 порожніми слотами та властивістю length 5.

var array1 = Array.of(5); // [5] var array2 = Array(5); // Array(5) {length: 5}

Крім цієї головної різниці, він Array.of()поводиться так само, як Arrayконструктор. Ви можете дізнатись більше про це Array.of()тут.

Зверніть увагу, що це Array.of()буде працювати лише у браузерах з підтримкою ES6.

Перетворення в масиви: схожі на масив та ітерабелі

Якщо ви пишете функції JavaScript досить довго, ви вже повинні знати про argumentsоб'єкт - це масивноподібний об'єкт, доступний у кожній функції для зберігання списку аргументів, отриманих функцією. Хоча argumentsоб’єкт схожий на масив, він не має доступу до Array.prototypeметодів.

До ES6 зазвичай ви бачите фрагмент коду, як показано нижче, при спробі перетворити argumentsоб'єкт на масив:

За Array.from()допомогою оператора поширення або оператора розповсюдження ви можете зручно перетворити будь-який схожий на масив об’єкт у масив. Отже, замість цього:

var args = Array.prototype.slice.call(arguments);

Ви можете зробити будь-що з наведеного:

// Using Array.from() var args = Array.from(arguments); // Using the Spread operator var args = [...arguments];

Це також стосується ітерабелів, як показано на наступному малюнку:

Тематичне дослідження: Функція дальності

Як практичний приклад перед тим, як продовжити, ми створимо просту range()функцію для реалізації нового вивченого нами масиву . Функція має такий підпис:

range(start: number, end: number, step: number) => Array

Ось фрагмент коду:

У цьому фрагменті коду ми використовували Array.from()новий масив діапазону динамічної довжини, а потім заповнювали його послідовно збільшеними числами, надаючи функцію відображення.

Зверніть увагу, що наведений вище фрагмент коду не буде працювати для браузерів без підтримки ES6, за винятком випадків, коли ви використовуєте поліфіли.

Ось деякі результати виклику range()функції, визначеної у наведеному вище фрагменті коду:

Ви можете отримати демо-версію коду, запустивши на Codepen наступну ручку :

Клонування масивів: виклик

У JavaScript масиви та об'єкти є посилальними типами. Це означає, що коли змінної присвоюється масив або об'єкт, те, що призначається змінній, є посиланням на місце в пам'яті, де зберігався масив або об'єкт.

Масиви, як і будь-який інший об'єкт у JavaScript, є посилальними типами. Це означає, що масиви копіюються за посиланням, а не за значенням.

Зберігання типів посилань таким чином має такі наслідки:

1. Подібні масиви не рівні.

Тут ми бачимо, що хоча array1і array2містять, здавалося б, однакові специфікації масиву, вони не рівні. Це пояснюється тим, що посилання на кожен з масивів вказує на інше місце в пам'яті.

2. Масиви копіюються за посиланням, а не за значенням.

Тут ми намагаємося скопіювати array1в array2, але в основному ми робимо вказівку array2на те саме місце в пам'яті, на яке array1вказує. Отже, обидва array1і array2вказують на одне і те ж місце в пам'яті і рівні.

Наслідком цього є те, що коли ми вносимо зміни до array2, видаляючи останній елемент, останній елемент array1також видаляється. Це пояснюється тим, що зміна фактично була внесена до масиву, що зберігається в пам'яті, тоді як array1і array2є лише вказівниками на те саме місце в пам'яті, де зберігається масив.

Клонування масивів: Хакі

1. Використання Array.prototype.slice ()

slice()Метод створює неповну копію частини масиву , не зраджуючи масив. Ви можете дізнатись більше про це slice()тут.

Фокус полягає у тому, щоб викликати slice()або 0як єдиний аргумент, або взагалі без аргументів:

// with O as only argument array.slice(0); // without argument array.slice();

Ось проста ілюстрація клонування масиву за допомогою slice():

Тут ви можете побачити, що array2це клон array1з однаковими елементами та довжиною. Однак вони вказують на різні місця в пам'яті, і як результат не рівні. Ви також помічаєте, що коли ми вносимо зміни до array2, видаляючи останній елемент, array1залишається незмінним.

2. Використання Array.prototype.concat ()

concat()Метод використовується для об'єднання двох або більше масивів, в результаті чого новий масив, в той час як вихідні масиви залишаються незмінними. Ви можете дізнатись більше про це concat()тут.

Фокус полягає у виклику concat()або порожнього масиву ( []) як аргументу, або взагалі без будь-яких аргументів:

// with an empty array array.concat([]); // without argument array.concat();

Клонування масиву за допомогою concat()схоже на використання slice(). Ось проста ілюстрація клонування масиву за допомогою concat():

3. Використання Array.from ()

Як ми бачили раніше, Array.from()може бути використаний для створення нового масиву, який є мілкою копією вихідного масиву. Ось проста ілюстрація:

4. Використання деструктуризації масивів

З ES6 у нас є кілька більш потужних інструментів, таких як деструктуризація , поширенняоператор , функції стрілок тощо. Деструктуризація є дуже потужним інструментом для вилучення даних із складних типів, таких як масиви та об'єкти.

Фокус полягає у використанні техніки, яка називається параметрами відпочинку, яка передбачає поєднання деструктуризації масиву та оператора розповсюдження, як показано у наступному фрагменті:

let [...arrayClone] = originalArray;

Наведений фрагмент створює змінну з іменем, arrayCloneяка є клоном originalArray. Ось проста ілюстрація клонування масиву за допомогою деструктуризації масиву:

Клонування: дрібне проти глибокого

Усі методи клонування масиву, які ми досліджували до цього часу, створюють неглибоку копію масиву. Це не буде проблемою, якщо масив містить лише примітивні значення. Однак, якщо масив містить вкладені посилання на об'єкти, ці посилання залишаться недоторканими, навіть коли масив клонований.

Ось дуже проста демонстрація цього:

Зверніть увагу, що зміна вкладеного масиву array1також змінила вкладений масив у array2і навпаки.

Рішення цієї проблеми полягає у створенні глибокої копії масиву, і це можна зробити декількома способами.

1. Техніка JSON

Найпростіший спосіб створити глибоку копію масиву - за допомогою комбінації JSON.stringify()та JSON.parse().

JSON.stringify()перетворює значення JavaScript у дійсний рядок JSON, тоді як JSON.parse()перетворює рядок JSON у відповідне значення JavaScript або об'єкт.

Ось простий приклад:

Техніка JSON має деякі недоліки, особливо коли задіяні значення, крім рядків, чисел і булевих значень.

Ці недоліки в техніці JSON можна в основному віднести до способу, яким JSON.stringify()метод перетворює значення в рядок JSON.

Ось проста демонстрація цього недоліку при спробі JSON.stringify()отримати значення, що містить вкладену функцію.

2. Помічник із глибокого копіювання

Життєздатною альтернативою техніці JSON буде впровадження власної допоміжної функції глибокого копіювання для клонування посилальних типів, незалежно від того, це масиви чи об’єкти.

Ось дуже проста і мінімалістична функція глибокого копіювання, яка називається deepClone:

Зараз це не найкраща з функцій глибокого копіювання, як ви скоро побачите з деякими бібліотеками JavaScript - однак вона виконує глибоке копіювання в досить значній мірі.

3. Використання бібліотек JavaScript

Щойно визначена нами допоміжна функція глибокого копіювання недостатньо надійна для клонування всіх типів даних JavaScript, які можуть бути вкладені в складні об'єкти або масиви.

Бібліотеки JavaScript, такі як Lodash та jQuery, забезпечують більш надійні функції утиліти глибокого копіювання з підтримкою різних типів даних JavaScript.

Ось приклад, який використовує _.cloneDeep()бібліотека Lodash:

Ось той самий приклад, але використання $.extend()з бібліотеки jQuery:

Висновок

У цій статті ми змогли дослідити кілька методів для динамічного створення нових масивів та клонування вже існуючих, включаючи перетворення масивоподібних об’єктів та ітерацій у масиви.

Ми також бачили, як деякі нові функції та вдосконалення, введені в ES6, можуть дозволити нам ефективно виконувати певні маніпуляції з масивами.

Ми використовували такі функції, як деструктуризація та оператор поширення для клонування та розповсюдження масивів. Детальніше про деструктуризацію ви можете дізнатися з цієї статті.

Хлопай і слідуй

Якщо ви знайшли цю статтю проникливою, ви можете дати кілька оплесків, якщо не проти.

Ви також можете піти за мною на Medium (Радий Chinda) для отримання більш проникливих статей, які можуть вам виявитися корисними. Ви також можете стежити за мною у Twitter (@gladchinda).

Щасливого злому ...