К списку уроков

Урок 3. Основы JS

Онлайн и в Л-512, 7 октября (вторник), 15:15

Изучим основы языка JavaScript. Синтаксис, типы данных, логические конструкции, циклы, массивы, функции.

Презентация - тык.
WIP...

Теория

Синтаксис

  1. Точка с запятой ;

  2. Комментарии

    • (однострочный - //)

    • (многострочный - /* */)

    • (выделенной области - ctrl + /)

  3. Объявление и инициализация переменной (синтаксис)

    • let - современный и рекомендуемый способ объявления.

    • const - позволяет объявить константу, т.е. неизменяемое значение. В JS некоторые сущности, объявленные через const, все таки можно изменить.

    • var (deprecated) - устаревший способ объявления переменной, существующий с момента создания JavaScript.

Типы данных (8 шт.)String

Это тип данных для хранения строковых значений. По сути, строка — это набор букв и других символов, заключенных в кавычки. Для строк в JavaScript можно использовать кавычки трёх видов:

  • Одинарные ‘ ’

  • Двойные “ ”

  • Косые

  • Чтобы использовать кавычки внутри строки, нужно использовать кавычки другого типа, либо поставить обратный слэш (экранирующий символ) перед кавычками внутри строки (*\"*). Например:

let correct1 = 'Я прохожу курс "База JavaScript"';
let correct2 = 'Я прохожу курс "База JavaScript"';

Number

Это тип данных для хранения чисел. Он используется как для целых, так и для дробных чисел

let age = 23;
let weight = 67.5;
let temperature = -5;

Boolean

Это логический (булевый) тип данных. Переменная этого типа может иметь всего два значения:

  • true(истина)

  • false(ложь)

Null

Специальный тип данных для хранения пустого или неизвестного значения

// У нас была машина
let myCarName = "Toyota Corolla";

// Но потом мы её продали и остались без машины
myCarName = null;

Undefined

Ещё один специальный тип данных. Он показывает, что переменная была объявлена, но значение ей не присвоено. Попробуем объявить переменную без присвоения значения и вывести её на консоль:

let x;
console.log(x); // Выведет undefined*

В чем же отличие от null? В случае с nullизвестно, что переменной уже присвоено значение. undefinedже говорит о том, что переменная объявлена, но никакого значения ей не было присвоено. На практике не рекомендуется вручную задавать значениеundefinedкакой-либо переменной. Если нужно явно задать пустое значение, используйте значениеnull.

Symbol

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

const id = Symbol("id");

BigInt

Используется для хранения очень больших целых чисел (больше, чем (2⁵³-1), т.е. 9007199254740991). Для создания переменной этого типа нужно поставить букву**n**в конце числа:

let bigNumber = 123456789n;

Object

Это сложный тип данных, который позволяет объединить несколько схожих параметров в одну сущность или структуру. Object объявляется с помощью фигурных скобок, внутри которых следует список пар "key" : value:

const car = {
	name: "Toyota Corolla",
	year: 2017,
	isNew: false,
	owner: null,
};

Логические конструкцииУсловный оператор if... else if... else...

  • Условный оператор (if) – если условие выполнится, то сделать какое-либо действие:

const number = 5;

// Передаем выражение
if (number < 10) {
	// Выполнится, так как 5 < 10 === true
	console.log(`Число ${number} меньше 10!`);
}

const string = "";
if (string.length > 0) {
	// Не выполнится
	console.log("Строка содержит текст!");
}

Второй console.log() не сработал, так как выражение в круглых скобках вернуло false. Также последний пример можно записать немного по-другому

const string = "";
if (string.length) {
	// Не выполнится
	console.log("Строка содержит текст!");
}
  • if...else – более расширенная запись предыдущей конструкции. В блоке else можем указывать код, который должен выполниться, если условие не сработает (выражение вернет false или falsy-значение).

const condition = 5 > 10; // вернет false
if (condition) {
	console.log("Выражение истино!");
} else {
	console.log("Выражение ложно!");
}
// Вывод: Выражение ложно!

const string = "";
if (string.length) {
	console.log("Строка содержит текст!");
} else {
	console.log("Строка пустая!");
}
// Вывод: Строка пустая!
  • else if – дополнительная проверка условия:

const status = "online";
if (status === "online") {
	console.log("Ваш статус: в сети");
} else if (status === "offline") {
	console.log("Ваш статус: не в сети");
} else {
	console.log("Ошибка! Неизвестный статус!");
}
// Вывод: Ваш статус: в сети

Условный оператор switch

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

switch (переменная) {
	case значение1:
		// выполнится, если переменная === значение1
		break; // завершение случая
	case значение2:
		// выполнится, если переменная === значение2
		break;
	default:
		// выполнится, если ни один другой случай не сработал
		break;
}
  • В круглых скобках после switch указывается переменная / значение, которое будем сравнивать. Ниже указываем случаи при помощи ключевого слова case. Если переменная / значение после switch будет равна значению из case, то он выполнится. Если ни один case, не выполнится, то выполнится default.

const status = "online";
switch (status) {
	case "online":
		console.log("Ваш статус: в сети");
		break;
	case "offline":
		console.log("Ваш статус: не в сети");
		break;
	default:
		console.log("Ошибка! Неизвестный статус!");
		break;
}
// Вывод: Ваш статус: в сети

Тернарный оператор ... ? ... : ... - компактная замена if...else

  • Если условие истинно, то вернется выражение1, а если ложно, то выражение2. Примеры:

const num = 7;
console.log(num === 7 ? "Число 7" : "Число не 7"); // 'Число 7'
console.log(num === 10 ? "Число 10" : "Число не 10"); // 'Число не 10'

// Запись в переменную
const text = num > 42 ? "Число больше 42" : "Число меньше 42";
console.log(text); // 'Число меньше 42'

ЦиклыЦикл while

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

  • В работе мы часто можем встретиться с задачами, где нужно использовать циклы (например, перебор какого-то списка). До выполнения программы мы не всегда можем знать, сколько элементов находится в списке и сколько нужно перебирать. Здесь на помощь приходят циклы, с помощью которых мы сможем выполнять какие-либо действия с элементами, пока выполняется условие (выполнять, пока не закончатся элементы списка).

while (условие) {
	// цикл будет повторяться, пока условие истинно
}
let i = 0;
while (i < 5) {
	console.log(i);
	i++;
}

/* 
  Вывод: 
  0
  1
  2
  3
  4
*/
  • Цикл выполняется 5 раз, переменная i меняет свое значение на каждой итерации и принимает значения от 0 до 5. Когда i становится 5, цикл прекращает свое выполнение. Так как переменную мы выводим только в теле цикла, значение 5 уже не выведется.

  • Рассмотрим другой пример. В условиях циклов также, как и в if , работает преобразование типов.

let i = 5;
while (i) {
	// приведение типов в условии цикла
	console.log(i);
	i--;
}
/* 
  Вывод: 
  5
  4
  3
  2
  1
*/
  • Здесь мы в условие передали число. Все числа преобразуются в true, но когда i доходит до значения 0, оно преобразовывается в false. Поэтому на этом цикл прекращает свое выполнение.

Цикл do ... while

do {
	// цикл будет повторяться, пока условие истинно
} while (условие);
  • Цикл похож на while, но здесь проверка условия находится после тела цикла. Сначала выполняется действие, а затем проверяется условие. Такая форма цикла полезна, если хотим, чтобы тело цикла выполнилось минимум 1 раз. Коротко говоря, do ... while сначала делает, а потому думает.

let i = 0;
do {
	console.log(i);
	i++;
} while (i < 5);

/* 
  Вывод: 
  0
  1
  2
  3
  4
*/

Цикл for

for (инициализация; условие; шаг) {
	// тело цикла
}
  1. Инициализация – выполняется один раз при входе в цикл (например i = 0)

  2. Условие – будет проверяться каждый раз на новой итерации цикла (0 < 10, 1 < 10, 2 < 10 и т. д.). Пока условие истинно, цикл будет выполняться.

  3. Шаг – выражение описывает, как должна изменяться i в процессе выполнения цикла. i++ означает, что переменная будет увеличиваться на 1 на каждой новой итерации.

  4. Тело цикла – будет выполняться снова и снова, пока цикл работает

for (let i = 0; i < 5; i++) {
	console.log(i);
}
/* 
  Вывод: 
  0
  1
  2
  3
  4
*/
const str = "Hello";
for (let i = 0; i < str.length; i++) {
	console.log(str[i]);
}
/* 
  Вывод: 
  H
  e
  l
  l
  o
*/
  • Цикл сработал ровно столько раз, сколько у нас символов в строке str (в данном случае 5 раз). Переменная i в процессе работы цикла меняет свое значение с 0 до 4. На каждой итерации вывелся символ строки str по индексу i.

Инструкция break

  • По умолчанию, циклы завершают свое выполнение, когда условие становится ложно. Но также мы можем вручную остановить цикл, используя инструкцию break. Если мы напишем break, то цикл полностью остановит свое выполнение:

let i = 0;
while (i < 10) {
	console.log(i);

	if (i === 3) break;

	i++;
}

/*
  Выведет:
  0
  1
  2
  3
*/
  • Цикл сработает 4 раза, i будет изменять свое значение от 0 до 3. Когда i будет равна 3, цикл закончит свое выполнение и i в итоге не дойдет до 9.

Инструкция continue

  • Инструкция continue, в отличие от break, останавливает работу текущей итерации, а не всего цикла. Её полезно использовать, когда нам больше не нужно выполнять действия в теле на текущей итерации, и мы можем перейти к следующей.

for (let i = 0; i < 5; i++) {
	if (i == 2) continue;
	console.log(i);
}

/*
  Выведет:
  0
  1
  3
  4
*/
  • В данном примере, цикл сработает 5 раз, i будет изменяться от 0 до 4. Но когда i будет равно 2 (на 3 итерации), выполнение в теле цикла прервется и мы перейдем сразу на следующую итерацию без вывода i в консоль.

МассивыЧто такое массив?

  • Массив – это набор элементов (значений). Элементы в нем могут быть как одного, так и разных типов.

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

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

  • Для того чтобы создать массив, достаточно создать какую-нибудь переменную, и в качестве значения прописать ей квадратные скобки:

const array = [];

Таким образом мы создали пустой массив.

  • Чтобы заполнить его элементами, в квадратных скобках можно прописать необходимые значения через запятую:

const array = [1, 2, true, 'JavaScript', 'Hello World!'];

В итоге мы создали массив из пяти элементов с различными типами данных.

Получение элементов массива

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

const array = ["first", "second", "third", "fourth"];
console.log("item:", array[1]); // item: second

Итак, мы получили второй элемент, который находится под индексом 1.

  • Если передать в скобки несуществующий индекс, то вернется значение undefined:

const array = ["first", "second", "third", "fourth"];
console.log("item:", array[4]); // item: undefined

В данном случае мы получили undefined, так как элемента под индексом 4 в массиве нет. Индекс последнего элемента - это 3.

Для того чтобы получить последний элемент массива, можно просто передать последний индекс. Но точный индекс мы можем не знать, так как в массиве может быть сколько угодно элементов, плюс их количество может меняться. Чтобы не передавать какой-то фиксированный индекс и сделать код универсальным, нам необходимо отталкиваться от длины массива. Таким образом, чтобы получить последний элемент массива, в скобках нужно написать array.length - 1:

const array = ["first", "second", "third", "fourth"];

console.log("last item:", array[array.length - 1]);
// last item: fourth

console.log("second item from end:", array[array.length - 2]);
// second item from end: third

В данном примере мы получили последний и предпоследний элементы массива.

Перебор массивов с помощью цикла for

  • Чтобы "пройтись" по каждому элементу массива, достаточно воспользоваться циклом (см. предыдущую тему) for и его свойством length:

const array = ["first", "second", "third", "fourth"];

for (let i = 0; i < array.length; i += 1) {
	console.log(i);
}

// Вывод:
/* 
  first
  second
  third
  fourth
*/
  • Что произошло?

    1. Обявили массив array с элементами "first", "second", "third", "fourth" (это строки)

    2. Использовали цикл for:

    • В нем объявили итерируемую переменную i (AKA счетчик)

    • Проверям, не меньше ли счетчик длины массива (так мы убеждаемся, что не выходим за пределы массива)

    • Увеличиваем счетчик

Перебор массивов с помощью цикла for ... of

  • Не менее удобным способом можно пройтись по массиву с помощью цикла for ... of:

const favouriteFruits = ["apple", "pear", "banana", "plum"];

for (let fruit of favouriteFruits) {
	console.log(fruit);
}

// Вывод:
/* 
  apple
  pear
  banana
  plum
*/
  • Что произошло?

    1. Обявили массив favouriteFruits с элементами "apple", "pear", "banana", "plum" (это строки)

    2. Использовали цикл for ... of:

    • Внутри мы объявили переменную fruit, которая будет являться определенным элементом массива (Например, в первый раз fruit = "apple", во второй fruit = "pear" и т.д.)

    • ...of favouriteFruits означает, что мы проходимся по массиву favouriteFruits

ОбъектыЧто такое объект

Объект — это набор свойств, и каждое свойство состоит из имени и значения, ассоциированного с этим именем. Значением свойства может быть функция, которую можно назвать методом объекта.

  • Объекты можно создавать с помощью фигурных скобок. Внутри них следует список свойств и их значений в формате ключ: значение через запятую:

// Пустой объект
const empty = {};

const car = {
	name: "VolksWagen Polo",
	year: 2017,
	isNew: false,
};
  • Значение свойства может иметь любой тип данных. Допускается вложенность объектов. В следующем примере объект owner вложен в объект car:

const car = {
	name: "VolksWagen Polo",
	year: 2017,
	isNew: false,
	owner: {
		name: "Ivanov Ivan",
		experience: 5,
	},
};
  • Если объект объявлен с помощью ключевого слова const, то его нельзя переопределить. Если попробовать это сделать, то будет выброшена ошибка:

const car = {
	name: "VolksWagen Polo",
	year: 2017,
	isNew: false,
};

car = {}; // Ошибка Uncaught TypeError: Assignment to constant variable.

Обращение к свойствам объекта

  • Можно обращаться к свойствам объекта через точку:

const car = {
	name: "VolksWagen Polo",
	year: 2017,
	isNew: false,
};

console.log(car.name); // VolksWagen Polo
// Или передаем название ключа в кавычках (это строка)
  • Либо через квадратные скобки:

const car = {
	name: "VolksWagen Polo",
	year: 2017,
	isNew: false,
};

// Передаем название ключа в кавычках, это строка
console.log(car["name"]); // VolksWagen Polo
  • Доступ через квадратные скобки дает нам возможность динамически менять занчение ключа, к которому мы оборащаемся:

const car = {
	name: "Toyota Corolla",
};

const key = "color";
car[key] = "red";

console.log(car);
  • Ко вложенным объектам можно обратиться с помощью последовательного использования точек либо квадратных скобок:

const car = {
	name: "VolksWagen Polo",
	year: 2017,
	isNew: false,
	owner: {
		name: "Ivanov Ivan",
		experience: 5,
	},
};

console.log(car.owner.name); // Invanov Ivan
console.log(car["owner"]["name"]); // Invanov Ivan

Изменение свойств объекта

  • Чтобы обновить свойство в объекте, используется такая же запись, как и при добавлении нового свойства:

const car = {
	name: "Toyota Corolla",
};

// Создание свойств
car.engine = 1.6;
car["maxSpeed"] = 185;

// Обновление свойства
car.name = "My Car";

console.log(car);
// { name: 'My Car', engine: 1.6, maxSpeed: 185 }
  • Чтобы удалить свойство в объекте, используется оператор delete:

const car = {
	name: "Toyota Corolla",
	engine: 1.6,
	maxSpeed: 185,
};

delete car.engine;
delete car.maxSpeed;

console.log(car);
// { name: 'Toyota Corolla' }

Перебор объектов

  • Осуществляется с помощью цикла for ... in:

const car = {
	name: "Toyota Corolla",
	year: 2017,
	isNew: false,
};

for (const key in car) {
	console.log(`${key}: ${car[key]}`);
}

// name: Toyota Corolla
// year: 2017
// isNew: false

Объединение нескольких объектов в один. Оператор spread.

  • Самый удобный и простой способ объединить несколько объектов в один — использовать spread-оператор.

Попробуем объединить два объекта car и carOwner в один — carFullInfo, а также добавим дополнительное свойство additionalInfo:

const car = {
	name: "Toyota Supra",
	year: 2017,
};

const carOwner = {
	ownerName: "Ivan Ivanov",
	ownerAge: 27,
};

const carFullInfo = {
	...car,
	...carOwner,
	additionalInfo: "Эта лекция когда-нибудь закончится?",
};

console.log(carFullInfo);
/*
{
  name: 'Toyota Supra', 
  year: 2017, 
  ownerName: 'Ivan Ivanov',
  ownerAge: 27,
  additionalInfo: 'Дополнительная информация'
}
*/
  • Spread-оператор является современным методом объединения объектов. При этом создается новый объект, который включает в себя все свойства из объединяемых объектов.

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

const obj1 = { x: 1, y: 1 };
const obj2 = { x: 2, y: 2 };

const unitedObj = {
	...obj1,
	...obj2,
	y: 3,
};

console.log(unitedObj.x); // Выведет 2
console.log(unitedObj.y); // Выведет 3

ФункцииЧто такое функция?

  • По сути, функции — это набор неких действий, «упакованных» под одним названием.

  • Давайте представим ситуацию, где мы управляем магазином некоторых товаров:

const tax = 0.2;

const price1 = 100;
const count1 = 5;
const totalPrice1 = price1 * count1 * (1 + tax); // Вычисляем итоговую цену

const price2 = 350;
const count2 = 2;
const totalPrice2 = price2 * count2 * (1 + tax); // Повторяем вычисления опять

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

  • Функции — это удобный способ организации кода. Они помогают:

    • Избавиться от необходимости писать один и тот же код повторно.

    • Если нужно изменить что-то в коде, то он меняется в одном месте.

    • Структура кода становится более четкой.

Объявление функции. Function declaration.

  • Синтаксис объявления простой функции выглядит так:

function doSomething() {
	// Код функции
}
  • Мы можем увидеть следующие части:

    • ключевое слово function

    • название функции

    • круглые скобки (далее мы рассмотрим, что внутри них можно передавать аргументы)

    • тело функции в фигурных скобках

  • Данный синтаксис объявления функции так и называется — Function Declaration (объявление функции). Чуть дальше мы рассмотрим и другие способы создания функций.

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

  • Функции могут иметь параметры, которые позволяют передавать значения в функцию. Рассмотрим эту возможность на примере функции, которая подсчитывает и выводит в консоль сумму товаров:

function showTotalPrice(price, count) {
	const totalPrice = price * count; // Используем параметры в вычислениях
	console.log(totalPrice);
}
  • Данная функция имеет два параметра — цена (price) и количество (count). По сути это переменные, которым присваиваются значения при вызове функции.

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

function showTotalPrice(price, count) {
	console.log(price * count);
}

showTotalPrice(100, 5); // Выведет в консоль: 500
showTotalPrice(350, 2); // Выведет в консоль: 700
  • Обратите внимание на разницу между параметрами и аргументами. На практике зачастую эти слова взаимозаменяемы, но стоит понимать отличия:

Параметр — это название переменной, которое указывается в объявлении функции. Аргумент — это значение параметра, которое передается при вызове функции.

Возврат значения из функции

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

function getTotalPrice(price, count) {
	const tax = 0.2;
	return price * count * (1 + tax); // Возвращаем результат
}

const totalPrice1 = getTotalPrice(100, 5); // Запишет: 600
const totalPrice2 = getTotalPrice(350, 2); // Запишет: 840

Function declaration

  • Function Declaration использует специальный синтаксис для объявления функций:

    • ключевое слово function

    • название функции

    • параметры функции

    • тело функции

    • возвращаемое значение (опционально)

const myCat = catToHumanAge(5);

function catToHumanAge(realAge) {
	console.log("Input cat age:", realAge);
	let message = "";

	if (realAge <= 1) {
		message = "your cat's human age is under 15";
	} else if (realAge == 2) {
		message = "your cat's human age is 24";
	} else {
		let age = 24 + (realAge - 2) * 4;
		message = `your cat's human age is ${age}`;
	}

	return message;
}

Function expression

  • Помимо стандартного синтаксиса объявления функции, существует другой способ, так называемый Function Expression (функциональное выражение). Он похож на объявление переменной с помощью одного из ключевых слов: let или const. Но переменной присваивается не обычное значение, а функция:

const addTen = function (number) {
	const result = number + 10;
	return result;
};

console.log(addTen(23));
  • Можно заметить, что по сути переменной присваивается анонимная функция. То есть мы сохраняем эту функции в переменную, и можем в дальнейшем обращаться к ней как к обычной именованной функции — doSomething().

Function declaration vs Function expression

  • Отметим различия между этими подходами:

  • Разный синтаксис: Function Declaration использует специальный синтаксис для объявления функций, Function Expression — похож на объявление переменной.

  • Функцию, объявленную с помощью Function Declaration синтаксиса, можно использовать до объявления, но если так сделать при помощи Function expression, то ничего не получится. Это связано с особенностями движка JavaScript.

  • Если вы ставите точку с запятой, то после фигурных скобок в Function Expression она должна ставиться (в отличие от синтаксиса Function Declaration).

// Function Expression
const sayHello1 = function () {
	alert("Hello");
};

// Function Declaration
function sayHello2() {
	alert("Hello");
}

Arrow function expression

  • В JavaScript также существуют стрелочные функции (Arrow Function Expression), которые позволяют писать еще более коротко:

const sayHello = (name) => {
	alert(`Hello, ${name}`);
};
  • Если параметр один, то скобки для списка параметров можно не писать. Если функция в теле содержит только одну строку, то можно пропустить фигурные скобки. В таком случае, из функции вернется результат выражения после стрелки (=>), это нужно иметь ввиду. Получится очень краткая и удобная форма записи:

const sayHello = (name) => alert(`Hello, ${name}`);
const calc = (a, b) => a + b;

sayHello("Vasya"); // Выведет сообщение "Hello, Vasya"
console.log(calc(1, 2)); // Выведет в консоль: 3

Важно: мы рекомендуем следить за скобками при использовании Arrow function expression, так как есть высокая вероятность забыть/переборщить с ними.

Итоги

function doSomething() {
	/* ... */
} // Function Declaration

const doSomething = function () {
	/* ... */
}; // Function Expression

const doSomething = () => {
	/* ... */
}; // Arrow Function Expression

Полезные ссылки

  • MDN - JavaScript - Подробная документация по JavaScript от Mozilla

  • Can I use - Информация о поддержке браузерами различных веб-технологий

Задания