Массивы в JavaScript

В этой статье мы рассмотрим стандартные JavaScript массивы с числовыми индексами. Массивы объявляются с помощью квадратных скобок:

var fruits = ["Apple", "Orange", "Donkey"]

Чтобы извлечь элемент, поместите его индекс в квадратные скобки. Первый индекс 0:

var fruits = ["Apple", "Orange", "Donkey"]

alert(fruits[0])
alert(fruits[1])
alert(fruits[2])

Мы также можем получить длину массива JavaScript:

var fruits = ["Apple", "Orange", "Donkey"]

alert(fruits.length)

Упс! Мы создали массив с двумя фруктами и ослом. Теперь нам нужно удалить осла.

Содержание

Методы pop и push

Метод pop в JavaScript удаляет элемент массива и возвращает его.

В следующем примере показано, как «Осел» извлекается из массива:

var fruits = ["Apple", "Orange", "Donkey"]
alert("Я удаляю "+fruits.pop())
// Теперь у нас только ["Apple","Orange"]
alert("Теперь размер массива: "+fruits.length) // осел удален

Обратите внимание, что pop изменяет сам массив.

Аналог pop — это метод push, который добавляет элемент в массив. Например, мы забыли добавить персик:

var fruits = ["Apple", "Orange"]
fruits.push("Peach");
// теперь у нас есть ["Apple", "Orange", "Peach"]
alert("Последний элемент:"+fruits[fruits.length-1])

Задание для самостоятельного выполнения

  1. Создайте массив styles с элементами “Jazz”, “Blues”;
  2. Добавьте значение «Rock’n’Roll«;
  3. Замените второе значение с конца значением «Classic«. У вас должен получиться массив: “Jazz”, ”Classic”, ”Rock’n’Roll”. Код должен работать для любой длины массива;
  4. Извлеките последнее значение из массива и выведите его через alert.

Решение

// 1
var styles = ["Jazz", "Bluez"]

// 2
styles.push("Rock'n'Roll") // или: styles[styles.length] = "Rock'n'Roll"

// 3 
styles[styles.length-2] = "Classic"

// 4
alert( styles.pop() )

Методы shift/unshift

Методы shift/unshift работают с концом массива, но вы также можете использовать shift, чтобы сдвинуть элементы вверх (первое значение массива удаляется со сдвигом элементов). Метод unshift позволяет в JavaScript добавить элемент в массив с конца:

var fruits = ["Apple", "Orange"]
var apple = fruits.shift() // теперь у нас остался только ["Orange"]
fruits.unshift("Lemon") // теперь мы имеем ["Lemon", "Orange"]
alert(fruits.length) // 2

И shift, и unshift могут работать с несколькими элементами одновременно:

var fruits = ["Apple"]
fruits.push("Orange","Peach")
fruits.unshift("Pineapple","Lemon")
// теперь массив выглядит так: ["Pineapple", "Lemon", "Apple", "Orange", "Peach"]

Задание для самостоятельного выполнения

Напишите код, чтобы вывести через alert случайное значение из массива arr:

var arr = ["Plum","Orange","Donkey","Carrot","JavaScript"]

Примечание: Код для получения случайного числа от минимального до максимального значения (включительно) следующий:

var rand = min + Math.floor(Math.random()*(max+1-min))

Решение

Нам нужно извлечь случайное число от 0 до arr.length-1 (включительно):

var arr = ["Plum","Orange","Donkey","Carrot","JavaScript"]

var rand = Math.floor(Math.random()*arr.length)

alert(arr[rand])

Перебор массива

В JavaScript перебор массива осуществляется с помощью цикла for:

var fruits = ["Pineapple", "Lemon", "Apple", "Orange", "Peach"]
for(var i=0; i<fruits.length; i++) {
  alert(fruits[i])
}

Задание для самостоятельного выполнения

Создайте функцию find(arr,value), которая находит значение в заданном массиве и возвращает его индекс или -1, если значение не найдено.

Например:

