Наверх Вниз

Make Web-Sites not War

 

Оглавление

Том III. JavaScript

 

Общие положения

Итак, JavaScript – это язык программирования, позволяющий достичь наивысшей производительности HTML-станиц, наполняя их анимацией, интерактивными элементами и динамическими визуальными эффектами. Язык JS способен сделать веб-страницы более полезными, обеспечивая немедленную обратную связь, то есть, для изменений не требует перезагрузки страницы. Если вы помните, в HTML документе мы можем создавать кнопки. Делается это с помощью зеркального тега <button>. Этот тег сам по себе является простым шаблоном и не обладает никакой функциональностью без нужной настройки. Соответственно, чтобы при нажатии добавленной нами кнопки выполнялся какой-то код, тегу <button>, к примеру, можно присвоить атрибут 'onclick'. Внутри этого атрибута можно указать код JavaScript inline-способом, то есть, без создания дополнительного документа с кодом. Самое элементарное, что мы сейчас можем сделать, просто для понимания – это по нажатию кнопки вывести какое-то сообщение. Выглядеть это будет следующим образом: <button onclick="alert('Hello world!')">Поприветствуй мир!</button>. Еще один абстрактный пример: точно таким же inline-способом (и всеми остальными, разумеется, тоже), с помощью JavaScript можно отправлять, к примеру, и сообщения в консоль браузера. Делается это с помощью кода <button onclick="console.log('Hello Console!')">Поприветствуй консоль!<button>. По нажатию на кнопку вы получите alert-сообщение в окне браузера и вывод сообщения в консоль.

*для справки: способы помещения кода JavaScript и взаимодействия его со страницей ничем не отличается от способов работы c CSS. У него точно так же есть способы inline – внутристрочный, internal – внутри HTML кода (документа), но отдельным блоком кода и external – создавая отдельный документ под JavaScript с расширением 'js'.
Одним из способов активировать код при загрузке страницы – это поместить его в HTML методом internal, код по умолчанию будет выполняться при загрузке страницы. К примеру, той же командой 'alert' из JavaScript можно уведомить пользователя о сборе файлов Cookies при входе на страницу.

 

Переменные в JavaScript

Как и в любом другом языке программирования в языке JavaScript есть переменные и типы данных. Есть несколько видов переменных: string, number, boolean, undefined и null.

Переменные являются неким логическим контейнером для хранения в них неких данных, к которым в дальнейшем можно обращаться. Переменные в языке JavaScript декларируются значением 'let' (от англ. пусть). К слову, есть и устаревшее значение var – variable (от англ. переменная), которое сейчас не используется. Далее переменной присваивается значение (имя). Выглядит это так: 'var firstName = "Vasya"'. То есть, грубо говоря, мы объявляем: пусть значение 'firstName' (то есть, в данном случае, имя человека) будет равняться (приравниваться) к значению 'Vasya', то есть, теперь мы ввели переменную 'firstName', которая равна 'Vasya'. Далее, после объявления переменной, мы можем на эту переменную сослаться, просто записывая в коде 'firstName'. Например, как и раньше, вывести в консоль. Выглядеть это будет следующим образом: 'console.log(firstName)', после чего мы получим выведенное в консоль ранее установленное значение переменной – 'Vasya'. Концепция переменных очень важна и позволяет значительно сократить код. Представьте себе самое длинное математическое выражение, с которым в дальнейшем коде вам нужно работать. Представьте, что оно еще и не одно. Так вот идея в том, вто вы можете поместить каждое из этих выражений в переменные и затем использовать их в виде одного короткого слова.

Значения переменных после присваивания им одних значений могут быть переписаны на другие значения. Для этого уже не нужно заново создавать "коробку" с переменной, а достаточно просто написать её ранее объявленное название и через знак приравнивания прописать ее новое значение, например: 'firstName = Petya;'. После объявления необходимых переменных их можно использовать в каких-то выражениях. Для этого объявим еще одну переменную – 'var lastName = "Ivanov";'. Теперь, после того, как у нас есть некоторый набор переменных, их можно, к примеру, сложить. Для этого объявим еще одну переменную – 'var fullName = firstName + lastName;'. Переменные в этом случае уже пишуться без кавычек! Как видно из кода, взаимодействие этих переменных осуществялется через операцию сложения. То есть, таким образом, мы получаем сумму двух переменных, в данном случае переменной 'firstName' и 'lastName', то есть имя и фамилию вместе, что в сумме представляет собой ранее объявленную переменную 'fullName'. К типу переменных, о которых сейчас была речь, относятся string, number.

Следующим типом переменных является тип boolean. Переменная типа boolean являет собой логический тип данных, который принимает только два значения: 'true' или 'false' – ложь или правда.

*для справки: логический тип данных 'boolean' точно так же объявляется уже описанным "контейнером" 'var', но после его объявялния ему придается значение 'true' или 'false'. Кроме того, логический тип данных 'boolean', в отличие от многих остальных языков программирования, можно так же как и любую другую переменную переписать в другое значение. То есть, если раньше это была логическая переменная со значениями 'true' и 'false', то после переписывания она легко может стать переменной 'string' типа.

Следующий тип переменных – undefined (от англ. неопределенный). Это переменная с неопределенным (не заданным) значением. То есть, если в обычных видах переменных мы указывали 'let *название_переменной* = *значение переменной*';, то в случае с 'undefined' у нас будет задаваться только имя переменной, без её значения – 'var *имя_переменной*';. В этом случае, результат обращения к этой переменной по имени будет рождать результат со значением 'undefined', что наглядно видно, например, в консоли браузера.

И последняя переменная – это 'null'. Это так же переменная с несуществующим значением, наподобие предыдущей. Сценарий использования этой переменной можно рассмотреть на примере игры. Предположим, что у нас есть некая компьютерная игра, где есть персонаж за которого играет некий человек по имени Петя. Объявим его: 'let player1234 = "Petya"'. После смерти игрового персонажа этого человека по одной из причин, его персонаж необходимо обнулить (удалить) из игрового процесса. В этом случае полезно применить переменную с нулевым значением – 'null', что делается как и прежде – 'player1234 = null;'.

*для справки: имена переменных всегда пишутся без пробелов. Для наилучшего понимания при программировании и последующего чтения кода, их желательно писать следующим образом: первое слово в имени переменной всегда пишется с маленькой буквы, второе, третье и, если необходимо, все последующие, с большой.

*для справки: так же еще одно немаловажное замечание по поводу имен переменных состоит в том, что имя любой объявленной переменной в JavaScript необходимо начинать с любой маленькой буквы латинского алфавита, символа нижнего подчеркивания или символа доллара. Другие символы запрещены и будут рождать ошибку при исполнении кода.

 

Встроенные методы

Что такое метод в языке программирования? Метод – это некий объем кода, состоящий из одной, нескольких или многих строк, помеченный каким-то именем, который в последствии можно вызывать (обращаться к нему). Это практически то же самое, что и переменная. Так же у метода существует такое понятие как "параметры". Параметр метода – это то значение, которое мы передаем внутрь метода для того, чтобы код в последствии мог к нему обращаться. Параметр метода пишется в круглых скобках.

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

 

Порядок операторов

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

 

Условные операторы. If/Else

Важнейшая концепция любого языка программирования – это условия. При помощи условий в языках программирования принимаются решения. Условный оператор записывается при помощи двух слов: 'if' (от англ. если) и 'else' (от англ. еще, иначе, в ином случае). Рассмотрим наглядно.

