Плавное введение в JS для тестировщиков
На первом дне нашего курса мы обсудим основные понятия программирования на JavaScript. Мы начнем с изучения переменных, функций и типов данных. Данное занятие является вводным и содержит в себе основной теоретический минимум.
Переменные — это контейнеры для хранения данных, которые можно изменять в процессе выполнения программы.
Переменные объявляются с помощью ключевых слов let
, const
и var
.
let name = "John"; // Переменная с именем name, содержащая строку "John"
const age = 30; // Константа с именем age, содержащая число 30
var isStudent = true; // Переменная с именем isStudent, содержащая булево значение true
Функции — это блоки кода, которые выполняют определенные задачи и могут быть вызваны многократно.
Функции могут быть объявлены с помощью ключевого слова function
или стрелочных функций.
// Обычная функция
function greet(name) {
return "Hello, " + name + "!";
}
// Стрелочная функция
const greetArrow = (name) => {
return "Hello, " + name + "!";
};
console.log(greet("Alice")); // Вывод: Hello, Alice!
console.log(greetArrow("Bob")); // Вывод: Hello, Bob!
JavaScript поддерживает такие типы данных, как:
let number = 42; // Число
let text = "Hello, World!"; // Строка
let isTrue = true; // Булево значение
let array = [1, 2, 3, 4, 5]; // Массив
let object = { name: "John", age: 30 }; // Объект
На втором дне нашего курса мы продолжим изучение JavaScript, сосредоточившись на циклах, булевой алгебре и ссылочных типах данных. Эти темы являются важными для понимания более сложных концепций программирования и эффективного использования языка.
Циклы позволяют повторять выполнение блока кода до тех пор, пока выполняется определенное условие. В JavaScript существует несколько типов циклов: for
, while
и do...while
(последний мы в целом затрагивать не будем).
Цикл for
используется, когда известно количество итераций, а также в случаях, когда нам нужна компактная запись
для переменной, которая будет изменяться после каждой итерации (прохождения цикла).
for (let i = 0; i < 5; i++) {
console.log(i); // Вывод: 0 1 2 3 4
}
Сам цикл можно разделить на три части:
let i = 0;
i < 5;
i++
Два плюса, которые идут в обновлении счетчика называются инкрементом (
i++
), все что делает данный оператор — просто добавляет единицу к переменной указаной до инкремента.Стоит упомянуть, что также существует обратный оператор — декремент, он уменьшает переменную на единицу (
i--
).
Цикл while выполняется до тех пор, пока условие истинно. Стоит заметить, что переменная у этого цикла
объявляется еще до начала цикла (смотрите где расположено let i
в примере ниже).
Также, важно упомянуть что в случае с while
мы сами должны увеличить значение счетчика внутри цикла.
let i = 0;
while (i < 5) {
console.log(i); // Вывод: 0 1 2 3 4
i++;
}
Цикл do...while гарантирует, что блок кода будет выполнен хотя бы один раз, прежде чем проверить условие.
let i = 0;
do {
console.log(i); // Вывод: 0 1 2 3 4
i++;
} while (i < 5);
Булева алгебра включает в себя операции AND (&&), OR (||) и NOT (!), которые используются для создания сложных условий и управления потоком выполнения программы.
let a = true;
let b = false;
console.log(a && b); // Вывод: false (AND)
console.log(a || b); // Вывод: true (OR)
console.log(!a); // Вывод: false (NOT)
Подробнее о булевой алгебре можно почитать вот тут: https://doka.guide/js/boolean/
Ссылочные типы данных, такие как объекты и массивы, хранят ссылку на данные, а не сами данные. Это означает, что при копировании ссылочного типа данных, изменения в одной переменной будут отражаться и в другой.
Массивы — это упорядоченные коллекции значений, которые могут быть разных типов.
let numbers = [1, 2, 3, 4, 5];
let anotherNumbers = numbers;
anotherNumbers[0] = 10;
console.log(numbers[0]); // Вывод: 10
Изучили стрелочные функции, коллбэки, вложенные циклы, работу со строками, рекурсивные алгоритмы, а также применили знания на практике в разработке маленьких игр
Стрелочные функции — это более короткий и удобный способ записи функций в JavaScript.
// Обычная функция
function greet(name) {
return `Hello, ${name}!`;
}
// Стрелочная функция, которая сразу возвращает значение
const greet = (name) => `Hello, ${name}!`;
// Стрелочная функция с несколькими параметрами
const add = (a, b) => a + b;
// Стрелочная функция с телом
const multiply = (a, b) => {
const result = a * b;
return result;
};
Коллбэки — это функции, которые передаются в другие функции в качестве аргументов и вызываются внутри этих функций. Они широко используются для обработки асинхронных операций и работы с массивами.
// Функция, принимающая коллбэк
function processArray(arr, callback) {
arr.forEach(callback);
}
// Коллбэк-функция
const printElement = (element) => {
console.log(element);
};
// Использование коллбэка
processArray([1, 2, 3, 4], printElement);
Вложенные циклы — это циклы, которые находятся внутри других циклов. Они используются для выполнения сложных итераций, таких как обработка многомерных массивов или генерация таблиц.
const matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
];
for (let i = 0; i < matrix.length; i++) {
for (let j = 0; j < matrix[i].length; j++) {
console.log(matrix[i][j]);
}
}
Рекурсивные алгоритмы — это алгоритмы, которые решают задачу путем решения более простых подзадач той же задачи. Они часто используются для решения задач, которые можно разбить на более мелкие подзадачи.
// Рекурсивная функция для вычисления факториала
function factorial(n) {
if (n === 0) {
return 1;
}
return n * factorial(n - 1);
}
console.log(factorial(5)); // 120
// Рекурсивная функция для вычисления чисел Фибоначчи
function fibonacci(n) {
if (n <= 1) {
return n;
}
return fibonacci(n - 1) + fibonacci(n - 2);
}
console.log(fibonacci(6)); // 8
JavaScript предоставляет множество встроенных методов для работы с различными типами данных, такими как массивы, объекты и числа.
shift()
: Удаляет первый элемент массива и возвращает его.unshift(element)
: Добавляет элемент в начало массива.slice(start, end)
: Возвращает новый массив, содержащий копию части исходного массива.splice(start, deleteCount, ...items)
: Изменяет содержимое массива, удаляя существующие элементы и/или добавляя новые.concat(...arrays)
: Объединяет два или более массива.indexOf(element)
: Возвращает первый индекс, по которому данный элемент может быть найден в массиве, или -1, если такого индекса нет.lastIndexOf(element)
: Возвращает последний индекс, по которому данный элемент может быть найден в массиве, или -1, если такого индекса нет.includes(element)
: Проверяет, содержит ли массив определённый элемент, возвращая true или false.find(callback)
: Возвращает значение первого найденного в массиве элемента, которое удовлетворяет условию, заданному в передаваемой функции.findIndex(callback)
: Возвращает индекс первого элемента в массиве, который удовлетворяет условию, заданному в передаваемой функции.every(callback)
: Проверяет, удовлетворяют ли все элементы массива условию, заданному в передаваемой функции.some(callback)
: Проверяет, удовлетворяет ли хотя бы один элемент массива условию, заданному в передаваемой функции.forEach(callback)
: Выполняет переданную функцию один раз для каждого элемента массива.sort(compareFunction)
: Сортирует элементы массива на месте и возвращает отсортированный массив.reverse()
: Переворачивает массив на месте.join(separator)
: Объединяет все элементы массива в строку и возвращает эту строку.flat(depth)
: Создает новый массив, в котором все элементы вложенных подмассивов были рекурсивно "подняты" на указанный уровень depth.flatMap(callback)
: Сначала применяет функцию коллбэк к каждому элементу, а затем "сплющивает" результат на один уровень.
Примеры использования дополнительных методов массивов:const numbers = [1, 2, 3, 4, 5];
const firstNumber = numbers.shift();
console.log(firstNumber); // 1
numbers.unshift(0);
console.log(numbers); // [0, 2, 3, 4, 5]
const slice = numbers.slice(1, 3);
console.log(slice); // [2, 3]
const splicedArr = [...numbers];
splicedArr.splice(2, 1, 99);
console.log(splicedArr); // [0, 2, 99, 4, 5]
const combined = numbers.concat([6, 7, 8]);
console.log(combined); // [0, 2, 99, 4, 5, 6, 7, 8]
const index = numbers.indexOf(99);
console.log(index); // 2
const includesThree = numbers.includes(3);
console.log(includesThree); // false
const found = numbers.find((num) => num > 3);
console.log(found); // 99
const allPositive = numbers.every((num) => num >= 0);
console.log(allPositive); // true
const someEven = numbers.some((num) => num % 2 === 0);
console.log(someEven); // true
numbers.forEach((num) => console.log(num)); // 0 2 99 4 5
const sortedNumbers = [...numbers];
sortedNumbers.sort((a, b) => a - b);
console.log(sortedNumbers); // [0, 2, 4, 5, 99]
const reversedNumbers = [...numbers];
reversedNumbers.reverse();
console.log(reversedNumbers); // [99, 5, 4, 2, 0]
const joined = numbers.join("-");
console.log(joined); // "99-5-4-2-0"
const nested = [1, [2, [3, 4]]];
const flattened = nested.flat(2);
console.log(flattened); // [1, 2, 3, 4]
const flatMapped = numbers.flatMap((num) => [num, num * 2]);
console.log(flatMapped); // [99, 198, 5, 10, 4, 8, 2, 4, 0, 0]
toFixed(digits)
: Возвращает строку, представляющую число с указанным количеством десятичных знаков.toPrecision(digits)
: Возвращает строку, представляющую число с указанным количеством значащих цифр.toString(radix)
: Возвращает строку, представляющую число в указанной системе счисления.isFinite()
: Проверяет, является ли число конечным.isInteger()
: Проверяет, является ли число целым.isNaN()
: Проверяет, является ли значение NaN (Not-a-Number).isSafeInteger()
: Проверяет, является ли число безопасным целым числом.parseFloat(string)
: Преобразует строку в число с плавающей точкой.parseInt(string, radix)
: Преобразует строку в целое число в указанной системе счисления.Примеры использования методов чисел:
const num = 123.456;
console.log(num.toFixed(2)); // "123.46"
console.log(num.toPrecision(4)); // "123.5"
console.log(num.toString(2)); // "1111011.0111110011001101..."
console.log(Number.isFinite(num)); // true
console.log(Number.isInteger(num)); // false
console.log(Number.isNaN(NaN)); // true
console.log(Number.isSafeInteger(9007199254740991)); // false
const floatNum = parseFloat("123.456abc");
console.log(floatNum); // 123.456
const intNum = parseInt("123abc", 10);
console.log(intNum); // 123
charAt(index)
: Возвращает символ по индексу.split(separator)
: Разделяет строку на массив подстрок по указанному разделителю.includes(searchString)
: Проверяет наличие подстроки в строке.slice(start, end)
: Извлекает часть строки и возвращает новую строку.replace(searchValue, newValue)
: Заменяет подстроку на новую строку.concat(...strings)
: Объединяет текст из двух или более строк и возвращает новую строку.trim()
: Удаляет пробелы с начала и конца строки.trimStart()
или trimLeft()
: Удаляет пробелы с начала строки.trimEnd()
или trimRight()
: Удаляет пробелы с конца строки.toLowerCase()
: Преобразует строку в нижний регистр.toUpperCase()
: Преобразует строку в верхний регистр.indexOf(searchValue, fromIndex)
: Возвращает индекс первого вхождения подстроки.lastIndexOf(searchValue, fromIndex)
: Возвращает индекс последнего вхождения подстроки.startsWith(searchString, position)
: Проверяет, начинается ли строка с указанной подстроки.endsWith(searchString, endPosition)
: Проверяет, заканчивается ли строка указанной подстрокой.repeat(count)
: Повторяет строку указанное количество раз.padStart(targetLength, padString)
: Дополняет строку слева до указанной длины.padEnd(targetLength, padString)
: Дополняет строку справа до указанной длины.Примеры использования методов строк:
const str = "Hello, World!";
console.log(str.charAt(4)); // "o"
console.log(str.split(", ")); // ["Hello", "World!"]
console.log(str.includes("World")); // true
console.log(str.slice(1, 4)); // "ell"
console.log(str.replace("World", "JavaScript")); // "Hello, JavaScript!"
console.log(str.concat(" Welcome to JavaScript!")); // "Hello, World! Welcome to JavaScript!"
console.log(str.trim()); // "Hello, World!"
console.log(str.trimStart()); // "Hello, World!"
console.log(str.trimEnd()); // "Hello, World!"
console.log(str.toLowerCase()); // "hello, world!"
console.log(str.toUpperCase()); // "HELLO, WORLD!"
console.log(str.indexOf("World")); // 7
console.log(str.lastIndexOf("o")); // 8
console.log(str.startsWith("Hello")); // true
console.log(str.endsWith("World!")); // true
console.log(str.repeat(2)); // "Hello, World!Hello, World!"
console.log(str.padStart(20, "*")); // "*******Hello, World!"
console.log(str.padEnd(20, "*")); // "Hello, World!*******"
Требуется повторить все изученные темы, так как на уроке будет брейншторм-тестинг, куча тестовых, а также разбор полетов домашки.
До текущего дня мы писали все наши маленькие программы в рамках одного файла. Пришло время немного освежить наши программы модулями, понять как из модулей получаются пакеты и зачем они нужны. Кроме того, мы запустим наши програмки на Node.js, вместо того чтобы запускать их в браузере.
Для того чтобы писать код на JavaScript разработчики используют редакторы кода. Они упрощают написание кода и, при наличии отладчика, ускоряют его отладку.
Среди популярных редакторов есть следующие:
Node.js — это среда, которая позволяет запускать JavaScript-код на сервере, а не только в браузере. Представьте, что вы можете использовать один и тот же язык программирования как для написания клиентского кода (то, что вы видите в браузере), так и для серверного кода (то, что работает на сервере и обрабатывает запросы). Это очень удобно, потому что вам не нужно переключаться между разными языками программирования.
Информация
Node.js построен на движке V8, который используется в Google Chrome.
Node.js предоставляет множество встроенных модулей, которые помогают работать с файловой системой, сетью и другими низкоуровневыми операциями. Это делает его мощным инструментом для создания серверных приложений.
Для установки Node.js воспользуйтесь официальным сайтом Node.js, либо пакетным менеджером своей системы:
# MacOS
brew install node
# Ubuntu
sudo apt install nodejs
# Fedora
sudo dnf install nodejs
Ранее мы могли открыть DevTools прямо в браузере и использовать его для запуска кода. Теперь мы можем использовать Node.js вместо браузера:
# Да, вот так просто
node
Если же нам нужно выполнить какой-то файл, то мы можем просто указать его:
node ./file.js
NPM (Node Package Manager) — это утилита, которая позволяет установить и управлять пакетами. C помощью него мы можем установить нужные нам пакеты и использовать их, но об этом немножко позже.
Модули — это способ организовать код в отдельные, независимые части. В JavaScript модулями являются любые файлы, которые имеют расширение .js
и включают в себя ключевые слова import
и export
.
Для написания модулей вводятся два важных ключевых слова:
export
— экспортирует переменную или функцию из файлаimport
— импортирует переменную или функцию из другого файлаВот как выглядит файл, который экспорирует переменную и функцию:
// Название файла: math.js
const PI = 3.141592653589;
function sum(a, b) {
return a + b;
}
export { PI, sum };
А вот как выглядит файл, который импортирует написанную нами переменную и функцию:
import { PI, sum } from "./math.js";
console.log(sum(PI, 2)); // 5.141592653589;
Пакеты — это коллекции модулей, которые можно легко установить и использовать в вашем проекте.
npm — это инструмент, который позволяет вам легко устанавливать и управлять пакетами. С его помощью вы можете найти и установить пакеты, созданные другими разработчиками, или опубликовать свои собственные пакеты.
Для установки пакета с помощью npm, вам нужно выполнить команду:
npm install имя-пакета
После этого пакет будет установлен в ваш проект, и вы сможете использовать его модули. Пакеты делают разработку более удобной и эффективной, позволяя вам использовать готовые решения и не изобретать велосипед.
По сути пакеты это группа модулей, которая содержит определенный функционал, уже сделанный кем-то.
Давайте установим express
, который используется для создания веб-серверов:
npm install express
После установки пакета вы можете использовать его в вашем проекте:
// Название файла: index.js
const express = require('express'); // Запрашиваем пакет express
const app = express(); // По умолчанию он отдает функцию, которая создает веб-сервер
// Обрабатываем URL /
app.get('/', (req, res) => {
res.send('Hello World!');
});
// Слушаем порт 3000
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
Запустите файл с помощью node ./index.js
и перейдите на http://localhost:3000.
Да, только что вы написали сервер, который отдает строку Hello, World!
В предыдущие дни мы работали с синхронным кодом, где выполнение операций происходило последовательно. Однако в реальных приложениях очень часто нужно выполнять задачи, которые требуют времени, такие как запросы к серверу, чтение файлов или таймеры. Чтобы справляться с такими задачами, используется асинхронное программирование.
Асинхронность позволяет вашему коду продолжать выполнение других задач, пока одна из операций, например, загрузка данных, еще не завершена.
Основные методы работы с асинхронностью в JavaScript:
Колбэки часто использовались раньше, но из-за их сложности в больших программах от них постепенно отходят. Пример:
setTimeout(() => {
console.log("Прошло 2 секунды");
}, 2000);
Промисы решают проблему вложенности в колбэках и позволяют проще работать с асинхронным кодом. Пример промиса:
const promise = new Promise((resolve, reject) => {
const success = true; // Условие для примера
setTimeout(() => {
if (success) {
resolve("Операция завершилась успешно!");
} else {
reject("Что-то пошло не так");
}
}, 2000);
});
promise
.then(result => console.log(result)) // Выводит: "Операция завершилась успешно!"
.catch(error => console.error(error));
async/await
— это современный способ работы с промисами, который делает код более читаемым.
Пример:
async function fetchData() {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/posts');
const data = await response.json();
console.log(data);
} catch (error) {
console.error("Ошибка:", error);
}
}
fetchData();
Этот подход позволяет писать асинхронный код, который выглядит как синхронный.
Сегодня мы рассмотрим важнейшие аспекты работы с данными в JavaScript: хранение, преобразование и взаимодействие с внешними источниками.
JSON — это формат для обмена данными, который очень популярен благодаря своей простоте и универсальности. Данные в формате JSON представляют собой строки, но выглядят как объекты JavaScript.
Пример JSON-объекта:
{
"name": "Иван",
"age": 25,
"hobbies": ["чтение", "игры", "путешествия"]
}
По сути JSON это обычный Javascript-объект, который можно записать в строку и обратно в объект:
const user = {
name: "Иван",
age: 25,
hobbies: ["чтение", "игры", "путешествия"]
};
const jsonString = JSON.stringify(user);
console.log(jsonString); // '{"name":"Иван","age":25,"hobbies":["чтение","игры","путешествия"]}'
const parsedUser = JSON.parse(jsonString);
console.log(parsedUser); // { name: 'Иван', age: 25, hobbies: [ 'чтение', 'игры', 'путешествия' ] }
localStorage — это встроенное хранилище браузера, которое позволяет сохранять данные между сеансами.
Пример работы с localStorage
:
// Сохранение данных
localStorage.setItem("user", JSON.stringify(user));
// Извлечение данных
const savedUser = JSON.parse(localStorage.getItem("user"));
console.log(savedUser);
// Удаление данных
localStorage.removeItem("user");
Важно: Данные в
localStorage
сохраняются до тех пор, пока пользователь их не удалит.
Session Storage — это встроенное хранилище браузера, которое позволяет сохранять данные только в течение сеанса.
Пример работы с sessionStorage
:
// Сохранение данных
sessionStorage.setItem("user", JSON.stringify(user));
// Извлечение данных
const savedUser = JSON.parse(sessionStorage.getItem("user"));
console.log(savedUser);
// Удаление данных
sessionStorage.removeItem("user");
Важно: Данные в
sessionStorage
сохраняются до тех пор, пока пользователь не закроет браузер.
Fetch API позволяет делать HTTP-запросы для получения или отправки данных на сервер.
Пример GET-запроса:
fetch("https://jsonplaceholder.typicode.com/posts")
.then(response => {
if (!response.ok) {
throw new Error(`Ошибка: ${response.status}`);
}
return response.json();
})
.then(data => console.log(data))
.catch(error => console.error("Ошибка запроса:", error));
Пример с использованием async/await
:
async function fetchPosts() {
try {
const response = await fetch("https://jsonplaceholder.typicode.com/posts");
if (!response.ok) {
throw new Error(`Ошибка: ${response.status}`);
}
const data = await response.json();
console.log(data);
} catch (error) {
console.error("Ошибка запроса:", error);
}
}
fetchPosts();