Работа с массивами объектов в javascript
Содержание:
- Квадратные скобки
- Копирование массива в JavaScript
- Работа с массивами JS — удаление из массива
- О различиях, стеке и рекурсии
- Performance
- Как узнать индекс элемента в массиве
- Поиск индекса элемента в JS
- А теперь пересчитаем элементы
- Работа с массивами JS — нечисловые ключи массива
- Поговорим о главной задаче массивов
- Итого
- Итого
Квадратные скобки
Для свойств, имена которых состоят из нескольких слов, доступ к значению «через точку» не работает:
JavaScript видит, что мы обращаемся к свойству , а затем идёт непонятное слово . В итоге синтаксическая ошибка.
Точка требует, чтобы ключ был именован по правилам именования переменных. То есть не имел пробелов, не начинался с цифры и не содержал специальные символы, кроме и .
Для таких случаев существует альтернативный способ доступа к свойствам через квадратные скобки. Такой способ сработает с любым именем свойства:
Сейчас всё в порядке
Обратите внимание, что строка в квадратных скобках заключена в кавычки (подойдёт любой тип кавычек)
Квадратные скобки также позволяют обратиться к свойству, имя которого может быть результатом выражения. Например, имя свойства может храниться в переменной:
Здесь переменная может быть вычислена во время выполнения кода или зависеть от пользовательского ввода. После этого мы используем её для доступа к свойству. Это даёт нам большую гибкость.
Пример:
Запись «через точку» такого не позволяет:
Мы можем использовать квадратные скобки в литеральной нотации для создания вычисляемого свойства.
Пример:
Смысл вычисляемого свойства прост: запись означает, что имя свойства необходимо взять из переменной .
И если посетитель введёт слово , то в объекте теперь будет лежать свойство .
По сути, пример выше работает так же, как и следующий пример:
…Но первый пример выглядит лаконичнее.
Мы можем использовать и более сложные выражения в квадратных скобках:
Квадратные скобки дают намного больше возможностей, чем запись через точку. Они позволяют использовать любые имена свойств и переменные, хотя и требуют более громоздких конструкций кода.
Подведём итог: в большинстве случаев, когда имена свойств известны и просты, используется запись через точку. Если же нам нужно что-то более сложное, то мы используем квадратные скобки.
Копирование массива в JavaScript
slice()
В JS копирование массива бывает поверхностным либо неглубоким (shallow copy) а также deep copy, то есть глубоким.
В первом случае мы присваиваем переменной значение другой переменной, хранящей массив:
var users = "Tom", "Bob", "Bill"]; console.log(users); // var people = users; // shallow copy people1 = "John"; // меняем 2-й элемент console.log(users); //
В нашем случае переменная people после неглубокого копирования станет указывать на тот же массив, что и переменная users. Именно поэтому в случае изменения элементов в people, поменяются элементы и в users, ведь по факту это один и тот же массив.
Вышеописанное поведение не всегда желательно. К примеру, нам надо, чтобы после копирования переменные указывали на отдельные массивы. Тогда подойдёт глубокое копирование посредством метода slice():
var users = "Tom", "Bob", "Bill"]; console.log(users); // var people = users.slice(); // deep copy people1 = "John"; // меняем 2-й элемент console.log(users); // console.log(people); //
Теперь после копирования переменные станут указывать на разные массивы, поэтому мы сможем менять их отдельно друг от друга.
Кроме того, функция slice() даёт возможность копировать часть массива:
var users = "Tom", "Bob", "Bill", "Alice", "Kate"]; var people = users.slice(1, 4); console.log(people); //
В функцию slice() мы передаём начальный и конечный индексы, используемые для выборки значений из нашего массива. В таком случае выборка в новый массив начнётся с первого индекса по индекс № 4, не включая его. И, так как индексация массивов в JavaScript начинается с нуля, в новом массиве будут 2-й, 3-й и 4-й элементы.
push()
Функция push() добавит элемент в конец нашего массива:
var fruit = []; fruit.push("груши"); fruit.push("яблоки"); fruit.push("сливы"); fruit.push("вишни","абрикосы"); document.write("В массиве fruit " + fruit.length + " элемента: <br/>"); document.write(fruit); // груши,яблоки,сливы,вишни,абрикосы
pop()
Такая функция, как pop(), удалит последний элемент из JavaScript-массива:
var fruit = "груши", "яблоки", "сливы"]; var lastFruit = fruit.pop(); // из массива извлекается последний элемент document.write(lastFruit + "<br/>"); document.write("В массиве fruit " + fruit.length + " элемента: <br/>"); for(var i=; i <fruit.length; i++) document.write(fruiti + "<br/>");
Итоговый вывод:
сливы В массиве fruit 2 элемента: груши яблоки
shift()
Теперь рассмотрим функцию shift(). Она может извлекать и удалять 1-й элемент из массива:
var fruit = "груши", "яблоки", "сливы"]; var firstFruit = fruit.shift(); document.write(firstFruit + "<br/>"); document.write("В массиве fruit " + fruit.length + " элемента: <br/>"); for(var i=; i <fruit.length; i++) document.write(fruiti + "<br/>");
Вывод следующий:
груши В массиве fruit 2 элемента: яблоки сливы
unshift()
Что касается функции unshift(), то она добавит новый элемент в самое начало массива:
var fruit = "груши", "яблоки", "сливы"]; fruit.unshift("апельсины"); document.write(fruit);
Вывод браузера:
апельсины,груши,яблоки,сливы
Работа с массивами JS — удаление из массива
Как мы знаем, массивы — это объекты, поэтому мы могли бы использовать delete, чтобы удалить значение:
var arr = delete arr // теперь arr = alert(arr) // не задано
Вы видите, что значение удаляется, но не так, как мы хотели бы, потому что массив содержит незаданный элемент.
Оператор delete удаляет пару ключ-значение, и это все. Естественно, так как массив — это только хэш, позиция удаленного элемента становится undefined.
Чаще всего нам нужно удалить элемент, не оставляя «дыр» между индексами. Существует еще один метод, который поможет нам в этом.
Метод splice
Метод splice может удалять элементы и заменять их в JavaScript многомерных массивах. Его синтаксис:
arr.splice(index, deleteCount)
Удаляет элемент deleteCount, начиная с index, а затем вставляет на его место elem1, …, elemN.
Давайте рассмотрим несколько примеров:
var arr = arr.splice(1, 1) // удалить 1 элемент, начиная с индекса 1 alert( arr.join(',') ) // (1 элемент удален)
Таким образом, вы можете использовать splice, чтобы удалить один элемент из массива. Номера элементов массива сдвигаются, чтобы заполнить пробел:
var arr = arr.splice(0, 1) // удаляем 1 элемент, начиная с индекса 0 alert( arr ) // "to" стал первым элементом
В следующем примере показано, как заменять элементы:
var arr = ; // remove 3 first elements and add two arr.splice(0, 3, "Come", "here") alert( arr ) //
Метод splice возвращает массив удаленных элементов:
var arr = ; // удаляем 2 первых элемента var removed = arr.splice(0, 2) alert( removed ) // "Go", "to" <-- массив удаленных элементов splice может вставлять элементы, задайте 0 для deleteCount. var arr = ; // со второй позиции // удаляем 0 // и вставляем "my", "sweet" arr.splice(2, 0, "my", "sweet") alert( arr) // "Go", "to", "my", "sweet", "home"
Данный метод также может использовать отрицательный индекс, который отсчитывается с конца массива:
var arr = // для элемента -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 == 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.
О различиях, стеке и рекурсии
Различия между реализациями объектно-ориентированной доктрины языками программирования, разница в их философии и понимании новой логики их авторами и разработчиками велика.
Уровень стековой организации данных и уровень рекурсии современных алгоритмов в области интернет-программирования низки и не часто встречаются. Локальное программирование применяет стек и рекурсию только при явной необходимости или при прямом указании задания на разработку.
Эти обстоятельства говорят о том, что динамика переменной и динамика набора значений (массив) в процессе исполнения алгоритма находятся на «начальном» уровне развития представлений о данных и алгоритмах.
Возможно, это не так, а мир интернет-программирования стремительно развивается.
Если переменная (в том числе в статусе массива или объекта) — это стек, то появляется история значений и возможность перемещаться по ней в поисках нужной информации (значения) или оптимального решения (множества значений).
Если функция (алгоритм) представляет собой последовательность действий без рекурсии (без возможности вызвать саму себя) — это тупик, который не может развиваться без сторонней помощи (без участия программиста).
Даже если цель функции не развитие своей функциональности, но функция рекурсивна, она может претендовать на статус полнофункционального решения, а это важно, это уровень программирования и оценка квалификации программиста
Performance
Methods run fast, while are slow.
Why is it faster to work with the end of an array than with its beginning? Let’s see what happens during the execution:
It’s not enough to take and remove the element with the number . Other elements need to be renumbered as well.
The operation must do 3 things:
- Remove the element with the index .
- Move all elements to the left, renumber them from the index to , from to and so on.
- Update the property.
The more elements in the array, the more time to move them, more in-memory operations.
The similar thing happens with : to add an element to the beginning of the array, we need first to move existing elements to the right, increasing their indexes.
And what’s with ? They do not need to move anything. To extract an element from the end, the method cleans the index and shortens .
The actions for the operation:
The method does not need to move anything, because other elements keep their indexes. That’s why it’s blazingly fast.
The similar thing with the method.
Как узнать индекс элемента в массиве
Для того чтобы найти некоторый элемент в массиве можно воспользоваться методом . Значение, которое надо найти у элемента указывается в качестве первого параметра (). Данный параметр является обязательным. Кроме этого при необходимости можно указать индекс элемента () в качестве 2 параметра данного метода, чтобы поиск элемента осуществлялся не с начала, а с указанного элемента.
var listOrder = // "Утюг" - valueElement // 1 - startIndex listOrder.indexOf("Утюг",1 ); //без указания начального индекса listOrder.indexOf("Утюг");
В качестве результата метод возвращает индекс найденного элемента или значение . В том случае если указанное значение имеют несколько элементов, то данный метод возвращает индекс первого найденного элемента.
Примечание: Если вы хотите осуществлять поиск элемента в массиве начиная с конца, то используйте метод .
var fruits = ; var apple = fruits.indexOf("Яблоко"); //1
Поиск индекса элемента в JS
Функции indexOf() и lastIndexOf() вернут индекс 1-го и последнего включения элемента в массиве. К примеру:
var fruit = "яблоки", "груши", "огурцы", "яблоки", "груши"]; var firstIndex = fruit.indexOf("яблоки"); var lastIndex = fruit.lastIndexOf("яблоки"); var otherIndex = fruit.indexOf("черешня"); document.write(firstIndex); // 0 document.write(lastIndex); // 3 document.write(otherIndex); // -1
У firstIndex значение 0, так как первое включение «яблоки» в нашем массиве приходится на индекс 0, последнее — на индекс № 3. Если же элемент в массиве отсутствует, функции indexOf() и lastIndexOf() вернут значение -1.
every()
С помощью every() мы проверим, все ли наши элементы соответствуют какому-нибудь условию:
var numbers = 1, -12, 8, -2, 25, 62 ]; function condition(value, index, array) { var result = false; if (value > ) { result = true; } return result; }; var passed = numbers.every(condition); document.write(passed); // false
В метод every() в качестве параметра осуществляется передача функции, представляющей условие. Данная функция принимает 3 параметра:
function condition(value, index, array) { }
Здесь параметр value представляет перебираемый текущий элемент массива, параметр index представляет индекс данного элемента, а параметр array осуществляет передачу ссылки на массив.
В такой функции можно проверить переданное значение элемента на его соответствие определённому условию. В нашем примере мы проверяем каждый элемент массива на условие, больше ли он нуля. Когда больше, возвращается значение true, так как элемент соответствует условию. Когда меньше, возвращается значение false, т. к. элемент не соответствует нашему условию.
В результате, когда осуществляется вызов метода numbers.every(condition) он выполняет перебор всех элементов нашего массива numbers, а потом поочерёдно передает их в функцию condition. Когда эта функция возвращает значение true для всех элементов, метод every() тоже возвращает true. Когда хоть один элемент условию не соответствует, возвращается false.
some()
Функция/метод some() похожа на every() с той лишь разницей, что осуществляется проверка на соответствие условию хотя бы одного элемента.
Здесь some() вернёт true. Но если соответствующих условию элементов в массиве не будет, вернётся false:
var numbers = 1, -12, 8, -2, 25, 62 ]; function condition(value, index, array) { var result = false; if (value === 8) { result = true; } return result; }; var passed = numbers.some(condition); // true
filter()
Как some() и every(), метод filter()принимает функцию условия. Но тут возвращается массив элементов, соответствующих условию:
var numbers = 1, -12, 8, -2, 25, 62 ]; function condition(value, index, array) { var result = false; if (value > ) { result = true; } return result; }; var filteredNumbers = numbers.filter(condition); for(var i=; i < filteredNumbers.length; i++) document.write(filteredNumbersi + "<br/>");
Вот результат вывода:
1 8 25 62
forEach() и map()
Функции forEach() и map() выполняют перебор элементов, осуществляя с ними некоторые операции. К примеру, чтобы вычислить квадраты чисел в массиве, делаем так:
var numbers = 1, 2, 3, 4, 5, 6]; for(var i = ; i<numbers.length; i++){ var result = numbersi * numbersi]; document.write("Квадрат нашего числа " + numbersi + " равен " + result + "<br/>"); }
Конструкция может быть упрощена посредством forEach():
var numbers = 1, 2, 3, 4, 5, 6]; function square(value, index, array) { var result = value * value; document.write("Квадрат нашего числа " + value + " равен " + result + "<br/>"); }; numbers.forEach(square);
Здесь forEach() в качестве параметра принимает ту же функцию, в которую в процессе перебора элементов передаётся перебираемый текущий элемент, и над ним выполняются операции.
Что касается map(), то этот метод похож на forEach с той лишь разницей, что map() возвращает новый массив, где отображены результаты операций над элементами массива.
Допустим, давайте, применим map к вычислению квадратов чисел нашего массива:
var numbers = 1, 2, 3, 4, 5, 6]; function square(value, index, array) { return result = value * value; }; var squareArray = numbers.map(square); document.write(squareArray);
Функция, передаваемая в map(), получает текущий перебираемый элемент, выполняя над ним операции и возвращая некоторое значение. Именно это значение и попадает в результирующий массив squareArray.
А теперь пересчитаем элементы
Для того чтобы узнать количество записанных в массив элементов нужно воспользоваться свойством объекта – length. Выше я уже использовал эту команду.
На тот момент длина коллекции равнялась 4, а мне нужно было дописать еще один продукт. Так как нумерация элементов массива начинается с нуля, то первая свободная ячейка в том примере находилась под индексом «4». Заметьте, что length определяет не количество занесенных товаров в коллекцию, а длину самого массива. Таким образом, если б среди того списка средние 2 элемента были пустыми
то свойство в результате выдало б число 6.
Зная данную команду, можно заняться разбором циклов. Они нужны для перебора элементов коллекции. Это очень удобный механизм, без которого сложно обойтись. В циклах можно указывать в каком направлении, с какого и по какой элемент, а также с каким шагом стоит перебирать значения.
Наиболее популярным и быстро работающим считается цикл, созданный с помощью ключевого слова for.
1 2 3 4 |
var goods = ; for (var i = 0; i < goods.length; i++) { document.writeln((i+1)+"."+ goods ); } |
Так, первый параметр цикла указывает начальную позицию, второй – конечную, а третий – с каким шагом проходить массив. В ответе будет выведена строка: «1.Говядина 2.Макароны 3.Твердый сыр 4.Специи».
Для наглядности я видоизменил параметры конструкции for. Проанализируйте код и решите для себя кокой получится ответ.
1 2 3 4 |
var goods = ; for (var i = goods.length-1; i >=0; i=i-2) { document.writeln((i+1)+"."+ goods ); } |
Те, кто решил, что в ответе будет строка: «4.Специи 2.Макароны», были правы. Молодцы!
Работа с массивами JS — нечисловые ключи массива
Ключи — это числа, но они могут иметь любые имена:
arr = [] arr = 5 arr.prop = 10 // не делайте так
Но делать этого не рекомендуется. Числовые массивы подходят для числовых ключей, а JavaScript ассоциативный массив — для связанных пар ключ-значение. И смешивать их не стоит.
Массивы в JavaScript представляют собой хэш-таблицы с их преимуществами в плане производительности, но и с определенными недостатками.
Например, push/pop работают только с крайними элементами массива, поэтому они невероятно быстры.
push работает только с концом:
var arr = arr.push("something") alert(arr) // строка "array"
Методы shift/unshift медленные, потому что им нужно изменить нумерацию всего массива. Метод splice также может привести к изменению нумерации:
shift/unshiftpush/popJavaScript
Какой получится результат? Почему?
arr = arr.push( function() { alert(this) } ) arr() // ?
Решение
Поскольку массивы являются объектами, arr<a href=»/..»>..</a> фактически является вызовом метода объекта, таким как obj<a href=»/method»>method</a>:
arr() // то же самое что arr() // синтаксически это неправильно, но концептуально то же самое: arr.2() // переписанное в том же стиле, что и obj.method() this = arr в таком случае передается функции, поэтому выводится содержимое arr. arr = arr.push( function() { alert(this) } ) arr() // "a","b",функция
Поговорим о главной задаче массивов
Первое, что нужно усвоить, это то, что массивы являются разновидностью , которые хранят в себе структурированные данные в определенных ячейка памяти, каждая из которых имеет свой идентификатор (номер).
Они создаются при помощи квадратных скобок, обладают рядом возможностей и методов, а также могут быть многомерными. Главное их преимущество – все элементы пронумерованы, поэтому к ним можно обратиться через идентификатор.
Однако существует еще один вид массивов. На самом деле в статье, посвященной объектам, я рассказывал, что последние также являются массивами. И действительно, они представляют собой ассоциативные массивы, которые являются структурой данных с системой хранения информации в виде «ключ => значение». Такую структуру часто называют хэшем, реже словарем.
Разберем подробнее каждый вид.
При работе с ассоциативными массивами очень легко как добавлять элементы, так и удалять. Так как данный скриптовый язык не типизирован и в нем нет множества строгих правил, создавать элементы объекта можно несколькими способами: перечислить их сразу, проинициализировать после создания объекта, а также создавать по ходу кода.
Сейчас я хочу показать примеры реализации таких механизмов. В первой программе я создал объект patient
, а после перечислил его элементы. Как видите, для patient.
param
я сделал вложенный объект, который обладает своими собственными значениями.
1 2 3 4 5 6 7 8 9 10 |
var patient = new Object(); patient.firstName =»Инна» patient.age =34, patient.param ={ height:169 , weight: 55, disease: «no» } alert(patient.firstName) // выведет «Инна» alert(patient.param.disease) // выведет no |
var patient = new Object();
patient.firstName =»Инна»
patient.age =34,
patient.param ={
height:169 ,
weight: 55,
disease: «no»
}
alert(patient.firstName) // выведет «Инна»
alert(patient.param.disease) // выведет no
Если появится необходимость добавить еще один параметр к уже существующим, например, фамилию, то это делается следующим образом. К презентованному выше коду допишите строки:
Вот так легко коллекция пополняется новыми свойствами. При желании изменить какое-либо значение, его нужно просто присвоить выбранному ключу:
Для объектов также предусмотрена операция удаления ненужных свойств при помощи команды delete:
Итого
Шпаргалка по методам массива:
-
Для добавления/удаления элементов:
- – добавляет элементы в конец,
- – извлекает элемент с конца,
- – извлекает элемент с начала,
- – добавляет элементы в начало.
- – начиная с индекса , удаляет элементов и вставляет .
- – создаёт новый массив, копируя в него элементы с позиции до (не включая ).
- – возвращает новый массив: копирует все члены текущего массива и добавляет к нему . Если какой-то из является массивом, тогда берутся его элементы.
-
Для поиска среди элементов:
- – ищет , начиная с позиции , и возвращает его индекс или , если ничего не найдено.
- – возвращает , если в массиве имеется элемент , в противном случае .
- – фильтрует элементы через функцию и отдаёт первое/все значения, при прохождении которых через функцию возвращается .
- похож на , но возвращает индекс вместо значения.
-
Для перебора элементов:
forEach(func) – вызывает func для каждого элемента. Ничего не возвращает.
-
Для преобразования массива:
- – создаёт новый массив из результатов вызова для каждого элемента.
- – сортирует массив «на месте», а потом возвращает его.
- – «на месте» меняет порядок следования элементов на противоположный и возвращает изменённый массив.
- – преобразует строку в массив и обратно.
- – вычисляет одно значение на основе всего массива, вызывая для каждого элемента и передавая промежуточный результат между вызовами.
-
Дополнительно:
Array.isArray(arr) проверяет, является ли arr массивом.
Обратите внимание, что методы , и изменяют исходный массив. Изученных нами методов достаточно в 99% случаев, но существуют и другие
Изученных нами методов достаточно в 99% случаев, но существуют и другие.
-
arr.some(fn)/arr.every(fn) проверяет массив.
Функция вызывается для каждого элемента массива аналогично . Если какие-либо/все результаты вызовов являются , то метод возвращает , иначе .
-
arr.fill(value, start, end) – заполняет массив повторяющимися , начиная с индекса до .
-
arr.copyWithin(target, start, end) – копирует свои элементы, начиная со и заканчивая , в собственную позицию (перезаписывает существующие).
Полный список есть в справочнике MDN.
На первый взгляд может показаться, что существует очень много разных методов, которые довольно сложно запомнить. Но это гораздо проще, чем кажется.
Внимательно изучите шпаргалку, представленную выше, а затем, чтобы попрактиковаться, решите задачи, предложенные в данной главе. Так вы получите необходимый опыт в правильном использовании методов массива.
Всякий раз, когда вам будет необходимо что-то сделать с массивом, а вы не знаете, как это сделать – приходите сюда, смотрите на таблицу и ищите правильный метод. Примеры помогут вам всё сделать правильно, и вскоре вы быстро запомните методы без особых усилий.
Итого
Массив – это особый тип объекта, предназначенный для работы с упорядоченным набором элементов.
-
Объявление:
Вызов создаёт массив с заданной длиной, но без элементов.
-
Свойство отражает длину массива или, если точнее, его последний цифровой индекс плюс один. Длина корректируется автоматически методами массива.
-
Если мы уменьшаем вручную, массив укорачивается.
Мы можем использовать массив как двустороннюю очередь, используя следующие операции:
- добавляет в конец массива.
- удаляет элемент в конце массива и возвращает его.
- удаляет элемент в начале массива и возвращает его.
- добавляет в начало массива.
Чтобы пройтись по элементам массива:
- – работает быстрее всего, совместим со старыми браузерами.
- – современный синтаксис только для значений элементов (к индексам нет доступа).
- – никогда не используйте для массивов!
Мы вернёмся к массивам и изучим другие методы добавления, удаления, выделения элементов и сортировки массивов в главе: Методы массивов.