arr = [ "test", 2, 1.5, false ]
find(arr, "test") // 0
find(arr, 2) // 1
find(arr, 1.5) // 2
find(arr, 0) // -1

Решение

Возможное решение может выглядеть так:

function find(array, value) {
  for(var i=0; i<array.length; i++) {
    if (array[i] == value) return i;
  }

  return -1;
}

Но это неверно, потому что == не определяет разницу между 0 и false.

Более корректно при работе с массивами в JavaScript использовать ===. Кроме того новейший стандарт ES5 содержит функцию Array#indexOf. С ее помощью мы можем определить функцию следующим образом:

function find(array, value) {
  if (array.indexOf) return array.indexOf(value) 
  for(var i=0; i<array.length; i++) {
    if (array[i] === value) return i;
  }

  return -1;
}
var arr = ["a", -1, 2, "b"];
var index = find(arr, 2);
alert(index);

Еще разумнее было бы определить find через условие, чтобы проверить, существует ли метод indexOf.

Задание для самостоятельного выполнения

Создайте функцию filterNumeric(arr), которая принимает массив и возвращает новый массив, содержащий только числовые значения из arr.

Пример того, как это должно работать:

arr = ["a", 1, "b", 2];
arr = filterNumeric(arr);
// теперь arr = [1,2]

Решение

Решение заключается в том, чтобы перебрать массив и добавить значения в новый массив, если они являются числовыми.

join и split

Иногда нужен быстрый способ преобразовать JavaScript массив в строку. Именно для этого предназначен метод join.

Он объединяет массив в строку, используя заданный разделитель:

var fruits = ["Lemon","Apple","Orange","Peach"];
var str = fruits.join(', ');
alert(str);

Обратное преобразование легко выполняется с помощью метода split:

var fruits = "Apple,Orange,Peach";
var arr = fruits.split(',');
// arr содержит теперь ["Apple", "Orange", "Peach"]
alert(arr[0]);

Задание для самостоятельного выполнения

Объект включает в себя свойство className, которое содержит имена классов, разделенные пробелами:

var obj = {
	className: 'open menu' 
}

Напишите функцию addClass(obj, cls), которая добавляет класс cls, но только если он не существует:

ddClass(obj, 'new') // obj.className='open menu new'
addClass(obj, 'open')  // без изменений (class already exists)
addClass(obj, 'me') // obj.className='open menu new me'
alert(obj.className)  //

Решение

Нужно разделить className и цикл на части. Если класс не найден, тогда он добавляется.

Цикл немного оптимизирован для увеличения производительности:

function addClass(elem, cls) {
  for(var c = elem.className.split(' '), i=c.length-1; i>=0; i--) {
    if (c[i] == cls) return
  }
	
  elem.className += ' '+cls
}
var obj = { className: 'open menu' }
addClass(obj, 'new')
addClass(obj, 'open') 
alert(obj.className)   // open menu new

В приведенном выше примере переменная c определяется в начале цикла, и для ее последнего индекса задается значение i.

Сам цикл обрабатывается в обратном направлении, заканчиваясь условием i>=0. Потому что i>=0 проверить быстрее, чем i. Что в JavaScript ускоряет поиск в массиве.

Использование length для обрезки массива

С помощью свойства length можно обрезать массив следующим образом:

var arr = [0, 1, 2, 3] 
alert(arr[2]); // элемент все еще здесь
arr.length = 2; // обрезаем массив до двух элементов (то есть остаются: [0,1])
alert(arr[2]); // нет, элемент уже был обрезан

Вы задаете длину, и браузер обрезает массив.

Array представляет собой объект, что из этого следует

На самом деле в JavaScript Array — это Object, дополненный автоматической установкой длины и специальными методами.

Это отличается от концепции в других языках, где массивы представляют собой непрерывный сегмент памяти. Это также отличается от очереди или стека на основе связанных списков.

Нечисловые ключи массива

Ключи — это числа, но они могут иметь любые имена:

arr = [] 
arr[0] = 5
arr.prop = 10 // не делайте так

