Стековая организация данных и создание функций с рекурсивными алгоритмами - ориентир для определения качества работы программиста. Обычный алгоритм никогда не был лучшим решением с точки зрения разработки, отладки и модификации. Только функционально полный, алгоритм, обеспечивающий рекурсию на распределенной обработке данных, можно считать развивающимся решением.
Особенности DOM + JavaScript
Страница, загруженная в браузер, образует дерево элементов DOM, к которому имеет непосредственный доступ JavaScript.
Здесь массивы JS - это простейшие объекты, помимо естественной функциональности, реализующие стековую идею (shift/unshift и push/pop). Выделение первого и последнего элемента как особого свойства массива играет важную роль при формировании описаний данных и алгоритмов.
Рекурсивная функция на JavaScript может использовать дополнительный синтаксис и семантику, которых нет на других языках. Анонимные функции позволяют оснащать синтаксические конструкции языка собственным функционалом.
События возникают на элементах дерева DOM, а их обработчики могут изменяться. Работа JS-кода - это параллельное исполнение множества процессов, динамику которого определяет общий функционал, загруженной в браузер, страницы и действия посетителя.
Функциональность, реализованная в виде методов объектов, в сочетании с «распределением» обязанностей по обработке информации между клиентом и сервером позволяет уменьшить нагрузку на сервер и упросить JS-код.
Переменные, массивы и объекты
Описание данных определяет алгоритм их использования. При такой формулировке верно как то, что данные первичны, так и наоборот: первичен алгоритм. «Казнить нельзя помиловать» - из той же сферы, решение принимает тот, кто ставит знак препинания или делает что-то иное.
Естественный язык отличается высочайшим уровнем свободы:
- «выражения смысла» тем, кто говорит;
- «понимания смысла» тем, кто слушает.
«Выражение смысла» одним носителем языка не уступает «свободе использования» другим носителем языка.
JavaScript - язык программирования. Следовательно, никакой свободы нет и быть не может. Программирование - это жесткие синтаксические конструкции и точный смысл. Даже условие, что тип переменной можно определить только в момент ее использования, не дает программисту никакой свободы.
Логика JavaScript такова, что отличить переменную от массива можно только в контексте описания или использования, а это призрачная свобода. Но возможность трансформации чего угодно во что угодно дает программисту необходимые инструменты для построения свободных конструкций данных и алгоритмов их обработки.
Логика JavaScript похожа на логику других языков, но на этом ее сходство с другими языками заканчивается. То, что можно сделать на JS, можно воссоздать на других языках тем точнее, чем ближе они находятся к JavaScript по уровню функциональности и развития.
Синтаксис и семантика массивов JavaScript
Массивы JS - это объекты. Впрочем, иной объект - это простая переменная, но в нужный момент времени она может стать системой объектов. Простая переменная - тоже массив, если придать ей нужную функциональность.
Принято считать достаточными два варианта описания массива или объекта: «[]» и «{}». Квадратные скобки - это простой массив или объект без методов. Фигурные скобки - это ассоциативный массив или объект с методами. Но это только общие положения. Синтаксис сказанного значительно проще:
Применение этого синтаксиса может быть значительно сложнее. Первые два примера - обычные массивы: создаются в цикле, содержат буквы латинского алфавита, отличаются только регистром букв и индексами. В первом случае индексом является порядковый номер буквы по алфавиту, во втором случае индекс - это код буквы.
Третий пример создает ассоциативный массив. Здесь буква является индексом, а ее код - значением. При желании можно все перепутать.
Второй пример сразу демонстрирует особенность JavaScript. Код буквы "A" = 65. На этом простом основании в массив автоматом помещается 64 неопределенных значения.
Функциональность массивов
JS массивы имеют не так много методов, но функциональность sort, forEach, filter, map, every/some, reduce/reduceRight заслуживает особого внимания. Здесь можно использовать собственные функции для определения нужного действия над элементами массива.
Строки символов - удобная форма представления массивов. Массив всегда можно преобразовать в строку и вернуть обратно. JS массив в строку обращается методом:
- sStr = aData.join('«разделитель»')
Все элементы массива aData становятся одной строкой. При слиянии можно использовать «разделитель», который может быть пустым '', одним символом ',' или строкой символов.
При слиянии элементов массива по одному «разделителю» ничто не мешает в методе строки:
- aData = sStr.split('«разделитель»')
Использовать другой «разделитель». Этот механизм позволяет строить довольно интересные алгоритмы обработки информации, хотя основное предназначение трансформации «JS массивы - строки» лежит в передаче и хранении данных. Строка компактнее массива, удобнее для хранения в базе данных и т. п.
Массивы можно дополнять другими массивами (метод concat), изменять порядок следования элементов на обратный (reverse), сортировать (sort) и т. д., но самая интересная и практичная функциональность лежит в первом и последнем элементах.
Стековая организация данных
Пришел последним, ушел первым - идея стека. Массивы JS преуспели в обеспечении функциональности этой идеи. Если положить новый элемент в массив:
- aData.push(oNew)
Он окажется в конце. Если извлечь последний элемент массива (метод pop), то он будет удален из него. Следующая операция извлечения элемента будет обращена вновь к последнему элементу.
Фактически добавление элементов в массив (push) увеличивает его, а извлечение элементов (pop) уменьшает его. Важно, что оба метода работают в конце массива.
Аналогичная пара shift/unshift работает с первым элементом массива, сдвигая остальные элементы вперед или назад. По логике вещей, удобнее и понятнее работать с последним элементом массива, но в некоторых задачах важно работать с префиксами, а не с суффиксами.
Если совместить слияние элементов в строку и стековый механизм, можно еще больше расширить функциональность синтаксиса JavaScript. Например, каждый элемент массива фрукт (яблоко, груша, апельсин, мандарин, ...):
- Тогда push/pop на элементе массива - работа со свойствами конкретного фрукта: цвет, вид, дата поставки...
- Слив все фрукты в одно целое - строку и push/pop - это свойства текущего наличия фруктов: общий вес, состав, количество...
Из массива можно извлекать подмассивы (метод slice) и исходный массив останется невредимым. Однако, если в js удалить элемент массива, то он будет изменен непосредственно на месте. Результата у метода удаления элемента (элементов) splice нет.
Важно. При удалении: первый параметр - индекс, первого удаляемого элемента, второй параметр - количество удаляемых элементов. При извлечении подмассива: первый параметр индекс первого элемента, второй параметр - индекс последнего. При извлечении, новые массивы JS получают то количество элементов, которое находится между первым и вторым параметром, включительно.
Многомерные массивы JavaScript
JavaScript не ограничивает программиста в количестве размерностей массива. Ничто не мешает создавать конструкции (за индексы принимаются номер магазина, склада/полки, наименование товар/группы товаров):
- aShop['NoShop']['BoxF']['Fruits']['Fruit']; // фрукты
- aShop['NoShop']['BoxB']['Berries']['Berry']; // ягоды
- aShop['NoShop']['BoxP']['Potatoes']['Potato']; // картофель
Такое работает. Однако, если в первых двух конструкциях есть несколько вариантов видов фруктов (ягод), то нет места для вариантов сортов и нужна еще одна размерность. В третьем случае есть возможность указать сорта картофеля, а видов у него просто нет.
Очевидно, на практике такое решение громоздко, трудоемко в модификации и тестировании. Не практично. Лучше всего когда размерность одна, максимум две - в очень редких случаях.
Динамичное и практичное решение на JS: массив объектов. Здесь сразу можно выделить объектом магазин, в котором будут объекты полки (склады), а на полках будут лежать объекты по видам, сортам, группам...
При таком подходе сразу появляется динамика по каждому уровню объектов. Можно оптом добавить или удалить массив, JS предлагает методы конкатенации/извлечения и удаления на месте. В первом случае группа товаров, например, перемещается со склада в магазин или обратно, а во втором случае просто продается и исчезает из магазина (удаляется из массива).
Ассоциации и ассоциативные массивы
В последние годы программирование стремилось к простоте. Борьба языков программирования за выживание показала не то, кто победил, а то, что простота - залог верного успеха. Все языки дозволяют создавать космические конструкции в части размерностей, но на практике современные программисты используют два варианта:
- простой (индексный) массив;
- ассоциативный массив.
Обе конструкции имеют индексы от нуля и до текущего количества элементов. Вторая позволяет также обращаться к значению по некоторому названию. По общей логике языка, в JS ассоциативный массив - это упорядоченная совокупность пар. Причем при необходимости пару «ключ»-«значение» можно менять местами.
Если есть массив из 16 пар: цифры шестнадцатеричной системы счисления, то создав простой массив из шестнадцати цифр: от 0 до 15, можно в качестве значений элементов выбрать символы (не цифры): ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'] и конвертировать число из десятичной системы счисления в шестнадцатеричную и обратно.
Ассоциации - внутреннее свойство данных. Программист может использовать ассоциации для организации структур данных на уровне объектов. В этом контексте основной упор делается на создание реальных объектов, внутри которых располагаются ассоциативные массивы по свойствам данных.
Массивы объектов
Объект, в котором расположены массивы свойств, предлагает их использовать по смыслу своих методов. Сущности (виды, сорта, свойства) яблок и груш отличаются, еще они более отличны от цитрусовых или ягод. Каждый фрукт или ягода (по виду и сорту, например) уникальны. Общим объектом можно описать абстракцию по свойствам, но методы здесь будут общими для всех видов и сортов.
Когда в магазин поступает товар, заполняется массив поступивших объектов. Этот массив имеет свои функции доступа к содержащейся в нем информации и дублирует методы входящих в него объектов.
В таком решении можно, например, посчитать количество товара, как по конкретной позиции, так и по всем позициям сразу. Можно вывести на экран любой имеющийся товар вне зависимости от его вида и сорта. Сработает метод именно того товара, к которому произошло обращение.
На практике такой объектно-ориентированный подход позволяет не обращаться к программисту на этапе эксплуатации системы объектов, поскольку она не требует перепрограммирования. Программист предусмотрел свойства, виды, сорта и методы работы с товаром. Этого достаточно, чтобы продавать все то, о чем шла речь выше и многое другое: персики, огурцы, баклажаны, изюм, орехи...
Система объектов в динамике
Понять сущность информации, подлежащей обработке, чрезвычайно важно. Чем полнее представление о предметной области и реальных объектах, которыми она манипулирует, тем более точно можно описать данные и разработать алгоритмы их обработки.
Идеальная картина качественно решенной задачи: составлен перечень элементарных объектов и систем объектов. Установлены все свойства каждого объекта и все связи между ними.
Каждый метод каждого объекта функционально полный, и решает всего одну задачу. К объекту можно обратиться только через его методы, а следовательно, нарушить его функционирование извне не представляется возможным.
Общее решение - это функционал JavaScript привязанный через свойства элементов DOM к функциям, которые обращаются к объектам только через их свойства. При необходимости что-либо развивать дорабатывается либо интерфейс связи с созданной системой объектов, либо какой-либо объект по мере необходимости.