К примеру, объявим две переменные: let userName = "Ivan"; и 'let userAge = 15;'. Код дальнейшего условия записывается таким образом: 'if(userAge >= 18) { console.log(userName + " is adult."); }'. Если после этого начать выполнять код и открыть консоль, то мы не получим никакого результата. Не получим мы его потому что в этом случае утверждение ложно, так как Ивану меньше 18ти лет. Код выполняться не будет. Этот код можно продолжить (ранее объявленные переменные прописывать не будем): let userName = "Ivan"; и 'let userAge = 15;'. 'if(userAge >= 18) { console.log(userName + " is adult.";) }'.

'else if(userAge < 10) { console.log(userName + " is a child"); }'. Веток 'else if' может быть сколько угодно. То есть, каждый раз, после того, как мы прописали какие-то условия 'else' после 'if', мы всегда после этого можем добавить неограниченное количество 'else if', включающих в себя новые условия. Мы можем добавить еще одно условие (учитывая ранее написанный код): 'else if(userAge > 10 && userAge < 18)' { console.log(userName + " is a teenager"); }. В этом случае, мы добавили 'else if', где с помощью оператора 'и' добавляются сразу два условия.

*для справки: мы уже познакомились с новым типом оператора 'и', который обозначается как '&&'. Так же существует еще один немаловажный логический оператор 'или', он обозначается как '||'. В случае применения этого логического оператора, выражение считается истинным, если хотя бы одно из перечисленных между 'или' (||) выражений истинно.

 

Тернарный оператор

Тернарный оператор действует точно так же, как и оператор 'if else', но формат записи у него однострочный. То есть, мы можем записать оба условия и 'if' и 'else' в одной строке. Итак, вот как это работало с уже известными нам операторами 'else if'. Давайте объявим две переменных:

let userName = "Jack";

let userWeight = 87;

if(userWeight > 90) {

console.log("У " + userName + " лишний вес.");

} else {

console.log("У " + userName + " нормальный вес.");

}

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

userWeight > 90 ? console.log("У " + userWeight + " лишний вес.") : console.log("У " + userName + " нормальный вес.");

Эта структура расшифровывается следующим образом. Тернарный потому что состоит из трех частей: из изначального условия, из части, когда условие принимает значение 'true' и части (после двоеточия), когда условие принимает значение 'false'. Удобство этого оператора в том, что мы можем его использовать сразу при присваивании какой-либо значения переменной. Причем можно еще больше упростить написание кода, введя еще одну переменную и далее, прямо на месте, снова использовать тернарный оператор, без дополнительных строк вывода в консоль, еще элегантнее:

let weightDescription;

weightDescription = userWeight > 90 ? " лишний" : " нормальный";

console.log("У " + userName + weightDescription + " вес.")

 

Оператор Switch

Оператор 'switch' действует как механизм перехода к какой-либо ветке кода (switch от англ. переключатель).

Для работы оператора как обычно объявляется переменная (или несколько). Объявим переменную 'JavaScript'. Далее, записывается сам оператор 'switch', после которого в скобках указывается объявленная переменная. После (внутри) оператора 'switch' словом 'case' (от англ. случай) указываются ветки. После 'case' в кавычках записывается значение. После чего ставится двоеточие и далее мы можем записать то, что будет выполняться, если переменная 'section' будет равна указанному в кавычках значению. К примеру для первого случая выведем в консоль сообщение "Вы изучаете раздел HTML". Для второго случая выведем в консоль сообщение "Вы изучаете раздел CSS". Для третьего "Вы изучаете раздел JavaScript". Важно после каждого случая 'case' ставить ключевое слово 'break' (от англ. прервать). Нужно это для того, чтобы в тех случаях, когда в значении 'case' находится такое же значение, как и установленное в переменной, то выполнение кода завершается и программа выходит из оператора, не выполняя последующие ветки. Так же может быть и такой момент, что в переменной может быть указано значение, не совпадающее ни с одним из случаев внутри оператора 'switch'. В этом случае необходимо исполнить какой-то код по умолчанию Для этого после последнего блока 'case' создается 'default', после чего, так же, как и после 'case', ставится двоеточие и указывается то действие, которое будет выполняться. В нашем случае, тоже выведем сообщение в консоль. В этом случае 'break' не требуется, так как 'default' ставится последним.

Вот так будет выглядеть код:

let section = "javaScript";

switch(section) {

case "html" :

console.log("Вы изучаете раздел HTML");

break;

case "css" :

console.log("Вы изучаете раздел CSS");

break;

case "javaScript" :

console.log("Вы изучаете раздел JavaScript");

break;

default :

console.log("Вы изучаете какой-то раздел");

}

*для справки: в одном 'case' может находиться несколько значений переменных, для которых будут выполняться одни и те же действия. Прописывается точно так же, как и первоначальная переменная, в конце ставится двоеточие. К примеру, если после первой строки 'case' из кода выше, вместе со значением 'html' указать второй 'case', а после него 'html5', то в консоль будет выводиться один и тот же результат.

Теперь провернем всё то же самое, только уже с числами. Для примера обратимся к части уже ранее пройденного калькулятора лишнего веса. Снова объявим две переменные 'age' и 'groupNumber'. Но если мы в 'switch' поставим переменную 'age', то в 'case' мы будем проверять равенство какому-то числу, а не сравнивать число с интервалом чисел. Чтобы сделать это с оператором 'switch', нужно провернуть небольшой трюк. Для этого в качестве переменной (в скобках после оператора 'switch') напишем 'true'. После чего пишем 'case', а в 'case' уже прописываем условия. В этом случае будет сравниваться значение 'true' (истина) с приведенным внутри 'case' выражением. То есть, если 'age >= 18 && age <=25' – это 'true', то в этом случае по значение переменной 'switch(true)', то 'true' = 'true' и переменной 'groupNumber' присваивается значение '1'. Вот так будет выглядеть этот код:

let age = 20;

let groupNumber;

switch(true) {

case age >= 18 && age <=25 :

groupNumber = 1;

break;

case age >= 25 && age <=46 :

groupNumber = 2;

break;

default :

groupNumber = 3;

}

 

Цикл While

Циклы в языках программирования нужны для многократного повторения одного и того же действия. Для начала мы посмотрим на цикл 'while' (от англ. пока, до тех пор).

Цикл 'while' чем-то похож на оператор 'if', в котором так же в скобках указывается условие: 'while(условие)'. После чего открываются фигурные скобки и туда вписывается код, который должен выполнится несколько раз. Этот код будет выполняться до тех пор, пока условие прописанное в скобках истинно. Оператор 'while' работает таким образом: сначала проверяется на истинность условие, прописанное в скобках после оператора. Затем, если условие не верно, выполняется тело цикла, прописанное между фигурными скобками. Далее, после выполнения кода в теле цикла, оператор снова проверяет истинность условия в круглых скобках после себя и так до тех пор, пока условие не будет ложным. То есть, условия выхода из цикла – это выражение в круглых скобках равняется 'false'. К примеру, можно указать вывод изначальной переменной 'x = 1' в консоль. Однако если сделать это без прочих условий, то код впадет в бесконечный цикл, что может привести к зависанию браузера и/или компьютера. Поэтому мы можем инкриминировать нашу переменную, прописав к ней 'x++'. Код выглядит так (для начала объявим переменную):

let x = 1;

while(x < 100) {

console.log(x);

x++

}

Есть и другой способ применения цикла. Например, мы можем вывести в консоль какое-то текстовое сообщение по символам. Давайте добавим еще две переменных:

let helloString = "Привет, Аня!"

let count = 0;

while(count < helloString.length) {

console.log(helloString[count]);

count++;

}

*для справки: внутри цикла 'while' можно размещать оператор 'if', который так же как и обычно будет проверять некие условия и выполнять какие-то действия. Здесь представлено выведение в консоль всех чисел, кратных пяти.

let count = 1;

while(count <= 30) {

if(count % 5 === 0) {

console.log(сount);

}

count++

}

 