Но делать этого не рекомендуется. Числовые массивы подходят для числовых ключей, JavaScript ассоциативные массивы — для связанных пар ключ-значение. И смешивать их не стоит.

В JavaScript массивы представляют собой хэш-таблицы с их преимуществами в плане производительности, но и с определенными недостатками.

Например, push/pop работают только с крайними элементами массива, поэтому они невероятно быстры.

push работает только с концом:

var arr = ["My", "array"]
arr.push("something")
alert(arr[1]) // строка "array"

Методы shift/unshift медленные, потому что им нужно изменить нумерацию всего массива. Метод splice также может привести к изменению нумерации:

Таким образом, shift/unshift работают медленнее, чем push/pop. Чем больше массив, тем больше времени занимает в JavaScript сортировка массива.

Задание для самостоятельного выполнения

Какой получится результат? Почему?

arr = ["a", "b"]
arr.push( function() { alert(this) } )
arr[arr.length-1]()  // ?

Решение

Поскольку массивы являются объектами, arr<a href=»/..»>..</a> фактически является вызовом метода объекта, таким как obj<a href=»/method»>method</a>:

arr[arr.length-1]() 
// то же самое что
arr[2]()
// синтаксически это неправильно, но концептуально то же самое:
arr.2() 
// переписанное в том же стиле, что и obj.method()
this = arr в таком случае передается функции, поэтому выводится содержимое arr.
arr = ["a", "b"]

arr.push( function() { alert(this) } )

arr[arr.length-1]() // "a","b",функция

Разреженные массивы, описание length

Свойство length позволяет получить не размер массива в JavaScript, а последний индекс + 1. Это важно, если речь идет о разреженных массивах, с «промежутками» в индексах.

В следующем примере мы добавим два элемента в пустые fruits, но значение length останется 100:

var fruits = [] // пустой массив
fruits[1] = 'Peach'
fruits[99] = 'Apple'
alert(fruits.length)  // 100 (но элементов в массиве всего 2)

Если вы попытаетесь вывести разреженный массив, браузер выдаст значения пропущенных индексов как пустые элементы:

var fruits = [] // пустой массив
fruits[2] = 'Peach'
fruits[5] = 'Apple'
alert(fruits)  // ,Peach,,,Apple (или что-то вроде этого)

Но массив — это объект с двумя ключами. Недостающие значения не занимают места.

Разреженные массивы ведут себя причудливо, когда к ним применяются методы массива. Они понятия не имеют о том, что индексы пропущены:

var fruits = [ ]
fruits[1] = 'Peach'
fruits[9] = 'Apple'
alert( fruits.pop() ) // выталкиваем 'Apple' (на индекс 9)
alert( fruits.pop() )  // выталкиваем не заданный элемент (на индекс 8)

Старайтесь избегать разреженных массивов. Во всяком случае, их методы не будут работать нормально. Вместо этого используйте Object.

Удаление из массива

Как мы знаем, массивы — это объекты, поэтому мы могли бы использовать delete, чтобы удалить значение:

var arr = ["Go", "to", "home"]
delete arr[1]
// теперь arr = ["Go", undefined, "home"]
alert(arr[1]) // не задано

Вы видите, что значение удаляется, но не так, как мы хотели бы, потому что массив содержит незаданный элемент.

Оператор delete удаляет пару ключ-значение, и это все. Естественно, так как массив — это только хэш, позиция удаленного элемента становится undefined.

Чаще всего нам нужно удалить элемент, не оставляя «дыр» между индексами. Существует еще один метод, который поможет нам в этом.

Метод splice

Метод splice может удалять элементы и заменять их в JavaScript многомерных массивах. Его синтаксис:

arr.splice(index, deleteCount[, elem1, ..., elemN])

Удаляет элемент deleteCount, начиная с index, а затем вставляет на его место elem1, …, elemN.

Давайте рассмотрим несколько примеров:

var arr = ["Go", "to", "home"]
arr.splice(1, 1)  // удалить 1 элемент, начиная с индекса 1
alert( arr.join(',') ) // ["Go", "home"] (1 элемент удален)

