Что такое массивы в JavaScript
Массивы в JavaScript являются особым подвидом объектов, в котором ключи это цифры, начиная с нуля. Такие “ключи” обычно называют индексами и они присваиваются элементом массива автоматически при добавлении. Мы можем обратиться к любому элементу массива почти так же как обращаемся к свойствам объекта, но в случае с массивами, индекс элемента нужно указывать в квадратных скобках или с помощью нового метода at , который в отличии от первого способа, может принимать и отрицательные значения. Это удобно, если нужно найти последний элемент массива, для чего нужно передать в метод at значение -1. Раньше для такой операции приходилось вычислять индекс последнего элемента отнимая единицу от длинны массива: const lastItem = arr[arr.length -1] .
const myArr = [1, 2, 3, 4, 5]; console.log(myArr[1]); // 2 console.log(myArr[-2]); // undefined console.log(myArr.at(1)); // 2 console.log(myArr.at(-2)); // 4 const myObj = < . myArr >; console.log(myObj); //
Способы создания
Массив можно объявить двумя способами: с помощью конструктора ( const myArr = new Array() ) или просто обернув нужные значения в квадратные скобки ( const myArr = [] ). Первый способ практически не используется в реальной жизни, а глобальный объект Array в основном полезен благодаря своим методам isArray , который проверяет является ли переданный параметр массивом и возвращает результат проверки в виде булева значения, а так же from , который, в отличии от конструктора, не просто принимает набор значений, а создает новый массив из итерируемого или псевдомассива (например, строки или объекта). При этом метод from может принимать необязательными параметрами функцию, которая будет вызвана для каждого элемента нового массива перед добавлением и аргумент, который будет использоваться в качестве this при выполнении такой функции:
const myArr = [1, 2, 3, 4, 5]; console.log(Array.isArray(myArr)); //true console.log(Array.from("string")); //[ 's', 't', 'r', 'i', 'n', 'g' ] function multiply(value) < return value * this.multiplier; >console.log(Array.from(myArr, multiply, < multiplier: 3 >)); // [ 3, 6, 9, 12, 15 ]
Вложенность и клонирование
Массивы — ссылочный тип данных JavaScript т.е. они могут содержать любые типы данных включая ссылки на другие массивы или объекты. Такие массивы называются вложенными и глубина вложенности не ограничена. Механизм ссылок позволяет экономить память, но если нам нужна не просто ссылка, а независимая копия (чаще называется клоном), мы должны убедиться, что вложенные массивы также будут скопированы и будут являться независимыми копиями. Для этого нужно использовать соответствующий подход в клонировании массива:
const nestedArr = [1, 2]; const nestedObg = < name: "John" >; const arr = ["a", nestedArr, nestedObg]; console.log(arr[1][0], arr[2].name); //1 John const clone1 = arr; //простое присвоение значения по ссылке const clone2 = JSON.parse(JSON.stringify(arr)); //а здесь созлается полностью новый массив nestedArr[0] = 3; nestedObg.name = "Stan"; console.log(arr, clone1); // Оригинал: [ 'a', [ 3, 2 ], < name: 'Stan' >] // Клон: [ 'a', [ 3, 2 ], < name: 'Stan' >] //после изменения вложенного массива эти изменения затронут и клон console.log(clone2); //[ 'a', [ 1, 2 ], < name: 'John' >] //здесь клон не зависит от оригинала
Важные особенности
Существуют разные движки JavaScript и все они имеют внутренние механизмы для оптимизации работы с массивами такие как использование специализированных инструкций процессора, хранение массивов в последовательном блоке памяти, использование в некоторых случаях специализированных структур данных (трансформирующие и константные массивы) и многое другое. Хотя методы оптимизации могут отличаться, все они делает работу массивов быстрой. Однако что бы не потерять эту скорость, разработчику нужно знать как наиболее оптимально использовать массивы. Далее мы разберем оптимальные приемы работы с массивами.
Добавление и удаление элементов.
Производительность сильно зависит от того с какой частью массива мы работает. Если воспользоваться методами, которые изменяют первый элемент (с индексом 0), а именно unshift для добавления элемента в начало массива или shift для удаления первого элемента — индексы всех элементов массива будут перезаписаны на новые (сдвинутся на 1). Такая операция займет гораздо больше времени и ресурсов, чем добавление элемента в конец массива методом push или удаление последнего элемента с помощью метода pop . Поэтому при работе с массивами крайне желательно использовать именно push и pop .
const myArr = new Array(10000000).fill(1); for (let i in myArr) < myArr[i] = myArr[i] * 2; >//займет 1151 мс for (let i of myArr) < i = i * 2; >//займет 141 мс
Разница не выглядит критичной, но при обработке большого количества массивов или на слабом железе она может сильно повлиять на производительность.
Разряженные и плотные массивы
В примере с добавлением и удалением элементов мы создали огромный массив с помощью конструктора и заполнили его через метод fill , но что будет если не заполнять такой массив? В таком случае он будет наполнен пустыми элементами. Массивы содержащие пустые элементы называются разряженными, а те что не имеют таковых — плотными. Разряженный массив так же можно создать прямо указав длину ( arr.length = ), если присвоенное значение длинны больше количества элементов в массиве. Или даже случайно, просто поставив две запятые после очередного элемента при описании массива вручную. Ни одна IDE не подсветит этот участок кода поскольку такое описание не нарушает синтаксис.
const myArr = new Array(5); console.log(myArr); //[ ] const myArr2 = []; myArr2.length = 5; console.log(myArr2); //[ ] const myArr3 = [, , , , ,]; console.log(myArr3); //[ ] console.log(myArr[0], myArr2[1], myArr3[2]); //undefined undefined undefined myArr[0] = "value"; console.log(myArr); //[ 'value', ]
Разреженные массивы имеют свое применение в некоторых случаях, однако, следует помнить, что пустые элементы все равно получают индекс, который занимает место в памяти и итерируется при переборе. Поэтому разряженные массивы могут быть менее эффективными в использовании памяти и в производительности по сравнению с обычными массивами в JavaScript. Следовательно лучше избегать прямого указания длинны массива и быть внимательным к количеству запятых.
Перебор массивов
Вы же помните что массивы это особый подвид объектов в JavaScript? Это значит что мы можем итерировать их с помощью цикла for…in… , но для массивов существует специальный цикл for…of… . Разница между ними не только в синтаксисе (в for…in… переменная это ключ объекта, а в for…of… — элемент массива ), но и в скорости работы с массивами:
const myArr = new Array(10000000).fill(1); for (let i in myArr) < myArr[i] = myArr[i] * 2; >//займет 1151 мс for (let i of myArr) < i = i * 2; >//займет 141 мс
Нечисловые свойства
Мы уже знаем, что кроме обращения к элементам массива по индексу можно напрямую обратиться к его длине и даже изменять ее, а что будет, если прямо задать нашему массиву ключ и присвоить этому ключу значение?
const myArr = [1, 2, 3]; myArr.type = "numbers"; console.log(myArr.length); // длинна равна 3 for (let i of myArr) < console.log(i); >// в консоли будут только числовые свойства 1 2 3 for (let i in myArr) < console.log(myArr[i]); >// в консоль выведется и нечисловое свойство numbers console.log(myArr.type); // numbers myArr.push(4); //элемент с нечисловым ключем не повлияет на индексы //новых элементов массива console.log(myArr[3]); //4
Как видно из примера, не произойдет ни какой ошибки, специфичные для массивов методы и способы перебора будут просто игнорировать присвоенную нами пару ключ-значение. У массива не изменится длинна, а for. in. не проитерирует новый элемент. Казалось бы мы получили объект со всеми преимуществами массива, но без ограничений по виду ключей! Такие структуры данных называются псевдомассивами. Но на самом деле даже одно кастомное свойство ломает всю “магию” оптимизации массивов:
const myArr = new Array(10000000).fill("some value"); myArr.someKey = "some value"; myArr.push("new value");//выполнится за 28 мс
Операция добавления нового элемента в конец псевдомассива занимает больше времени, чем добавление элемента в начало настоящего массива. Интересно что цикл for…of… перебирает псевдомассивы так же быстро, как и обычные. Псевдомассивы могут найти свое применение, но только если вы точно знаете что в данной ситуации вам нужно именно такое решение, в противном случае вы получите просадку производительности.
Заключение
Давайте резюмируем что нам нужно помнить при работе с массивами JavaScript:
- Массивы — специальный вид объектов со всеми вытекающими особенностями.
- При копировании массива — его значение передается по ссылке, то же происходит и со вложенными в него массивами и ссылочными типами данных, поэтому для создания независимого массива его нужно клонировать специальными способами.
- Значительно производительнее добавлять и удалять элементы в конец массива, чем в его начало или в любой другой участок.
- Прямое указание длинны массива или лишняя запятая при перечислении элементов создадут разряженный массив, который не будет оптимален по расходу памяти и производительности.
- Перебирать массивы быстрее с помощью цикла for…of… , чем любым другим способом.
- Массиву можно добавить кастомное свойство, но тогда он превратится в псевдомассив и движок JavaScript не сможет так хорошо оптимизировать работу с ним.
Массивы в JavaScript
Массив – это специальная структура данных, которая предназначена для хранения упорядоченных коллекций значений.
Массивы очень популярная конструкция и довольно часто используются в коде, т.к. позволяют нам хранить несколько значений в одной переменной.
Например, вместо того чтобы использовать 5 переменных можно объявить одну со всеми этими значениями:
const ocean1 = 'Атлантический'; const ocean2 = 'Индийский'; const ocean3 = 'Тихий'; const ocean4 = 'Северный Ледовитый'; const ocean5 = 'Южный'; // вместо них массив const oceans = ['Атлантический', 'Индийский', 'Тихий', 'Северный Ледовитый', 'Южный'];
Каждое значение в массиве имеет свой порядковый номер (индекс). Значения называются элементами. Первый элемент массива имеет индекс 0, второй – 1, третий – 2 и т.д.
На следующем рисунке показан массив, состоящий из 5 элементов: 123, 7, 50, -9, 24.
При этом необязательно, чтобы все элементы массива имели один и тот же тип данных. Элементами могут быть любые значения – даже другие массивы. Это позволяет создавать сложные структуры данных, например, такие как массивы объектов или массивы массивов.
Создание массива
Создавать новые массивы в JavaScript можно двумя способами:
- с использованием литерала, т.е. посредством квадратных скобок [] и перечислением в них элементов через запятую;
- путем создания нового экземпляра класса Array .
Пример создания пустого массива:
// посредством литерала массива const arr = []; // с использованием конструктора Array() const otherArr = new Array();
Метод создания массива с помощью литерала (квадратных скобок) более предпочтителен и в большинстве случаев лучше использовать именно его.
При объявлении массива в нём можно сразу создать элементы. Для этого внутрь скобок необходимо поместить элементы, отделив их друг от друга запятой:
const numArr = [3, -5, 9, 1, 21]; // с помощью Array() // const numArr = new Array(3, -5, 9, 1, 21);
Внимание! Если конструктору Array() передать один аргумент, который является числом, то он создаст массив с указанным количеством элементов. Значения элементов при этом будут неопределёнными (пустыми):
const arr = new Array(3); // [ , , ]
При создании массивов в конец разрешается вставлять необязательную завершающую запятую:
const coffee = [ Lavazza, Nescafe, Jardin, ];
Доступ к элементам
Получение доступа к элементу массива выполняется через квадратные скобки, внутрь которых нужно поместить индекс.
Так как индексы нумеруются с 0, то для получение первого, второго и третьего элемента нужно использовать индексы 0, 1 и 2.
const colors = ['black', 'white', 'grey']; console.log( colors[0] ); // 'black' console.log( colors[1] ); // 'white' console.log( colors[2] ); // 'grey'
При попытке получить доступ к несуществующему элементу возвращается undefined :
console.log( colors[3] ); // undefined
Чтобы изменить элемент, ему нужно просто присвоить новое значение:
colors[1] = 'yellow'; // ['black', 'yellow', 'grey']
Сейчас в переменной $colors три элемента. Для добавления нового элемента в массив, можно просто присвоить нужное значение следующему индексу:
colors[3] = 'red'; // ['black', 'yellow', 'grey', 'red']
Если при добавлении элемента случайно пропустить индекс, то в массиве появится неопределенный элемент:
colors[5] = 'green'; // ['black', 'yellow', 'grey', 'red', , 'green'] console.log( colors[4] ); // undefined
Определить количество элементов в массиве можно с помощью свойства length :
console.log( colors.length ); // 6
Зная количество, получить последний элемент можно так:
const lastIndex = colors.length – 1; console.log( colors[lastIndex] ); // "green"
Пример массива, элементы которого содержат различные типы данных:
const arr = [5, {name: 'Василий', age: 35}, [7, 54, 2], true, function() { console.log('good'); }]; // получим элемент с индексом 1, который в этом примере является объектом, а затем значение его свойства age console.log(arr[1].age); // 35 // получим элемент с индексом 2, который является массивом, а затем его элемент с индексом 1 console.log(arr[2][1]); // 54 // получим элемент с индексом 4, т.е. функцию и выполним её arr[4](); // "good"
В качестве элементов массива можно использовать выражения:
const ratio = 5; const point = [3 * ration, 7 * ratio];
Массивы являются объектами
Массивы в JavaScript не являются каким-то определённым типом данных. Это объект, у которого прототипом является Array .
Например, получим с помощью оператора typeof тип данных:
const arr = ['One', 'Two', 'Three']; console.log(typeof arr); // "object"
А так как массивы являются объектами, то при его копировании, передаётся не он сам, а ссылка на него:
const names = ['Иван', 'Вася']; const copyNames = names; copyNames[1] = 'Петр'; console.log(names); // ['Иван', 'Петр']
Создать копию массива можно следующими способами:
const names = ['Иван', 'Вася']; // клонирование массива с помощью slice const cloneNames1 = names.slice(); // с помощью оператора spread const cloneNames2 = [. names]; // с помощью Array.from const cloneNames3 = Array.from(names);
Конструктором массивов является Array :
const arr = ['One', 'Two', 'Three']; console.log(arr.constructor === Array); // true
При этом прототипом массивов является «Array.prototype»:
console.log( Object.prototype.toString.call(arr) ); // "[object Array]" console.log( Object.prototype.toString.call(Array.prototype) ); // "[object Array]"
Проверить содержит ли переменная массив можно следующим образом:
// 1 способ console.log( Array.isArray(arr) ); // true // 2 способ console.log( arr instanceof Array ); // true // 3 способ console.log( arr.constructor === Array ); // true
Перебор массива
Перебор элементов можно выполнить различными способами.
1. Использовать цикл for :
const students = ['Петя', 'Вася', 'Коля', 'Максим']; for (let i = 0, length = students.length; i < length; i++) { console.log( students[i] ); }
2. Метод forEach :
students.forEach((item) => { console.log(item); });
Этот метод является очень популярным. С помощью него можно перебрать все элементы массива и выполнить определённые действия. Методу forEach в качестве аргумента нужно передать функцию. В этом примере используется стрелочная функция. Эта функция является колбэк функцией, так как будет вызвана где-то внутри метода forEach для каждого элемента массива. В данном случае метод forEach вызовет эту функцию 4 раза и будет передавать ей в качестве аргумента значение определенного элемента массива. Получать значение аргумента будем с помощью параметра item .
for (let item of students) { console.log( item ); }
Если нужно с индексами:
const nums = ['One', 'Two', 'Three']; // 1 способ for (let i = 0, length = nums.length; i < length; i++) < console.log(`nums[$] = $`); > // 2 способ nums.forEach(function(item, index, array) { // item – элемент, index – его индекс, array – массив console.log( `nums[${index}] = ${item}` ); });
Поиск элемента в массиве
Найти элемент в массиве можно с помощью метода indexOf() :
const disks = ['500Gb', '1Tb', '2Tb']; const index = disks.indexOf('1Tb'); // 1
В качестве результата он возвращает индекс первого найденного элемента.
Если элемент не найден, то indexOf() возвращает -1 . Это можно использовать, например, чтобы проверить существует ли элемент в массиве:
const disks = ['500Gb', '1Tb', '2Tb']; if (disks.indexOf('1Tb') > -1) { console.log( 'Этот элемент в массиве есть!' ); }
Метод indexOf() позволяет искать элементы не только с начала, но и с определённого индекса. Для этого ему нужно его указать в качестве второго аргумента:
const nums = ['One', 'Two', 'One']; nums.indexOf('One', 1); // 2
Метод lastIndexOf() выполняет то же самое что indexOf() , только осуществляет это с конца:
// arr – массив, element – искомый элемент, from – индекс (по умолчанию arr.length) arr.lastIndexOf(element[, from])
const nums = ['One', 'Two', 'Three', 'One']; nums.lastIndexOf('One'); // 3 nums.lastIndexOf('Two'); // 1
Начиная с версии ECMAScript 7 появился новый метод includes() .
Он позволяет проверить содержит ли массив указанный элемент:
// arr – массив, element – искомый элемент, from – индекс (по умолчанию 0) arr.includes(element[, from])
Он похож на indexOf() , но в отличие от него возвращает true или false .
[1, 2, 3].includes(2); // true [1, 2, 3].includes(4); // false
С использованием второго аргумента:
[1, 2, 3].includes(2, 1); // true [1, 2, 3].includes(2, -1); // false
При отрицательных значениях from поиск выполняется начиная с array.length + from .
В отличие от indexOf() , в котором используется строгое равенство (Strict Equality Comparison), в includes() используется алгоритм равенства SameValueZero. Это значит, что вы можете, например, определить, содержит ли массив NaN :
[1, 2, NaN].includes(NaN); // true [1, 2, NaN].indexOf(NaN); // -1
Также в отличие от indexOf() , includes() не пропускает отсутствующие элементы:
[1, , 3].includes(undefined); // true [1, , 3].indexOf(undefined); // -1
Удаление элементов массива
Удаление элемента массива с помощью delete делает его неопределённым (пустым). Длина массива при этом не изменяется.
const nums = ['One', 'Two', 'Three', 'Four', 'Five']; delete nums[2]; // ['One', 'Two', , 'Four', 'Five']
Если нужно конкретно удалить элемент из массива, то можно воспользоваться методом splice .
Пример удаления элемента массива по индексу:
const nums = ['One', 'Two', 'Three', 'Four', 'Five']; nums.splice(2, 1); // ['One', 'Two', 'Four', 'Five'] По значению это можно сделать так: const nums = ['One', 'Two', 'Three', 'Four', 'Five']; for (let i = 0; i < nums.length; i++) { if (nums[i] === 'Three') { nums.splice(i--, 1); } }
С помощью filter() :
const nums = ['One', 'Two', 'Three', 'Four', 'Five']; const result = nums.filter(value => value !== 'Three'); console.log(result); // ['One', 'Two', 'Four', 'Five']
Добавление и удаление элементов
В JavaScript для добавления и удаления элементов имеются следующие методы:
- push() – для добавления одного или нескольких элементов в конец массива;
- unshift() – для добавления одного или нескольких элементов в начало массива;
- pop() – для удаления последнего элемента;
- shift() – для удаления первого элемента.
Пример использования методов:
const planets = ['Меркурий']; // добавим в конец 2 элемента planets.push('Земля', 'Марс'); // ["Меркурий", "Земля", "Марс"] // добавим в начало 1 элемент planets.unshift('Венера'); // ["Венера", "Меркурий", "Земля", "Марс"] // удалим последний элемент planets.pop(); // ["Венера", "Меркурий", "Земля"] // удалим первый элемент planets.shift(); // ["Меркурий", "Земля"]
Методы push() и unshift() в качестве результата возвращают количество элементов в массиве, а pop и unshift - удаленный элемент:
const list = ['One']; console.log(list.push('Two', 'Three')); // 3 console.log(list.unshift('Four')); // 4 console.log(list.pop()); // "Four" console.log(list.shift()); // "One"
Функции для работы с массивами (методы объекта Array)
Объект Array содержит следующие методы (функции) для работы с массивами:
slice - копирование участка массива
Метод slice предназначен для копирования участка массива. При этом он не изменяет исходный массив, а возвращает в качестве результата новый массив, состоящий из выбранных элементов.
Метод slice имеет 2 параметра:
- 1 параметр (обязательный) - предназначен для указания индекса элемента, с которого необходимо начать копировать элементы;
- 2 параметр (необязательный) - предназначен для указания индекса элемента, до которого необходимо копировать (при этом он не включается в новый массив). Если его не указать, то будут скопированы элементы до конца указанного массива.
let namePlanets = ["Венера", "Меркурий", "Земля", "Марс", "Юпитер"]; let newNamePlanets = namePlanets.slice(2, 4); // ["Земля", "Марс"]
splice - изменение содержимого массива
Метод splice предназначен для изменения содержимого массива. Он может использваться как для добавления элементов в массив, так и для их удаления.
Синтаксис метода splice :
array.splice(startIndex, deleteCount [, element1[, element2[, . ]]]); /* startIndex (обязательный) - стартовый индекс элемента, с которого нужно начать изменение массива. Если в качестве startIndex указать число, большее длины массива, то стартовый индекс будет установлен на конец массива. Если в качестве startIndex указать отрицательное число, то отсчет стартового элемента будет вестись с конца. deleteCount (обязательный) - число, показывающее какое количество элементов необходимо удалить из массива. Если элементы не нужно удалять из массива, то deleteCount необходимо установить 0. После этого нужно указать как минимум один новый элемент, который нужно добавить в массив. Если в качестве deleteCount указать число, которое будет превышать количество оставшихся элементов в массиве, начиная с startIndex, то в этом случае они всё равно будут удалены (т.е. все элементы до конца массива, начиная со стартового индекса) element1, element2, . (необязательные) - элементы которые нужно добавить в массив. */
Примеры использования метода splice.
Применения метода splice для удаления части элементов из массива.
let namePlanets = ["Венера", "Меркурий", "Земля", "Марс"]; namePlanets.splice(2, 2); //["Земля", "Марс"] console.log(namePlanets); // ["Венера", "Меркурий"]
Применение метода splice для удаления элемента из массива и добавления в него новых.
let namePlanets = ["Венера", "Меркурий", "Земля", "Марс"]; namePlanets.splice(1, 1, "Уран", "Нептун", "Сатурн"); // ["Меркурий"] console.log(namePlanets); // ["Венера", "Уран", "Нептун", "Сатурн", "Земля", "Марс"]
Применение метода splice только для добавления новых элементов в массив.
let namePlanets = ["Юпитер", "Сатурн", "Уран"]; namePlanets.splice(0, 0, "Венера", "Меркурий", "Земля", "Марс"); // [] console.log(namePlanets); // ["Венера", "Меркурий", "Земля", "Марс", "Юпитер", "Сатурн", "Уран"]
join - преобразование массива в строку
Метод join предназначен для соединения всех элементов массива в строку.
Синтаксис метода join :
array.join([separator]); /* separator (необязательный) - разделитель, который используется в качестве соединительной строки между каждым элементом массива. Если данный параметр не указать, то в качестве соединительной строки будет использоваться ",". Если в качестве параметра указать пустую строку, то элементы массивы в возвращаемой строке между собой ничем разделены не будут */
let berries = ["Виноград", "Виноград", "Смородина", "Шиповник"]; let berriesStr1 = berries.join(); // "Виноград,Виноград,Смородина,Шиповник" let berriesStr2 = berries.join(""); // "ВиноградВиноградСмородинаШиповник" let berriesStr3 = berries.join(", "); // "Виноград, Виноград, Смородина, Шиповник" let berriesStr4 = berries.join(" + "); // "Виноград + Виноград + Смородина + Шиповник"
Если в качестве separator использовать не строку, то он будет преобразован к строке.
let berries = ["Виноград", "Виноград", "Смородина", "Шиповник"]; let berriesStr1 = berries.join(false); // "ВиноградfalseВиноградfalseСмородинаfalseШиповник" let berriesStr2 = berries.join(4/2); // "Виноград2Виноград2Смородина2Шиповник"
Элементы массива, которые имеют в качестве значения null или undefined, будут приведены к пустой строке.
let arr = [0, undefined, 5, null, -4]; let arrStr = arr.join(", "); // "0, , 5, , -4"
Преобразование строки в массив
Создание массива из строки посредством её разбивания с помощью разделителя в JavaScript осуществляется с помощью метода split() . Разделитель указывается в качестве аргумента.
const str = 'Процессоры||Материнские платы||Видеокарты'; const items = str.split('||'); // ['Процессоры', 'Материнские платы', 'Видеокарты']
Переворот массива
Перестановка элементов массива в обратном порядке осуществляется в JavaScript с помощью reverse() :
const nums = [1, 2, 3, 4, 5]; // переворачиваем массив nums.reverse(); // [5, 4, 3, 2, 1]
Сортировка элементов массива
Сортировка массива выполняется с помощью метода sort() . По умолчанию он сортирует массив в порядке следования символов в кодировке Unicode.
const auto = ['Mazda', 'Audi', 'Toyota', 'Nissan', 'Tesla']; // сортируем массив auto.sort(); // ['Audi', 'Mazda', 'Nissan', 'Tesla', 'Toyota']
В обратном порядке:
auto.sort().reverse(); // ['Toyota', 'Tesla', 'Nissan', 'Mazda', 'Audi']
Массивы
Объекты позволяют хранить данные со строковыми ключами. Это замечательно.
Но довольно часто мы понимаем, что нам необходима упорядоченная коллекция данных, в которой присутствуют 1-й, 2-й, 3-й элементы и т.д. Например, она понадобится нам для хранения списка чего-либо: пользователей, товаров, элементов HTML и т.д.
В этом случае использовать объект неудобно, так как он не предоставляет методов управления порядком элементов. Мы не можем вставить новое свойство «между» уже существующими. Объекты просто не предназначены для этих целей.
Для хранения упорядоченных коллекций существует особая структура данных, которая называется массив, Array .
Объявление
Существует два варианта синтаксиса для создания пустого массива:
let arr = new Array(); let arr = [];
Практически всегда используется второй вариант синтаксиса. В скобках мы можем указать начальные значения элементов:
let fruits = ["Яблоко", "Апельсин", "Слива"];
Элементы массива нумеруются, начиная с нуля.
Мы можем получить элемент, указав его номер в квадратных скобках:
let fruits = ["Яблоко", "Апельсин", "Слива"]; alert( fruits[0] ); // Яблоко alert( fruits[1] ); // Апельсин alert( fruits[2] ); // Слива
Мы можем заменить элемент:
fruits[2] = 'Груша'; // теперь ["Яблоко", "Апельсин", "Груша"]
…Или добавить новый к существующему массиву:
fruits[3] = 'Лимон'; // теперь ["Яблоко", "Апельсин", "Груша", "Лимон"]
Общее число элементов массива содержится в его свойстве length :
let fruits = ["Яблоко", "Апельсин", "Слива"]; alert( fruits.length ); // 3
Вывести массив целиком можно при помощи alert .
let fruits = ["Яблоко", "Апельсин", "Слива"]; alert( fruits ); // Яблоко, Апельсин, Слива
В массиве могут храниться элементы любого типа.
// разные типы значений let arr = [ 'Яблоко', < name: 'Джон' >, true, function() < alert('привет'); >]; // получить элемент с индексом 1 (объект) и затем показать его свойство alert( arr[1].name ); // Джон // получить элемент с индексом 3 (функция) и выполнить её arr[3](); // привет
Висячая запятая
Список элементов массива, как и список свойств объекта, может оканчиваться запятой:
let fruits = [ "Яблоко", "Апельсин", "Слива", ];
«Висячая запятая» упрощает процесс добавления/удаления элементов, так как все строки становятся идентичными.
Получение последних элементов при помощи «at»
Новая возможность
Эта возможность была добавлена в язык недавно. В старых браузерах может понадобиться полифил.
Допустим, нам нужен последний элемент массива.
Некоторые языки программирования позволяют использовать отрицательные индексы для той же цели, как-то так: fruits[-1] .
Однако, в JavaScript такая запись не сработает. Её результатом будет undefined , поскольку индекс в квадратных скобках понимается буквально.
Мы можем явно вычислить индекс последнего элемента, а затем получить к нему доступ вот так: fruits[fruits.length - 1] .
let fruits = ["Apple", "Orange", "Plum"]; alert( fruits[fruits.length-1] ); // Plum
Немного громоздко, не так ли? Нам нужно дважды написать имя переменной.
К счастью, есть более короткий синтаксис: fruits.at(-1) :
let fruits = ["Apple", "Orange", "Plum"]; // то же самое, что и fruits[fruits.length-1] alert( fruits.at(-1) ); // Plum
Другими словами, arr.at(i) :
- это ровно то же самое, что и arr[i] , если i >= 0 .
- для отрицательных значений i , он отступает от конца массива.
Методы pop/push, shift/unshift
Очередь – один из самых распространённых вариантов применения массива. В области компьютерных наук так называется упорядоченная коллекция элементов, поддерживающая два вида операций:
- push добавляет элемент в конец.
- shift удаляет элемент в начале, сдвигая очередь, так что второй элемент становится первым.
Массивы поддерживают обе операции.
На практике необходимость в этом возникает очень часто. Например, очередь сообщений, которые надо показать на экране.
Существует и другой вариант применения для массивов – структура данных, называемая стек.
Она поддерживает два вида операций:
- push добавляет элемент в конец.
- pop удаляет последний элемент.
Таким образом, новые элементы всегда добавляются или удаляются из «конца».
Примером стека обычно служит колода карт: новые карты кладутся наверх и берутся тоже сверху:
Массивы в JavaScript могут работать и как очередь, и как стек. Мы можем добавлять/удалять элементы как в начало, так и в конец массива.
В компьютерных науках структура данных, делающая это возможным, называется двусторонняя очередь.
Методы, работающие с концом массива:
Удаляет последний элемент из массива и возвращает его:
let fruits = ["Яблоко", "Апельсин", "Груша"]; alert( fruits.pop() ); // удаляем "Груша" и выводим его alert( fruits ); // Яблоко, Апельсин
И fruits.pop() и fruits.at(-1) возвращают последний элемент массива, но fruits.pop() также изменяет массив, удаляя его.
Добавляет элемент в конец массива:
let fruits = ["Яблоко", "Апельсин"]; fruits.push("Груша"); alert( fruits ); // Яблоко, Апельсин, Груша
Вызов fruits.push(. ) равнозначен fruits[fruits.length] = . .
Методы, работающие с началом массива:
Удаляет из массива первый элемент и возвращает его:
let fruits = ["Яблоко", "Апельсин", "Груша"]; alert( fruits.shift() ); // удаляем Яблоко и выводим его alert( fruits ); // Апельсин, Груша
Добавляет элемент в начало массива:
let fruits = ["Апельсин", "Груша"]; fruits.unshift('Яблоко'); alert( fruits ); // Яблоко, Апельсин, Груша
Методы push и unshift могут добавлять сразу несколько элементов:
let fruits = ["Яблоко"]; fruits.push("Апельсин", "Груша"); fruits.unshift("Ананас", "Лимон"); // ["Ананас", "Лимон", "Яблоко", "Апельсин", "Груша"] alert( fruits );
Внутреннее устройство массива
Массив – это особый подвид объектов. Квадратные скобки, используемые для того, чтобы получить доступ к свойству arr[0] – это по сути обычный синтаксис доступа по ключу, как obj[key] , где в роли obj у нас arr , а в качестве ключа – числовой индекс.
Массивы расширяют объекты, так как предусматривают специальные методы для работы с упорядоченными коллекциями данных, а также свойство length . Но в основе всё равно лежит объект.
Следует помнить, что в JavaScript существует 8 основных типов данных. Массив является объектом и, следовательно, ведёт себя как объект.
Например, копируется по ссылке:
let fruits = ["Банан"] let arr = fruits; // копируется по ссылке (две переменные ссылаются на один и тот же массив) alert( arr === fruits ); // true arr.push("Груша"); // массив меняется по ссылке alert( fruits ); // Банан, Груша - теперь два элемента
…Но то, что действительно делает массивы особенными – это их внутреннее представление. Движок JavaScript старается хранить элементы массива в непрерывной области памяти, один за другим, так, как это показано на иллюстрациях к этой главе. Существуют и другие способы оптимизации, благодаря которым массивы работают очень быстро.
Но все они утратят эффективность, если мы перестанем работать с массивом как с «упорядоченной коллекцией данных» и начнём использовать его как обычный объект.
Например, технически мы можем сделать следующее:
let fruits = []; // создаём массив fruits[99999] = 5; // создаём свойство с индексом, намного превышающим длину массива fruits.age = 25; // создаём свойство с произвольным именем
Это возможно, потому что в основе массива лежит объект. Мы можем присвоить ему любые свойства.
Но движок поймёт, что мы работаем с массивом, как с обычным объектом. Способы оптимизации, используемые для массивов, в этом случае не подходят, поэтому они будут отключены и никакой выгоды не принесут.
Варианты неправильного применения массива:
- Добавление нечислового свойства, например: arr.test = 5 .
- Создание «дыр», например: добавление arr[0] , затем arr[1000] (между ними ничего нет).
- Заполнение массива в обратном порядке, например: arr[1000] , arr[999] и т.д.
Массив следует считать особой структурой, позволяющей работать с упорядоченными данными. Для этого массивы предоставляют специальные методы. Массивы тщательно настроены в движках JavaScript для работы с однотипными упорядоченными данными, поэтому, пожалуйста, используйте их именно в таких случаях. Если вам нужны произвольные ключи, вполне возможно, лучше подойдёт обычный объект <> .
Эффективность
Методы push/pop выполняются быстро, а методы shift/unshift – медленно.
Почему работать с концом массива быстрее, чем с его началом? Давайте посмотрим, что происходит во время выполнения:
fruits.shift(); // удаляем первый элемент с начала
Просто взять и удалить элемент с номером 0 недостаточно. Нужно также заново пронумеровать остальные элементы.
Операция shift должна выполнить 3 действия:
- Удалить элемент с индексом 0 .
- Сдвинуть все элементы влево, заново пронумеровать их, заменив 1 на 0 , 2 на 1 и т.д.
- Обновить свойство length .
Чем больше элементов содержит массив, тем больше времени потребуется для того, чтобы их переместить, больше операций с памятью.
То же самое происходит с unshift : чтобы добавить элемент в начало массива, нам нужно сначала сдвинуть существующие элементы вправо, увеличивая их индексы.
А что же с push/pop ? Им не нужно ничего перемещать. Чтобы удалить элемент в конце массива, метод pop очищает индекс и уменьшает значение length .
Действия при операции pop :
fruits.pop(); // удаляем один элемент с конца
Метод pop не требует перемещения, потому что остальные элементы остаются с теми же индексами. Именно поэтому он выполняется очень быстро.
Аналогично работает метод push .
Перебор элементов
Одним из самых старых способов перебора элементов массива является цикл for по цифровым индексам:
let arr = ["Яблоко", "Апельсин", "Груша"]; for (let i = 0; i
Но для массивов возможен и другой вариант цикла, for..of :
let fruits = ["Яблоко", "Апельсин", "Слива"]; // проходит по значениям for (let fruit of fruits)
Цикл for..of не предоставляет доступа к номеру текущего элемента, только к его значению, но в большинстве случаев этого достаточно. А также это короче.
Технически, так как массив является объектом, можно использовать и вариант for..in :
let arr = ["Яблоко", "Апельсин", "Груша"]; for (let key in arr) < alert( arr[key] ); // Яблоко, Апельсин, Груша >
Но на самом деле это – плохая идея. Существуют скрытые недостатки этого способа:
- Цикл for..in выполняет перебор всех свойств объекта, а не только цифровых. В браузере и других программных средах также существуют так называемые «псевдомассивы» – объекты, которые выглядят, как массив. То есть, у них есть свойство length и индексы, но они также могут иметь дополнительные нечисловые свойства и методы, которые нам обычно не нужны. Тем не менее, цикл for..in выведет и их. Поэтому, если нам приходится иметь дело с объектами, похожими на массив, такие «лишние» свойства могут стать проблемой.
- Цикл for..in оптимизирован под произвольные объекты, не массивы, и поэтому в 10-100 раз медленнее. Увеличение скорости выполнения может иметь значение только при возникновении узких мест. Но мы всё же должны представлять разницу.
В общем, не следует использовать цикл for..in для массивов.
Немного о «length»
Свойство length автоматически обновляется при изменении массива. Если быть точными, это не количество элементов массива, а наибольший цифровой индекс плюс один.
Например, единственный элемент, имеющий большой индекс, даёт большую длину:
let fruits = []; fruits[123] = "Яблоко"; alert( fruits.length ); // 124
Обратите внимание, что обычно мы не используем массивы таким образом.
Ещё один интересный факт о свойстве length – его можно перезаписать.
Если мы вручную увеличим его, ничего интересного не произойдёт. Зато, если мы уменьшим его, массив станет короче. Этот процесс необратим, как мы можем понять из примера:
let arr = [1, 2, 3, 4, 5]; arr.length = 2; // укорачиваем до двух элементов alert( arr ); // [1, 2] arr.length = 5; // возвращаем length как было alert( arr[3] ); // undefined: значения не восстановились
Таким образом, самый простой способ очистить массив – это arr.length = 0; .
new Array()
Существует ещё один вариант синтаксиса для создания массива:
let arr = new Array("Яблоко", "Груша", "и тд");
Он редко применяется, так как квадратные скобки [] короче. Кроме того, у него есть хитрая особенность.
Если new Array вызывается с одним аргументом, который представляет собой число, он создаёт массив без элементов, но с заданной длиной.
Давайте посмотрим, как можно оказать себе медвежью услугу:
let arr = new Array(2); // создастся ли массив [2]? alert( arr[0] ); // undefined! нет элементов. alert( arr.length ); // length 2
Как мы видим, в коде, представленном выше, в new Array(number) все элементы равны undefined .
Чтобы избежать появления таких неожиданных ситуаций, мы обычно используем квадратные скобки, если, конечно, не знаем точно, что по какой-то причине нужен именно Array .
Многомерные массивы
Массивы могут содержать элементы, которые тоже являются массивами. Это можно использовать для создания многомерных массивов, например, для хранения матриц:
let matrix = [ [1, 2, 3], [4, 5, 6], [7, 8, 9] ]; alert( matrix[1][1] ); // 5, центральный элемент
toString
Массивы по-своему реализуют метод toString , который возвращает список элементов, разделённых запятыми.
let arr = [1, 2, 3]; alert( arr ); // 1,2,3 alert( String(arr) === '1,2,3' ); // true
Давайте теперь попробуем следующее:
Массивы в JavaScript: что это и как с ними работать
Массив в JavaScript — это упорядоченный список элементов с указанным индексом (ключом к ним). Когда мы хотим сохранить список определенных элементов, а затем получить к ним доступ с помощью одной переменной, именно тогда применяем массив. В отличие от остальных языков, где массив — это ссылка на несколько переменных, в JavaScript — это единственная переменная, в которой может храниться несколько элементов.
Курс UI/UX для геймдеву.
Під час навчання ви розробите проекти для портфоліо, що складається з 5 ключових аспектів UX/UI-дизайну, та отримаєш необхідні навички для професійного росту.
Особенности массива в JS:
-
В массиве хранятся значения смешанных типов. То есть, массив может содержать в себе числа, строки и объекты других массивов.
Курс Финансовый директор.
Отримайте комплексне розуміння фінансової діяльності. Час підніматись по карʼєрним сходам.
Пара квадратных скобок [] обозначает массив, элементы разделяются запятыми (,). Положение элемента в массиве обозначается индексом.
Важно! Отсчет индекса начинается с 0 и дальше увеличивается на единицу. Число элементов в массиве определяет его длину.
Допустим, есть список каких-то фруктов, которые мы запишем в отдельные переменные:
let fruit1 = "Ananas"; let fruit2 = "Orange"; let fruit3 = "Lemon";
При условии, что фрукта всего три, задача легко решается. А что, если фруктов несколько сотен, а нам нужен конкретный? Для этого используют массив, когда с помощью индекса (ключа) мы можем получить доступ к нужному элементу.
const fruits = ["Ananas","Orange","Lemon"]; fruit1[0]; // "Ananas" фрукт под индексом 0 это Ananas fruit3[2]; //"Lemon" фрукт под индексом 2 это Lemon
Еще получить нужный нам объект можно, используя length (длину). Также length дает возможность перемещать элементы.
const fruits = ["Ananas","Orange","Lemon"]; const len = fruits.length; fruits[len - 1]; // "Lemon"fruits[len - 2]; // "Orange"
Интересно, что у массивов в JS нет фиксированной длины. Мы можем изменить ее в любой момент.
Как создать массив
В JavaScript есть несколько способов формирования массива, самый простой — присвоить переменной значение массива, как мы это сделали ранее:
const fruit = ["Ananas","Mango","Lemon"];
Пробелы и перенос строки не важны. Наш пример можно записать и так:
const fruit = [ "Ananas", "Orange", "Lemon" ];
Также можно создать массив, а затем представить элементы:
Курс Аналітик даних.
Протягом 4 місяців ви вивчите повний набір інструментів для аналізу даних та отримаєте можливість працевлаштування в Laba Group.
const fruit = []; fruit[0]= "Ananas"; fruit[1]= "Orange"; fruit[2]= "Lemon";
Можно использовать Array:
const fruit = new Array("Ananas", "Orange", "Lemon");
Массив в JavaScript может содержать в себе разные типы значений (числа, строки):
let fruit = ["Ananas", 100, "2Orange", "Lemon", true];
Можно поменять местами значения двух переменных:
let fruit1 = 'Ananas'; let fruit2 = 'Mango'; [fruit1,fruit2] =[fruit2, fruit1]; console.log (fruit1); //'Mango' console.log (fruit2); // 'Ananas'
Когда нам не нужен какой-то элемент, мы можем его пропустить:
let [ananas, ,lemon] = ['Ananas','Mango','Lemon']; console.log(ananas); // "Ananas"console.log(lemon); // "Lemon"
Бывает так, что у элемента массива нет значения, тогда его назначают:
let [ananas, lemon = 'Lemon'] = ['Ananas']; console.log (ananas); // 'Ananas' console.log (lemon); // 'Lemon'
Интересно, что в массив может быть вложен еще один массив:
let fruits = ['Ananas', 'Mango', 'Lemon', ['Potato', 'Onion', 'Tomato']];
Как нам вывести Tomato на консоль? Применим индекс!
frits [3] [2] // 'Tomato'
Основы для работы с массивами в JavaScript
Основные методы работы с массивами в JS:
Курс Аналітик даних.
Протягом 4 місяців ви вивчите повний набір інструментів для аналізу даних та отримаєте можливість працевлаштування в Laba Group.
А также не обойтись без оператора spread.
Каждый из перечисленных методов перебирает массив, выполняет определенные модификации или вычисления.
К map() обращаются для создания нового массива из уже готового, применяя при этом функцию к каждому из элементов первого массива:
var names = [ 'Katya', 'Petro', 'Ivan'] var nameLengths = names.map (function (name) < return name.length; // результат – массив с длинами alert (nameLenghts) ; // 5,5,4
Метод filter() фильтрует элементы по условию, указанному в функции:
const fruits = ['Ananas', 'Lemon', 'Orange', 'Lime', 'Plum']; const result = fruits.filter(word => fruit.length <5); console.log(result); Array ["Plum", "Lime"]
Мы задали условие, чтобы из массива в консоли отобразились слова, длина которых меньше 5 символов. В итоге мы получаем два слова — Plum, Lime.
Метод reduce() — инструмент, который сжимает массив до одного значения. Чтобы получить выходное значение, он запускает функцию редуктора для всех элементов массива:
var salary = [200, 310, 560]; var sum = salary.reduce (function (total, amont) < return total+amount>); sum// 1070
С помощью reduce() мы сократили значения трех элементов к одному, то есть к сумме 1070.
Полезный оператор для добавления элементов в массивы, объединения массивов или объектов — spread(…). Происходит своего рода «растягивание» элементов:
const bigfruits = ['Ananas', 'Waterlemon', 'Melon']; const smallfruits = ['Plum', 'Lime', … bigfruits]; // ['Plum', 'Lime', 'Ananas', 'Waterlemon', 'Melon' ] // Массив bigfruits не меняется
В smallfruits добавились значения из bigfruits, но при этом bigfruits не изменился — ни его длина, ни объекты.
Такоже в JS выделяют такие методы:
- Array.isArray — помогает узнать, является ли заданное значение массивом. Мы получим true, если является, или false, если нет.
- Array.isArray (obj) , где obj — значение/объект, который необходимо проверить.
Array.is Array (['Ananas', 'Orange']);// получим trueArray.isArray ('Ananas');// получим false;
- Array.from () — возвращает объект Array из любого объекта со свойством length или итерируемого объекта.
Array.from ('fruit') // ['f', 'r', 'u', 'i', ' t' ]
- Array.of () — создает новый массив, используя любое количество элементов любого типа.
Array.of (element0 [, element1 [, .. [, elementX]]]) Array.of(100, true, 'Ananas', ) (4) [100, true, “Ananas”, <>] Element at index 0: 100Element at index 1:trueElement at index 2: “Ananas”Element at index 3: ) length :4
Методы класса Array
Так, сила массивов JS заключается в их методах. Они дают возможность управлять, сортировать, трансформировать, проводить необходимые манипуляции данными, когда это необходимо.
Каждый метод имеет уникальную функцию, которая дает возможность изменять массивы.
Метод join()
Метод join() возвращает новую строку, где объединены все элементы массива. Разделитель элементов по умолчанию — запятая (,). Этот метод не меняет первоначальный массив.
const fruits = ['Ananas', 'Lime', 'Lemon']; console.log(fruits.join()); //в результатe: "Ananas, Lime, Lemon"
Метод reverse()
Метод reverse() меняет позиции элементов в массиве, крайний элемент становится на первое место, а первый — на последнее.
const fruits = ['Ananas', 'Lime', 'Orange']; fruits.reverse (); // возвращает ["Orange", "Lime", "Ananas"]
Метод sort()
Самым используемым методом стал sort(). Он сортирует элементы по возрастанию или убыванию, по буквам. По умолчанию сортировка происходит по возрастанию и алфавиту. Метод sort() трансформирует исходный массив.
const fruits = ['Ananas', 'Lime', 'Lemon']; fruits.sort (); // возвращает ["Ananas", "Lemon", "Lime"]
Метод concat()
Метод concat() объединяет один или несколько массивов, затем возвращает объединенный массив. Не меняет существующие массивы.
const fuits = ['Ananas', 'Lime', 'Lemon'];const vegatables = ['Potato', 'Onion', 'Carrot'];const merged = fruits.concat(vegatables); console.log(merged); // ["Ananas", "Lime", "Lemon", "Potato", "Onion", "Carrot"];console.log(first); // ["Ananas", "Lime", "Lemon"]console.log(second); // ["Potato", "Onion", "Carrot"]
Метод slice()
Скопировать и клонировать массив в новый массив можно, применив slice().
const fruits = ['Ananas', 'Lemon', 'Lime', 'Orange']; console.log(fruits.slice(2)); // результат: Array ["Lime", "Orange"]
Первоначальный массив не модифицируется.
В качестве альтернативы можно использовать оператор spread(…).
Метод splice()
Метод splice() помогает добавлять и убирать элементы в массиве. Так, исходный массив модифицируется. Для добавления элемента с помощью splice(), нужно обозначить позицию, в которую мы хотим добавить элементы.
Например, добавим элемент Lime по индексу 1 без удаления остальных элементов:
const fruits = ['Ananas', 'Orange', 'Plum']; names.splice (1, 0, 'Lime'); console.log (fruits); // [“Ananas”, “Lime”, “Orange”, “Plum”]
Методы push() и pop()
push() — вставить элемент в конец массива.
pop() — исключает элемент из конца массива.
Метод push() добавляет новые элементы в конец массива.
Так, длина массива меняется.
Добавим Lime к нашим фруктам:
const fruits = ["Ananas", "Orange", "Lemon", "Plum"]; fruits.push("Lime"); //Новый состав фруктов ["Ananas", "Orange", "Lemon", "Plum","Lime"];
Мы научились добавлять, а как же удалить элементы из массива в JS?
Один из самых легких способов — использование метода pop(). При его вызове удаляется элемент из конца массива.
const fruits = ["Ananas", "Orange", "Lemon", "Plum"]; fruits.pop(); // удаляем сливу/Plum
Получаем новый состав:
["Ananas", "Orange", "Lemon"];
Методы unshift() и shift()
Метод unshift() добавляет новые элементы, но уже в начало массива, и меняет его длину.
const fruits = ["Ananas", "Orange", "Lemon", "Plum"]; fruits.unshift("Mango");
Теперь фруктов стало больше:
[("Mango","Ananas", "Orange", "Lemon", "Plum"];
Метод shift() убирает первый элемент массива (длина массива меняется).
var fruits = ["Ananas", "Orange", "Lemon", "Plum"]; fruits.shift();
["Orange", "Lemon", "Plum"];
Такое разнообразие методов необходимо для работы с массивами JavaScript, а еще значительно упрощает написание кода, делает его более чистым.
Курс Python.
Цю мову програмування використовують від базових концепцій до складних застосувань у сферах штучного інтелекту. Після проходження курсу гарантовано отримай роботу в ІТ.