Цикл for

Цикл 'for' являет собой более удобный в записи и восприятии вариант цикла 'while'. Всё дело в том, что все данные для цикла (в том числе даже объявление переменной) записываются прямо в строке самого цикла 'for', в круглых скобках. На примере предыдущей операции рассмотрим принцип его работы:

for(count = 1; count <= 10; count++) {

console.log(count);

}

Для лучшего понимания, давайте и здесь, с помощью цикла 'for' устроим перебор символа строки. Теперь используем не 'count' переменную, а переменную 'i'. Переменная 'i' в реальной жизни в циклах 'for' используется практически постоянно. То есть, в цикле 'for' указывается одна буква для счетчика, так как эта переменная больше нигде не используется, ей не нужно присваивать имя, которое будет что-то описывать. Рассмотрим код:

let helloString = "Привет, Аня!"

for(i = 0; i < helloString.length; i++) {

console.log(helloString[i]);

}

*для справки: внутри цикла 'for' можно размещать оператор 'if', который так же как и обычно будет проверять некие условия и выполнять какие-то действия. Здесь представлено выведение в консоль всех чисел, кратных семи в диапазоне до 30.

for(i = 1; i <= 30; i++) {

if(i % 7 === 0) {

console.log(i);

}

}

 

Функции

Функции чем-то схожи с переменными. То есть, как говорилось выше, в переменную можно поместить какое-либо значение, а затем, в последующих сценариях работы программы, при запросе этой переменной, мы можем получить это значение. В функцияхможно хранить не значение, а целый код. То есть, функция – это как бы переменная (контейнер) для кода. Потом, соответственно, обратившись к функции программы по имени, в любом месте кода программы, мы можем запустить этот кусок кода.

Код организовывается следующим образом. Сначала пишется слово 'function', после которого всегда ставятся круглые скобки (в них могут помещаться аргументы функции). Затем, как и в предыдущих примерах, открываются фигурные скобки, в которых записывается само тело функции. То есть тот код, который будет выполняться, когда мы будем обращаться к этой функции в другом месте программы по её имени. Если записать функцию подобно циклам или операторам, то код работать не будет (функция – название функции – круглые скобки – фигурные скобки – тело функции (к примеру, вывод чего-либо в консоль)). Потому что в таком случае мы не обращаемся к функции, а просто определяем её. То есть мы создали функцию, прописали её внутренний код (тело), который эта функция должна выполнять, но мы не обращаемся к этой функции, чтобы она начала свою работу.

Чтобы запустить функцию (обратиться к ней), нужно просто прописать её имя, а сразу после обязательно поставить круглые скобки с точкой с запятой в конце.

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

function hello() {

console.log("Hello World!");

}

function();

Теперь функция полностью готова. Далее в коде, мы можем несколько раз обращаться к этой функции и код будет выполняться ровно столько раз, сколько мы к ней обратимся.

Вы можете подумать, зачем же всё так усложнять? Можно же просто несколько раз прописать 'console.log()' с нужными нам данными и тогда всё будет работать точно так же. Но не всё так просто. К примеру, если мы пропишем другую функцию:

function complexHello() {

console.log("Привет, меня зовут Глеб. Мне 24 года. Я из Ульяновска.");

console.log("До свидания!");

}

complexHello();

То есть, надеюсь, становится понятно, что чем длиннее и сложнее код, тем проще поместить его в некий "контейнер", в данном случае, в виде функции и выполнять его автоматизированно.

Функция может содержать множество строк самого различного кода. То есть, принцип чем-то похож на контейнер 'div' из HTML, который мы наполняем каким-то содержимым, с которым в последствии можно работать, обратившись к нему, например, из CSS.

 

Параметры (аргументы) функции

В предыдущей главе мы выводили через функцию в консоль некий текст приветствия. Вызывая эту функцию, мы будем выводить в консоль один и тот же текст. Но мы можем модифицировать эту функцию так, чтобы мы могли передавать текст приветствия в качестве параметра функции и в консоль уже выводился бы тот текст, который мы туда (в консоль) передадим. Для этого мы передадим функцию параметра или аргумента. Вернемся к нашему примера с функцией 'complexHello' и в данном случае, в качестве параметра подходит 'text'. Соответственно, далее в 'console.log()' мы указываем не какую-то определенную строку текста, а, соответственно, этот параметр – 'text'. И теперь, вместо этого параметра, мы можем передавать функции 'console.log' любой текст. Код будет выглядеть следующим образом:

function complexHello() {

console.log(text);

}

complexHello("Привет, меня зовут Глеб. Мне 24 года. Я из Ульяновска.");

В качестве параметра функции мы можем передавать не только текст. Например, мы можем создать функцию, вычисляющую квадрат какого-то числа. Для этого, как обычно, создадим функцию и присвоим ей параметр 'number'. После чего, поместим в тело этой функции 'console.log(number);', которая будет выводить в качестве результата эту функцию. А затем, мы можем эту функцию вызвать и передавать ей значение цифры, квадрат которой требуется получить. Код будет выглядеть следующим образом:

function square(number) {

console.log(number * number);

}

square(2);

Параметр, передаваемый в функцию, может быть не один. Например, давайте создадим другую функцию, вычисляющую площадь прямоугольника. Площадь прямоугольника равна ширине умноженной на высоту. Поэтому, параметры функции мы так и назовем: 'width' и (через запятую) 'height'. После чего, в теле функции, прописываем 'console.log(width * height)';. И далее мы можем вызывать эту функцию с различными параметрами сторон этого треугольника, например 2 и 3. Код будет выглядеть следующим образом:

function area(width, height) {

console.log("Площадь прямоугольника =" + width * height + " сантиметров квадратных");

}

area(2, 3);

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

Параметров у функции может быть любое количество, но в обычной жизни их как правило не бывает больше трех-четырех. На примере текста приветствия это снова можно представить таким образом:

function complexHello(helloText, name, age) {

console.log(helloText + " Меня зовут " + name + ". Мне " + age + " лет.");

}

complexHello("Здравствуйте!", "Иван", 46);

Этот код позволяет подставлять в функцию различные параметры в реальном времени. То есть, мы можем несколько раз обращаться к функции, меня её параметры и выводить, к примеру, различные приветствия.

В итоге думаю становится понятно, что аргументы функции – это всё те же, хоть и локальные, но концептуально переменные. Исходя из этого, параметры функции можно называть как угодно.

 

Возвращаемые значения функций

В качестве примера давайте рассмотрим уже изученную выше функцию по расчету площади прямоугольника. Эта функция, в качестве входного параметра, получает 'number', а затем умножает этот 'number' на себя же и выводит в консоль. То есть, функцию можно рассмотреть как некий черный ящик: мы передаем что-то в этот ящик, а затем функция внутри себя как-то эту переданную информацию обрабатывает. Но что если мы хотим использовать далее в программе результат того, что выполнила функция? В рассматриваемом примере – это квадрат значения 'number' – то есть, вычисление площади прямоугольника. Если мы пойдем по известному нам пути, попытаемся объявить переменную 'let x = square(2)' (square() от ранее рассмотренной функции – квадрат числа), и пропишем отображение в консоли. В этом случае, первая строка с переменной работать будет, то есть, будет вычисляться квадрат от числа 2, так как работает функция. Но вот 'console.log(x)' будет выводить значение 'undefined'. То есть, мы присвоили значение переменной 'x', но эта функция в данном случае не возвращает никакого значения – она просто проделывает какую-то работу. Но можно сделать и так, чтобы данные после работы функции выдавались как выходные. Делается это очень просто:

function square(number) {

return number * number;

}

После прописывания 'return' нам становится доступно присвоение результата работы функции какой-то другой переменной.