Таким образом, вы можете использовать splice, чтобы удалить один элемент из массива. Номера элементов массива сдвигаются, чтобы заполнить пробел:

var arr = ["Go", "to", "home"]
arr.splice(0, 1)  // удаляем 1 элемент, начиная с индекса 0
alert( arr[0] ) // "to" стал первым элементом

В следующем примере показано, как заменять элементы:

var arr = ["Go", "to", "home", "now"];
// remove 3 first elements and add two
arr.splice(0, 3, "Come", "here") 
alert( arr ) // ["Come", "here", "now"]

Метод splice возвращает массив удаленных элементов:

var arr = ["Go", "to", "home", "now"];
// удаляем 2 первых элемента 
var removed = arr.splice(0, 2) 
alert( removed ) // "Go", "to" <-- массив удаленных элементов
splice может вставлять элементы, задайте 0 для deleteCount.
var arr = ["Go", "to", "home"];
// со второй позиции 
// удаляем 0 
// и вставляем "my", "sweet"
arr.splice(2, 0, "my", "sweet") 
alert( arr) // "Go", "to", "my", "sweet", "home"

Данный метод также может использовать отрицательный индекс, который отсчитывается с конца массива:

var arr = [1, 2, 5]
// для элемента -1 (предпоследнего)
// удаляем 0 элементов, 
// и вставляем 3 и 4
arr.splice(-1, 0, 3, 4)
alert(arr)  // 1,2,3,4,5

Задание для самостоятельного выполнения

Объект содержит свойство className, в котором содержатся имена классов, разделенные пробелами:

var obj = {
	className: 'open menu'
}

Напишите функцию removeClass(obj, cls), которая удаляет класс cls, если он задан:

removeClass(obj, 'open') // obj.className='menu'

removeClass(obj, 'blabla')  // без изменений (класса для удаления не существует)

Решение

Нужно разделить className на части и перебрать эти части через цикл. Если найдено совпадение, оно удаляется из JavaScript массива объектов, а затем добавляется обратно в конец.

Немного оптимизируем это:

function removeClass(elem, cls) {
  for(var c = elem.className.split(' '), i=c.length-1; i>=0; i--) {
    if (c[i] == cls) c.splice(i,1)
  }
	
  elem.className = c.join(' ')
}
var obj = { className: 'open menu' }
removeClass(obj, 'open')
removeClass(obj, 'blabla')
alert(obj.className)   // menu

В приведенном выше примере переменная c задана в начале цикла, и для i задан ее последний индекс.

Сам цикл выполняется в обратном направлении, заканчиваясь условием i>=0. Это сделано потому, что i>=0 проверяется быстрее, чем i. Что ускоряет поиск свойства в c.

Метод slice

Можно извлечь часть массива с помощью метода slice(begin[, end]):
var arr = ["Why", "learn", "JavaScript"];
var arr2 = arr.slice(0,2) // принимает 2 элемента, начиная с 0
alert(arr2.join(', ')) // "Why, learn"

Обратите внимание, что этот метод не изменяет в JavaScript количество элементов в массиве, а копирует его часть.

Можно опустить второй аргумент, чтобы получить все элементы, начиная с определенного индекса:

var arr = ["Why", "learn", "JavaScript"];
var arr2 = arr.slice(1) // принимает все элементы, начиная с 1
alert(arr2.join(', ')) // "learn, JavaScript"

Метод поддерживает отрицательные индексы, так же, как String#slice.

Метод reverse

Еще один полезный метод — reverse. Предположим, я хочу получить последнюю часть домена, например, “com” от “my.site.com”. Вот как это можно сделать:

var domain = "my.site.com"
var last = domain.split('.').reverse()[0]
alert(last)

Обратите внимание, что JavaScript массивы поддерживают сложный синтаксис (reverse()[0]) для вызова метода, а затем извлечения элемента из полученного массива.