Есть еще один сценарий использования функции внутри другой функции. В данном случае функции 'square'. Создадим новую функцию, которая посредствам вычислений будет сообщать нам, большой ли квадрат. В качестве параметра бы будем использовать сторону квадрата 'side'. Внутри функции объявим переменную 'squareArea', которая посредствам функции будет заниматься вычислением площади квадрата – возведением стороны квадрата в квадрат. Поэтому эту переменную мы можем приравнять к уже объявленной функции, которая этим занимается – 'square' со значением 'side'. Ниже мы можем использовать оператор 'if', который будет выяснять: если площадь квадрата больше установленного нами значения, то квадрат большой. И если да, то эта функция будет возвращать 'true'. А в другом случае, соответственно, 'false'. А после выведем в консоль функцию, с параметром, к примеру "2": 'console.log(isSquareBig(2))'; Код будет выглядеть следующим образом:

function square(number) {

return number * number;

}

function isSquareBig(side) {

let squareArea = square(side);

if(squareArea > 100) {

return true;

} else {

return false;

}

}

console.log(isSquareBig(2));

 

Statements vs. Expressions

Есть два способа создания функции. Первый способ мы уже рассмотрели, он называется Statement или объявление функции. То есть, запись в форме 'function название_функции() {}'. Но есть второй способ – способ создания функции в выражении – Expression. Для наглядности давайте создадим реальную функцию и посмотрим, как её можно реализовать двумя способами. В функции мы будем передавать два параметра: название и вид животного и в зависимости от того, какое это животное, мы будем выводить в консоль имя этого животного и звук, который оно издает. То есть, если мы введем параметры "Имя 'Ричард'" и "Вид 'собака'", то в консоль должно выводиться "Ричард гавкает". Код в первом случае будет выглядеть следующим образом:

function animalVoice(animal, animalName) {

if(animal === "dog") {

return animalName + " гавкает.";

} else if(animal === "cat") {

return animalName + " мяукает.";

} else if(animal === "pig") {

return animalName + " хрюкает.";

} else {

return animalName + " издает какие-то звуки.";

}

}

console.log(animalVoice("dog", "Ричард"));

А вот второй способ – Expression:

let animalVoice = function(animal, animalName) {

if(animal === "dog") {

return animalName + " гавкает.";

} else if(animal === "cat") {

return animalName + " мяукает.";

} else if(animal === "pig") {

return animalName + " хрюкает.";

} else {

return animalName + " издает какие-то звуки.";

}

}

console.log(animalVoice("dog", "Ричард"));

Как видно из кода – работа функций в обоих способах абсолютно идентична. Единственное отличие, как вы могли заметить по первой строке, состоит в том, что при объявлении функции как переменной, эту функцию можно обнулить, если присвоить этой переменной другое значение. Это в дальнейшем функционально может пригодиться в программе.

 

Различные примеры реализации функций и рефакторинг

В первом примере мы рассмотрим, как создать функцию, вычисляющую, является ли число нечетным или нет. Если число нечетное, то в консоль выводится 'true', а если четное, то 'false'. Код будет выглядеть следующим образом:

function isNumberOdd(number) {

if(number % 2 === 0) {

return false;

} else {

return true;

}

}

*для справки: для лучшего понимания оператора '%', строка 'if(number % 2 === 0)' буквально означает, что "если, при делении на "2" переменной 'number' остаток равен "0", то...

*для справки: кроме того, у обращения к функциям есть два варианта. На примере консоли первый вариант будет выглядеть так: 'let x = isNumberOdd(11)';, а затем вывести в консоль, соответственно 'console.log(x);'. И второй вариант: 'console.log(isNumberOdd(11));'.

Еще бонусом можно было бы отметить, что в программировании существует такое понятие как 'рефакторинг'. Сразу написать идеальный код бывает довольно сложно, особенно если код сам по себе тоже сложен, поэтому написанный код часто можно многократно улучшить, оптимизировав его. Это, разумеется, будет давать прирост в скорости его работы, но так же и положительно сказываться на его читаемости. На примере ранее описанной функции 'isNumberOdd', можно показать, насколько сильно можно укоротить код:

function isNumberOdd(number) {

return % 2 !== 0;

}

То есть, сначала будет вычисляться значение логического выражения после 'return' и если остаток от деления % 2 НЕ равен нулю (неравенство обозначается оператором '!=='), то будет возвращаться 'true' и на этом работа функции завершается. Быстро и элегантно.

Во втором примере мы посмотрим на функцию, вычисляющую факториал числа:

function factorial(naturalNumber) {

if(naturalNumber < 0) {

return 0;

}

let result = 1;

for(i = 1; i <= naturalNumber; i++) {

result = result * i;

}

return result;

}

А теперь, смотря на код выше, давайте разберемся, как работает эта функция.

В начале проверяется, ввел ли пользователь корректное значение. То есть, ввел ли пользователь натуральное число, чтобы факториал сработал. Например, мы можем договориться, что если пользователь ввел отрицательное значение, то мы возвращаем из функции значение 0. Так как сам факториал не может возвращать 0, следовательно, возвращенный 0 будет сигнализировать о том, что пользователь ввел некорректное значение.

Далее к самому факториалу. Сначала 'i = 1', точно так же, как и объявленная переменная 'result = 1'. То есть, исходя из логики работы цикла, переменной 'result' будет присвоено значение 1 * 1, что будет равняться единице.

Если передать в параметр функции значение, к примеру, 3, то 'i < 3', следовательно, запускается цикл. Так как из-за 'i++' цикл продолжается, следовательно, i = 2. В 'result' находится 1, 1 * 2 = 2, после чего 'result' присваивается 2. Далее снова идет проверка в цикле. i всё еще меньше заданного значения функции 3, поэтому снова срабатывает 'i++' и теперь i = 3. В 'result' всё еще находится 22 * 3 = 6. Число 6 присваивается 'result', после чего снова срабатывает i++ и i теперь равна 4 и в этом случае мы выходим из цикла, так как 4 > 3, где 3 – установленное значение функции. И в самом конце мы возвращаем 'result'.

И в последнем, третьем примере мы рассмотрим, как с помощью функции JavaScript можно автоматически менять текстовые символы. Для примера возьмем функцию, которая будет заменять все пробелы на символы нижнего подчеркивания – _.

function changeSpaceToUnderscore(text) {

let resultText = text.replace(/ /g, "_");

return resultText;

}

Здесь всё довольно просто. Мы создаем функцию с параметром 'text'. Затем, внутри этой функции, мы объявляем переменную 'resultText', которая равна параметру объявленной функции 'text', но с методом 'replace()', который в JavaScript и занимается заменой символов. После метода 'replace' в скобках между двумя слешами указывается то, что мы хотим заменить, после чего обязательно ставится символ 'g' и после запятой в кавычках указывается то, НА ЧТО мы хотим заменить.

 

Область видимости переменных

В JavaScript есть два типа областей видимости переменных:

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

К примеру, переменные определённые внутри некой функции не будут доступны за её пределами. Но это не работает для переменных, определенных снаружи относительно тела функции. В этом случае мы не только можем обращаться к внешним переменным, но и менять их значения. Такие переменные, декларированные за пределами каких-либо структур, называются глобальными. Внутри же функции предпочтение всегда отдается локальным одноименным переменным.

*для справки: именно из-за понятия области видимости для циклов 'for' и подобных структур, условия рекомендовано прописывать в формате переменной, как, например, 'for(let i = 1; i < 10; i++) {}'. Всё потому что в случае, если объявить в цикле 'i' без ключевого слова 'let', то в коде будет создаваться переменная глобального типа. Это, как можно понять, может серьезно повлиять на ход выполнения программы, если этой переменной случайно присвоится какое-то иное значение.

 