Вы можете создавать более длинные вызовы, например, reverse()<a href=»/0%5D%5B1″>0][1</a>[5]…, синтаксис языка позволяет это.

Сортировка, метод sort(fn)

Метод sort() сортирует массив, не изменяя количество элементов:

var arr = [ 1, 2, 15 ]
arr.sort()
alert( arr )   // 1, 15, 2

Запустите приведенный выше код. Вы получите порядок 1, 15, 2. Это потому, что метод преобразует все в строку и использует по умолчанию лексикографический порядок.

Чтобы сделать метод «умнее», нам нужно передать в него пользовательскую функцию сравнения. Она должна принимать два аргумента и возвращать 1, 0 или -1:

function compare(a, b) {
  if (a > b) return 1
  else if (a < b) return -1
  else return 0
}
var arr = [ 1, 2, 15 ]
arr.sort(compare)
alert( arr )   // 1, 2, 15

Теперь все работает правильно.

Задание для самостоятельного выполнения

Создайте функцию ageSort(people) для сортировки массива объектов людей по возрасту:

var john = { name: "John Smith", age: 23 }
var mary = { name: "Mary Key", age: 18 }
var bob = { name: "Bob-small", age: 6 }
var people = [ john, mary, bob ]
ageSort(people) // теперь люди должны быть отсортированы в таком порядке [ bob, mary, john ]

Выведите имена людей после сортировки JavaScript двумерного массива.

Решение

Нужно использовать Array#sort и пользовательское сравнение:

function ageCompare(a, b) {
  if (a.age > b.age) return 1
  else if (a.age < b.age) return -1
  return 0
}
function ageSort(people) {
  people.sort(ageCompare)
}
// проверьте это
var john = { name: "John Smith", age: 23 }
var mary = { name: "Mary Key", age: 18 }
var bob = { name: "Bob-small", age: 6 }
var people = [ john, mary, bob ]
ageSort(people)
// проверьте порядок
for(var i=0; i<people.length; i++) {
  alert(people[i].name)
}

Более короткий вариант

Функция сравнения может быть короче. Альтернативное решение:

people.sort(function(a,b) { return a.age - b.age })

Оно работает, так как нам не нужно возвращать 1 / -1 / 0, будут работать положительные или отрицательные числа.

Подробнее об определении массива

new Array()

В JavaScript объявление массива можно осуществить с помощью другого синтаксиса:

var arr = Array("Apple", "Peach", "etc")

Он используется редко только потому, что квадратные скобки [] короче.

Также существует вероятность того, что new Array, вызываемый с одним числовым аргументом, создаст массив заданной длины с неопределенными элементами:

var arr = new Array(2,3) // Ок, мы имеем [2, 3]
arr = new Array(2) // получили ли мы [2] ?
alert(arr[0]) // нет! мы получили [undefined, undefined]

В приведенном выше примере мы получили undefined, потому что new Array(number) создает пустой массив с параметром length равным number.

Это может быть весьма неожиданно. Но если вы знаете об этой особенности, вы можете использовать new Array(number), например, так:

var indent = new Array(5).join('a') // aaaa (4 элемента)

Это оптимизированный способ повторить строку.

Многомерные массивы

Массивы в JavaScript могут хранить любой тип данных:

var arr = ["My", "Small array", true, {name:'John'}, 345]
alert(arr[1]) // Small array

Это можно использовать для создания многомерных массивов:

var matrix = [
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]
]
alert(matrix[1][1]) // центральный элемент

Заключение

Это все, что касается углубленного определения JavaScript массивов.

Мы рассмотрели:

  • Как объявляется массив, два различных синтаксиса;
  • Как добавлять, заменять, удалять элементы массива;
  • Как перебрать массив;
  • Как разбить строку на массив, а затем собрать ее снова;
  • Сравнение Array и Object в JavaScript.

Этого должно быть достаточно в 95% случаев. Чтобы узнать о других методах, обратитесь к Руководству по массиву на Mozilla.

 

Данная публикация представляет собой перевод статьи «Array» , подготовленной дружной командой проекта Интернет-технологии.ру

Меню