Массивы

Массивы – это одна из структур данных, которые существуют в языке JavaScript. Итак, что же это такое?

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

let color1 = "красный";

let color2 = "оранжевый";

let color3 = "желтый";

let color4 = "зеленый";

let color5 = "голубой";

let color6 = "синий";

let color7 = "фиолетовый";

Итак, у нас есть семь цветов радуги – семь переменных. Но проблема в том, что эти переменные никак не связаны друг с другом. То есть, мы никак не отображаем, что все эти семь цветов относятся к цветам радуги. И именно в таком случае, когда нам нужно собрать какие-то данные в единую структуру, как и в случае с цветами радуги, нам и приходят на помощь массивы. Массивы данных создаются следующим образом:

let rainbowColors = ["красный", "оранжевый", "желтый", "зеленый", "голубой", "синий", "фиолетовый"];

И теперь все цвета собраны воедино и мы можем обращаться к этим значениям по одному имени – по имени rainbowColors.

Обращаться затем к этому массиву данных можно следующим образом (для примера возьмем консоль): consol.log(rainbowColors[2, 3]);. В квадратных скобках указывается индекс элемента в массиве, то есть, его порядковый номер. Индексируются массивы начиная с нуля. Для примера этот вывод в консоль можно скомбинировать еще и со строкой: 'console.log("Небо " + [4] + " цвета");'.

Можно не только считывать данные из элемента массива, но и менять значения этих элементов. То есть, к примеру, если мы хотим изменить цвет "красный" на "оранжевый", то мы можем сделать следующее: 'rainbowColors[0] = "оранжевый";'.

Ровно так же можно вывести в консоль (или куда-либо еще) все элементы массива при помощи указания его имени: 'console.log(rainbowColors);'.

Так же можно не только изменять какие-то элементы в массиве, но и добавлять в него новые. Но что делать, если мы не знаем, что у нас только семь элементов в массиве, а высчитать из них последний было бы затруднительно? Сделать это можно указать свойство массива '.length'. Это свойство равно количеству элементов, а именно: 'rainbowColors[rainbowColors.length] = "темно-синий";'. Есть и другие способы создания массивов.

Второй способ создать массив выглядит следующим образом: 'let emptyArray = [];'. При такой записи создается пустой массив, в которые затем можно поместить значения.

Еще одна, не слишком распространенная форма создания массива – это: 'let emptyArray = new Array();' – осуществляется, как можно видеть, с помощью обращения к функции 'array'. Повторюсь, такой способ не распространен.

Конечно же, массивы могут хранить не только строки, они могут хранить в себе любые значения. Можно так же создать массив с числами: 'let numbers = [1, 5, 3, 2];'. Более того, JavaScript позволяет добавлять в массив значения разных типов: 'let anyItems = [32, "Hello!", null]';.

 

Методы массивов

У массивов спаренные методы, такие как 'push/pop', 'shift/unshift', и есть одиночные indexOf, 'slice'. Это основные методы для работы с массивами. По ссылке можно перейти на сайт W3Schools и подробнее прочесть о самых разных методах для работы с массивами данных – их существует очень много. Но в основном используются те методы, речь о которых пойдет далее.

Начнем с метода Push. Создадим массив: 'let names = ["Вася", "Саша", "Игорь", "Олег"];'. Далее, если мы хотим изменить массив, а именно, добавить в него какой-то элемент, мы можем воспользоваться либо технологией, описанной выше, либо прибегнуть к методу Push. Выглядит это следующим образом (воспроизведем это как полноценный код):

let names = "Вася", "Саша", "Игорь", "Олег"];

names.push("Вячеслав");

После чего, с помощью такого метода записи, в массив (в его конец) будет добавлен новый элемент.

Кроме того, метод 'push' может возвращать значения. Вообще, метод – это, своего рода, функция. И мы можем извлечь из нее значение. Это можно реализовать следующим образом:

let names = "Вася", "Саша", "Игорь", "Олег"];

let x = names.push("Вячеслав");

console.log(names);

console.log(x);

При этом console.log();' в виде переменной 'x' будет выводить число, означающее длину массива.

Противоположностью метода push является метод pop. Метод pop удаляет последний элемент в массиве. Он так же способен возвращать значения, но он возвращает не количество элементов, а значение того элемента, который он удалил, то есть последнего. Код для удаления из массива последнего элемента выглядит так: 'names.pop();'.

Следующая пара методов – shift и unshift. По той же самой логике, что и предыдущие два массива, они используются для работы с элементами в начале массива. Метод unshift добавляет элемент, метод shift удаляет. Следующий метод – indexOF. Он нужен для определения индекса элемента массива. Записывается следующим образом: 'names.indexOf("Игорь");'. Как можно заметить, в качестве параметра этот метод принимает в себя конкретный элемент массива, а после чего возвращает его индекс. Если мы укажем какой-то элемент, которого нет в массиве, то будет возвращено значение -1. То есть, если нужно определить, существует ли введенный элемент в массиве, то это можно легко сделать при помощи этого метода. Есть еще один момент. Если в массиве присутствует два одинаковых элемента, то будет возвращено значение 0. Мы можем использовать метод indexOf, к примеру, в следующий логике:

let toyota = ["Camry", 2018, "sedan", "black", true];

let isSedan = toyota.indexOf("sedan") === -1 ? console.log("Тип кузова - не седан") : console.log("Тип кузова - седан");

В этом случае метод indexOf проверяет в массиве, есть ли там характеристика автомобиля – седан. Если же такого элемента массива нет, то возвращается значение -1, что исходя из работы тернарного оператора, интерпретируется как false и срабатывает описанный вывод в консоль, что машина не является седаном.

Последний метод, который мы рассмотрим – это метод slice. Метод slice позволяет скопировать часть какого-то массива и присвоить её другой переменной, то есть создать другой массив из уже существующего. Например, let cars = ["Honda", "Toyota", "Peugeot", "Opel", "Mersedes", "BMW"];. И к примеру, мы хотим выбрать из этого массива только немецкие марки автомобилей, то есть, Opel, Mersedes и BMW. Для этого мы можем создать новый массив: 'let germanCars = cars.slice(3, 5)';. В параметре метода в круглых скобках указываются индексы элементов, с которого мы начинаем "резать" массив, чтобы забрать его кусок и второй индекс, которым мы заканчиваем.

 

Объекты

В JavaScript есть еще одна структура для хранения данных – это объекты. Пример массивов из предыдущей главы может хранить как данные одинакового типа (строки), так и данные разных типов (строки, числа, boolean, undefined, null). Этот способ хранения данных, по большому счету, валиден (корректен), однако не слишком удобен: чтобы получить доступ к какому-либо элементу из массива, нам нужно точно знать, на какой позиции расположено то или иное свойство. Поэтому для множества типов данных, которые принадлежат к какому-то объекту, в JavaScript и существует тип хранения данных объекты. В коде объекты записываются следующим образом 'let carToyota = {}';. В фигурных скобках перечисляются свойства объекта. Но они будут сохраняться не по их индексу, а по так называемому ключу (строке). Соответственно, в объекте есть ключ и его значение, по которому считывается информация. Более развернуто посмотрим на код:

let carToyota = {

model: "Camry",

year: "2010",

carBody: "sedan",

color: "black",

hasAirbag: true

}

Далее мы можем обращаться к элементу объекта при помощи похожей записи, что мы рассматривали выше, только в этот раз используется не индекс, а название свойства обекта. На примере консоли: 'console.log(carToyota["year"]);'. Но для обращения к свойству объекта не обязательно используется метод обращения, как к массиву – для этого есть специальная запись: 'carToyota.year';. Это обращение к объекту по его ключу. То есть теперь нам не важно знать, на каком месте расположено то или иное свойство. Мы даже можем менять их местами – на результат это никак не повлияет. Есть небольшое отличие между двумя типами записей. В первом случае, когда элемент указывается в квадратных скобках, мы можем использовать переменную. Например, если мы создадим переменную с каким-либо свойством из массива, а затем обратимся к ней, то всё сработает корректно:

let x = "Camry";

console.log(carToyota[x]);

Но если мы обратимся к 'x' в формате записи 'console.log(carToyota.x)', то в результате мы получим значение 'undefined'. В этом случае будет разыскиваться свойство с названием 'x', а не назначенная переменная.

В объектах точно так же можно менять одни свойства на другие. Например, цвет: 'carToyota.color = "red";.

Как и массив, объект точно так же можно вызвать. Например, в консоль: 'console.log(carToyota);'.

Дляобъектов так же существует не один способ их создания. В первом способе, который мы рассмотрели выше, мы сразу прописываем все поля. Но есть второй способ:

let carMazda = {};

carMazda.year = 2018;

carMazda.model = "CX7";

carMazda.carBody = "crossover";

И еще один способ, как и при создании массивов – при помощи функции. Код выглядит следующим образом:

let carOpel = new Object();

carOpel.body = "Hatchback";

carOpel.year = 2018;

carOpel.color = "blue";

 

Массивы или объекты

Так что же выбрать? Какие данные лучше отображать в массивах, а какие в объектах?

В массивах лучше хранить данные одного вида (типа), то есть однообразные данные. Конечно, не воспрещается хранить в массивах и разнообразные данные, однако способ массивов для этих целей не представляется удобным. К примеру, массив с различными названиями цветов, где, конечно же, будут только цвета и ничего больше: let colors = ["red", "orange", "yellow", "white", "blue"];. Если же мы хотим сохранять данные разных типов (как в выше описанном примере с автомобилем), то лучше использовать способ объектов. Как например, информация о человеке:

let personIvan = {

firstName: "Иван",

lastName: "Иванов",

age: 24,

isMarried: false,

}

Но кроме того, и массивы и объекты могут хранить в себе другие массивы и/или объекты. Например, массив 'numbers' (числа), может хранить в себе какие-то массивы с какими-то числами: 'let numbers = [[1,2,3], [4,5,6], [7,8,9,10]];'. А далее, чтобы обратиться к этому массиву, к примеру, через консоль, нужно сначала указать индекс массива в массиве, а затем индекс самого элемента в выбранном массиве: 'condole.log(numbers[1][2]);'.

Объекты с массивами можно конфигурировать по-разному. К примеру, объекты могут содержать в себе массивы. Вернемся к нашему примеру с 'personIvan':

let personIvan = {

firstName: "Иван",

lastName: "Иванов",

age: 24,

isMarried: false,

pets: ["cat", "dog", "humster"]

}

А чтобы обратиться к одному из элементов массива, нужно: 'console.log(personIvan.pets[1]);'.

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

let sellers = [

{

firstName: "Иван",

firstName: "Иванов",

regDate: "09.08.2019",

hasDiscount: false,

age: 25

},

{

firstName: "Gleb",

lastName: "Glebov",

regDate: "01.01.2010",

hasDiscount: true,

age: 34

}

]

То есть, как можно заметить, у нас есть массив и внутри этого массива есть два объекта (можно создавать сколько угодно объектов). Как же нам обратиться к полям этих объектов? Есть два способа. Мы можем обратиться ко всему объекту, к примеру, в консоли: 'console.log(sellers[1]);'. Или мы можем обратиться только к какой-то конкретной строке: 'console.log(sellers[1].hasDiscount);'.

В реальной жизни мы можем создавать базы данных, а затем обращаться к конкретным элементам в них. Например, создать базу данных об автомобилях в автосалоне, а затем вывести только те автомобили, которые были проданы. Для этого, собственно, создадим базу данных. А затем создадим цикл, где будет высчитываться длина выводимой информации. Выглядеть это будет следующим образом:

let cars = [

{

carProd: "Mersedes",

carModel: "SL900",

color: "black",

carYear: 2020,

isSelled: true

},

{

carProd: "Toyota",

carModel: "Camry",

color: "blue",

carYear: 2019,

isSelled: true

},

{

carProd: "BMW",

carModel: "X7",

color: "white",

carYear: 2021,

isSelled: false

},

{

carProd: "Audi",

carModel: "A8L",

color: "black",

carYear: 2022,

isSelled: false

}

];

for(let i = 0; i < cars.length; i++) {

if(cars[i].isSelled === false) {

console.log(cars[i]);

}

}

Для работы с массивами можно использовать не только цикл for. Так же можно использовать цикл forEach (от англ. для каждого), код выглядит следующим образом:

cars.forEach(function(car) {

if(car.isSelled === false) {

console.log(car);

}

}

 

Методы объектов

Внутри объектов можно прописывать функции, выполняющие те или иные действия. Например, вычисление скидки в зависимости от длительности регистрации пользователя. Рассмотрим это используя вышеприведенный пример с продавцами автомобилей. Если пользователь зарегистрирован на сайте меньше двух лет, то он не имеет никакой скидки. Если от двух до пяти лет, то у него есть скидка 20%, а если больше пяти, то 30%. И далее вызвать метод этого объекта в консоль. Код будет выглядеть следующим образом:

let carSeller1 = {

firstName: "Игорь",

lastName: "Курушин",

regYear: 2017,

hasDiscount: true,

discountCalculation: function(year) {

let numberOfYears = 2022 - year;

if(numberOfYears <= 2) {

discount = 0;

} else if(numberOfYears > 2 && numberOfYears <= 5) {

discount = 20;

} else if(numberOfYears > 5) {

discount = 30;

}

return discount;

}

}

console.log(carSeller1.discountCalculation(2019));

 

'this' в методе объекта

Как можно заметить, в выше рассмотренном методе функции мы используем значение, которое можно получить из самого объекта. В данном примере внутриобъектной функции, мы используем параметр 'year', то есть год регистрации пользователя. Но так как в самом объекте уже есть свойство с годом регистрации, то нам необязательно использовать для функции параметр 'year'. Мы можем обратиться непосредственно к свойству 'regYear' внутри объекта через функцию. К этому свойству объекта внутри функции мы можем обратиться используя ключевое слово 'this'. Выглядеть в этом случае и объект, и функция будут почти полностью идентично, поэтому не будем переписывать код рассмотренный выше. Отметим только, что теперь параметр функции указываться не будет – скобки после слова 'function' будут оставаться пустыми, а объявленная нами переменная 'numberOfYears' будет теперь выглядеть так: 'let numberOfYears = this.regYear';. Теперь, чтобы получить результат выполнения функции, не нужно искусственно прописывать в коде вызова консоли параметр функции – функция будет брать данные для своей работы непосредственно из свойства объекта, то есть, из года регистрации пользователя.

Но мы можем пойти и еще дальше. Например, если выше описанный объект имеет свойство discount, в котором эта скидка указывается, например, по умолчанию скидка будет равна 0. А далее в коде мы можем объявить переменную 'discount' и присвоить ей значение выше описанной функции с методом 'this' и далее мы можем переменной 'carSeller1.discount' присвоить значение переменной 'discount'. То есть, мы присваиваем полю discount в объекте, значение, которое мы вычисляем с помощью выше описанной функции:

let discount = carSeller1.discountCalculation();

carSeller1.discount = discount;

Но можно записать еще короче и сделать так, чтобы код обращался к объекту (carSeller1), затем к его свойству (discount) и отождествлял его с объектом (carSeller1), затем функцией (discountCalculation();). А затем вывести в консоль весь объект. В этом случае, будет выведен весь объект, а в нём автоматически прописываться вычисленный функцией процент скидки, в зависимости от года регистрации на сайте:

carSeller1.discount = carSeller1.discountCalculation();

Но можно пойти еще дальше по пути оптимизации. Для этого мы можем больше не использовать 'return' в функции, а обратиться к полю объекта discount. Сделать это можно с помощью 'this.discount = discount;'. То есть, мы присваиваем свойству значения discount присваиваем значение внутренней переменной 'discount'. Теперь мы можем не присваивать полю ничего дополнительного – весь функционал будет осуществляться внутри объекта. Присвоение свойству discount нового значения при помощи свойства regYear. Итак, весь код для справки будет выглядеть так, для лучшего понимания, снова запишем:

let carSeller1 = {

firstName: "Игорь",

lastName: "Курушин",

regYear: 2010,

discount: 0,

calculateDiscount: function() {

let discount;

let numberOfYears = 2022 - this.regYear;

if(numberOfYears <= 2) {

discount = 0;

} else if(numberOfYears > 2 && numberOfYears <= 5) {

discount = 20;

} else if(numberOfYears > 5) {

discount = 30;

}

this.discount = discount;

}

}

carSeller1.calculateDiscount();

console.log(carSeller1);

 

DOM

Как можно понять, JavaScript нужен, конечно, совсем не для того, чтобы выполнять какие-то игрушечные абстрактные задачи вроде выведения в консоль вычисления площади прямоугольника или содержимого массивов. JavaScript нужен для взаимодействия, а главное – управления HTML и CSS содержимым на веб-странице. Делается это с помощью понятия DOM, что означает Document Object Model (от англ. Объектная Модель Документа).

 

DOM. Селекторы

Для доступа к различным HTML-элементам с помощью стандарта DOM точно так же, как и в случае с CSS используются селекторы. Существует несколько видов селекторов:

Начнем с первого селектора – 'document.getElementById();'. С помощью этого селектора, к примеру, можно заполнить любой тег на странице, которых до этого был пустым, допустим, параграф, где написано - "Привет, Мир!". Поскольку, как можно заметить, если попытаться развернуть и перевести название селектора (это будет "Получить элемент по ID"), то этому параграфу нужно своевременно (до выполнения кода), присвоить ID, по которому этот код будет выполняться. Выглядеть код будет так:
'document.getElementById("paragraph").innerHTML = "Привет, Мир!";'.



Следующий селектор 'document.getElementByClassName();' – находит все элементы по имени указанного класса. То есть, мы можем присвоить HTML-элементам какие-то классы, а затем этот селектор способен возвращать их для дальнейшей с ними работы, например, изменения/добавления контента. Проще говоря, этот селектор занимается примерно тем же, что и классы в HTML – собирает в себя другие элементы, объединяя в группу. Принцип его работы состоит только в том, чтобы собрать по HTML-коду все элементы с прописанным (указанным) в селекторе классом, а затем возвратить (передать) эту информацию для дальнейшей работы кому-нибудь другому. Схема применения может выглядеть следующим образом:

<!DOCTYPE html>

<html lang="ru">

<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<title>DOM</title>

</head>

<body>

<h2>Поиск HTML элементов по имени класса</h2>

<p>Привет, Мир!</p>

<p class="intro">DOM очень полезен.</p>

<p class="intro">Этот пример демонстрирует метод getElementByClassName.</p>

<p id="demo"></p>

<script>

let x = document.getElementByClassName("intro");

document.getElementById("demo").innerHTML = 'Первый параграф (индекс 0) с классом="intro": ' + x[0].innerHTML;

</script>

</body>

</html>

*для справки: метод '.innerHTML' отвечает за отображение выбранного HTML-элемента. С помощью него, к примеру, можно обратиться, то есть прочесть и/или вывести контент любого тега и/или заменить его.

Принцип работы этого кода довольно прост. В HTML мы создаем два параграфа заполненные текстом с классом 'intro' и один пустой параграф с id 'demo'. Далее мы объявляем переменную 'x' и присваиваем ей значение в виде 'document.getElementByClassName("intro");', то есть переменная 'x' теперь представляет тот контент, который входит в класс 'intro'. Как и все прочие перечисления индексов элементов в JS, селекторы при работе с DOM не стали исключением и тоже отсчитываются от ноля. Далее мы вставляем в пустой параграф с id 'demo' текст 'Первый параграф (индекс 0) с классом="intro": ' с помощью 'document.getElementById("demo")' и приплюсовываем ко всему этому переменную 'x[0]' с индексом 0, так как у нас два параграфа с классом 'intro', а исчисляются индексы, как говорилось выше, начиная от ноля, а затем вставляем всё это в тот же пустой параграф.

Выглядит запутано, но глядя на код выше и вчитываясь в это запутанную инструкцию (пусть даже придется прочесть не один раз), всё непременно будет ясно.



Следующий селектор в нашем списке – это document.getElementByTagName();. Этот селектор по принципу своего действия очень похож на предыдущий, но с одним, как можно догадаться по названию, отличием: он находит в документе HTML-теги. Можно сказать, что у этого и предыдущего селектора буквально один и тот же принцип действия, только в этот раз мы бы подставляли не класс, а название тега: параграф, заголовок и/или другие.



Следующий селектор выбора элементов – 'document.querySelector();'. Этот селектор работает уже не с HTML-тегами, а с селекторами CSS. Как говорилось выше, JS позволяет работать (манипулировать/взаимодействовать) не только с HTML, но и CSS. К примеру, если у нас есть заголовок в HTML с id 'header', то с помощью этого селектора мы можем обратиться к нему точно так же, как в CSS, то есть: 'document.querySelector("#header");'. Это актуально и в случае обращения к классу. Важно отметить, что при выборе селектором 'querySelector' класса будут выбираться не все элементы с соответствующими классами, а только первое совпадение.

Чтобы выбрать все элементы с одинаковыми нужными нам классами или тегами, существует вариация селектора 'querySelector''querySelectorAll'.

DOM. Изменение контента

Рассмотрим, как можно манипулировать полученными с помощью селекторов объектами.

Как уже говорилось выше, есть способ обратиться к содержимому HTML-элемента. Это делается с помощью свойства '.innerHTML'. Но есть и второй способ – это свойство '.textContent'. В чем же разница? Разница в том, что свойство '.innerHTML' занимается не только отображением "сухого" текста, то есть, буквально содержимого тега, а вообще всего, что касается этого тега. То есть, если рассмотреть принцип его работы на примере, допустим, тега 'ul' – неупорядоченного списка – то как результат работы свойства '.innerHTML' (его возвращения), мы получим всю структуру тега 'ul' с его дочерними элементами, текстом и всем прочим (к примеру, тегом strong). Свойство '.textContent' же занимается буквальным отображением текстового содержимого, где не будет ничего, кроме текста, который содержит тег, к которому совершено обращение. По этой причине, при изменении текста с помощью свойства '.innerHTML' нужно указывать не только текст, но и сам тег, текст внутри которого требуется изменить.

 

DOM. Изменение стилей

Изменение стилей с помощью DOM и JS провернуть довольно просто. Допустим, в HTML документе у нас есть заголовок, у этого заголовка есть id="header". Чтобы изменить его стиль с помощью JS, код будет выглядеть следующим образом (для удобства запишем функцию с методом (селектором) как переменную):

let h1 = document.getElementById("header");

h1.style.color = "red";

Этим кодом мы через JS обратились по id к заголовку и стилизовали его, сделав его цвет красным.

Кроме того, исходя из концепции DOM, JS позволяет добавлять не существовавшие ранее классы. Для этого существует метод '.classList.add("название_нового_класса");'. У добавления/создания чего-либо, конечно, есть и обратное действие, для этого есть метод'.classList.remove("название_удаляемого_класса");'. Еще один метод, который часто используют для добавления/удаления классов – это '.classList.toggle("название_класса");'. Этот метод работает как некий переключатель – при первом срабатывании он "включает" новый класс, создавая его, при втором, соответственно, "выключает", удаляя.

*для справки: разумеется, используя все перечисленные методы для добавления и/или удаления классов, нужно использовать селекторы обращения к элементам. То есть, используя один из представленных выше методов, полная конструкция будет выглядеть так: 'document.getElementById("некий_id").classList.toggle("некий_класс");'.

DOM. Изменение атрибутов

Концепцию манипуляции с атрибутами было бы удобно рассмотреть с точки зрения такого явления в веб-разработке, как "Image Gallery" – галереи изображений. То есть, некой структуры (блока) на сайте, который включает в себя серию (набор) неких изображений, относящихся к тематике сайта. Эти изображения по нажатию, например, на некую кнопку перелистывания, требуется каким-то образом менять. Для этого и применяется методы изменения атрибутов.

По кнопке можно перейти на страницу, где можно увидеть готовую галерею изображений, а если открыть код страницы, то можно увидеть посредствам какого функционала она реализована.

Итак, для начала нам нужно добавить в HTML разметку наши изображения:

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

let cats = document.querySelector("#cats");

cats[0].getAttribute("src");

cats[0].setAttribute("src", "img/cat2.jpeg");

Как видно из кода, с помощью объявленной переменной 'cats' и индекса (порядкового номера) "0" мы обращаемся к атрибуту 'src' первого изображения и получаем его. Затем мы меняем значение этого атрибута с помощью метода '.setAttribute', у которого используется не только значение 'src', но и то значение, которое требуется заменить. В данном случае, это путь к новому изображению – 'img/cat2.jpeg'.
Точно так же, мы можем менять и любой другой атрибут, к примеру атрибут ссылки – тега 'a':

let link = document.querySelector("#link");

link[0].setAttribute("href", "http://ya.ru");

В этом случае, если у нас есть некая ссылка с ID "link", у которой уже будет прописан некий URL, то он будет заменен на тот, что мы прописали, то есть на ya.ru.

 

DOM. Events

Что такое "events"? Events в переводе с английского – события. То есть, мы можем изменять объекты DOM структуры с помощью JS по каким-то событиям. Схема организации реакции на какие-либо события выглядит следующим образом. Для начала нам, конечно, нужно выбрать какой-то элемент, с которым мы и будем работать. Затем, нужно добавить так называемого Event Listener – прослушивателя события. Он через точку записывается после обращения к элементу, а после него в круглых скобках и кавычках пишется, непосредственно событие. Самое распространенное, к примеру 'click'. Вторым элементом указывается функция, в которой мы пишем код, который будет запускаться, когда будет кликнут выбранный нами элемент. Ну а дальше всё как обычно из принципа работы функции. К примеру, изменим цвет заголовка на красный. Рассмотрим это на практике. Допустим, у нас есть элемент заголовка – h1 с id "test". Код JS будет выглядеть следующим образом:

let h1 = document.getElementById("test");

h1.addEventListener("click", function() {

this.style.color = (this.style.color == 'red') ? 'rgb(232, 230, 230)' : 'red';

});

Тестовый заголовок, меняющий цвет по нажатию!

Для одного элемента мы можем устанавливать несколько Event Listener.

Точно такой же функционал, как и со сменой цвета заголовка из примера выше, мы можем цеплять и, к примеру, к кнопкам. Для наглядности и простоты, кнопка тоже будет менять цвет элемента. По нажатию она будет менять цвет фона всей страницы! А код будет выглядеть следующим образом (HTML-код мы поместим в синюю рамку, а JS-код в красную:

<button class="button" id="button_test">Сменить цвет!</button>

this.style.transition = '.5s ease-in-out';

let button = document.getElementById("button_test");

let container = document.getElementById("container");

let body = document.getElementById('body');

let isButtonApplied = false;

button.addEventListener("click", function() {

if(isButtonApplied === false) {

container.style.background = "black";

container.style.transition = 'background 2.5s ease-in-out';

button.style.boxShadow = "0 0 10px rgba(255, 255, 255, 1)";

body.style.transition = "color 3.5s ease-in-out";

body.style.color = "red";

setTimeout(() => {body.style.background = 'rgba(0, 0, 0, .9)', body.style.transition = 'background 1s ease-in-out';}, 2500);

isButtonApplied = true;

} else if (isButtonApplied === true) {

container.style.background = "linear-gradient(90deg, rgba(224, 0, 255, 0.5) -15%, rgba(179, 90, 195, 0.5) 48%, rgba(20, 138, 227, 0.5) 100%)";

container.style.transition = 'background 2.5s ease-in-out';

button.style.boxShadow = "0 0 15px rgba(0, 0, 0, 1)";

body.style.color = "rgba(232, 230, 230, 1)";

body.style.transition = "color 3.5s ease-in-out";

setTimeout(() => {body.style.transition = 'background 1s ease-in-out', body.style.background = 'linear-gradient(90deg, rgba(59, 9, 121, 1) -40%, rgba(147, 0, 255, 1) 48%, rgba(207, 0, 255, 0.5) 100%)';}, 2500);

isButtonApplied = false;

}

});

 

Финал JS. Игра на реакцию

Внимательно смотрите за кодом, как и обычно HTML-код мы разместим внутри синей рамки, JS-код в красной, а CSS будет в зеленой:

<div id="shape"></div>

<h3>Время реакции <span id="reactionTime"></span></h3>

#shape {

width: 150px;

height: 150px;

background-color: green;

display: none;

position: relative;

}

let startTime = new Date().getTime();

function getRandomColor() {

let letters = "0123456789ABCDEF".split('');

let color = "#";

for(let i = 0; i < 6; i++) {

color += letters[Math.floor(Math.random() * 16)];

}

return color;

let makeShapeVisible = function() {

let shape = document.getElementById("shape");

let top = Math.random() * 400;

let left = Math.random() * 700;

let width = Math.random() * 200 + 50;

shape.style.top = top + "px";

shape.style.left = left + "px";

shape.style.width = width + "px";

if(Math.random() > 0.3) {

shape.style.borderRadius = "50%";

shape.style.borderBottom = "0";

shape.style.backgroundColor = getRandomColor();

} else if(Math.random() >= 0.3 && Math.random() <= 0.7) {

shape.style.borderRadius = "0";

shape.style.backgroundColor = getRandomColor();

shape.style.borderRadius = "0";

} else if(Math.random() > 0.7) {

shape.style.left = "0";

shape.style.right = "0";

shape.style.borderLeft = "50px solid transparent";

shape.style.borderReft = "50px solid transparent";

shape.style.borderBottom = "100px solid " + getRandomColor();

shape.style.backgroundColor = "transparent";

}

shape.style.display = "block";

startTime = new Date().getTime();

}

setTimeout(makeShapeVisible, Math.random() * 1000);

document.getElementById("shape").onclick = function() {

let shape = document.getElementById("shape");

shape.style.display = "none";

let finishTime = new Date().getTime();

let reactionTime = (finishTime - startTime) / 1000;

document.getElementById("reactionTime").innerHTML = reactionTime + " секунд";

setTimeout(makeShapeVisible, Math.random() * 1000);

}

Время реакции





















На этом наше увлекательное путешествие по миру базовых знаний JavaScript подошло к концу. Помните, что всё, что здесь написано, лишь очень незначительная попытка показать, что из себя представляет этот язык, но никак не полное руководство по нему. Далее мы будем смотреть на популярную библиотеку JavaScript, которая называется .