atpv

Навчальні матеріали з автоматизації технологічних процесів та виробництв, розроблені спільнотою

<- До підрозділу

Основи JavaScript: теоретична частина

1. Основні поняття

JavaScript (скорочено JS ) — мова програмування, яка використовується для сценаріїв (скриптів). Рушій JavaScript підключається до об’єктів свого середовища виконання (наприклад, веб-браузера) та надає можливість керування ними.

JS це реалізація стандарту ECMAScript. ES - це скорочення від ECMAScript. Кожне видання специфікації отримує назву з абревіатурою ES та номеру версії. Починаючи з ES6 офіційна назва специфікації позначається роком виходу. Тобто ES2015…ES2019…

Не дивлячись на схожість назв і елементів синтаксису, JavaScript та Java - це різні мови, які навіть відрізняються парадигмами програмування. Серед відмінностей окремо виділимо середовища виконання.

Java компілюється в проміжний байт-код, який на необхідному для виконання середовищі перетворюється на машинний код та виконується за допомогою Java-машини. Таким чином, реалізовуючи Java-машину (JVM) на різному залізі т ОС реалізовується крос-платформність.

рис.2.1. Принципи роботи Java для порівняння з Java Script

На відміну від системи з проміжною компіляцією, мова JavaScript - інтерпретована. Тобто аналізується та виконується вихідний код програми JavaScript-рушієм. Цей рушій може бути вбудований наприклад в браузер.

Таким чином, JavaScript найчастіше використовується як частина браузера, що надає можливість виконання коду на стороні клієнта (на пристрої кінцевого користувача), взаємодіяти з ним, керувати браузером, обмінюватися даними з сервером, змінювати структуру та зовнішній вигляд веб-сторінки. Однак рушій може бути частиною іншого застосунку або бути окремим застосунком, наприклад серверним.

рис.2.2. Принципи роботи Java Script

Таким чином, JavaScript використовується для:

Серед рушіїв можна виділити:

Якщо спрощено, рушії працюють за таким алгоритмом: читають та розбирають (парсять) текст скрипта; перетворюють скрипт в машинний код; запускають машинний код. При цьому можуть використовуватися алгоритми оптимізації.

У даному курсі будуть використовуватися приклади в основному для серверних застосунків, що виконуються на Node.js

Для створення скриптів та застосунків можна використовувати різні редактори, IDE (інтегровані середовища розробки, Integrated Development Environment) або/та утиліти розробки. При розробці скриптів для Веб-сторінок можна скористатися інтегрованими у Веб-бразуери утилітами. Наприклад для FireFox є Веб-консоль, яка може працювати у мультирядковому режимі. Можна вводити туди JS код і дивитися результат, запустивши його на виконання.

рис.2.3. Налагоджувач JS в консолі WEB-браузера FireFox

У останніх версіях, при намаганні скопіювати в консоль текст, FireFox попросить один раз явно написати дозвіл на виконання копіпасту. Слід також розуміти, що код в консолі буде запускатися в контексті відкритої сторінки.

У Google Chrome можливість перевірки коду доступна в Інструментах розробника. Введені у консолі інструкції одразу виконуються після натискання Enter. Якщо необхідно ввести кілька рядків інструкцій, після кожної треба вводити Shift+Enter.

рис.2.4. Налагоджувач JS в консолі WEB-браузера Google Chrome

У даному курсі усі приклади для лабораторних робіт наводяться для безкоштовного IDE середовища Visual Studio Code. Код пишеться в файлах *.js, які можна об’єднати в один проект, наприклад для серверу Node.js.

рис.2.5. Інструментальне середовище Visual Studo Code

Таким чином, усі приклади, що наведені нижче, можете тестувати у Visual Studio Code: створити файл, зберегти його як test.js, копіювати туди код і запускати на виконання F5.

2. Основні конструкції JavaScript

Побудова коду програми

Код складається з набору інструкцій, які розділені символом ;. У наступному фрагменті дві інструкції введені через крапку з комою в одному рядку.

console.log("Привіт світ!"); console.log("Друга інструкція.");

У наведених вище інструкціях викликається метод log вбудованого системного об’єкту console. Це дає можливість виводити на налагоджувальну консоль текстові повідомлення.

Мова JavaScript чутлива до регістру. Наприклад код console.Log() видасть помилку, так як у системному об’єкті console методу Log не існує, натомість є метод log.

Синтаксис коментарів такий самий, як в C та багатьох інших мовах програмування:

console.log("Привіт світ!"); // коментар для одного рядка 
/* довгий коментар
   на кілька рядків
*/
/* Однак, не можна /* змішувати коментарі */ тут буде SyntaxError */

При необхідності виконувати кілька інструкцій при керуванні потоком виконання (наприклад, if, for, while), використовують блокові інструкції { та }. Все що знаходиться в межах фігурних дужок об’єднується в одну інструкцію.

let a=3; let b=2;
if (a>b) //якщо треба виконати кілька інструкцій коли a>b  
{ //початок блоку
	console.log("Це перша інструкція блоку."); 
	console.log("Це друга інструкція блоку.");
} //кінець блоку

Змінні та константи

Для створення змінної в JavaScript використовуються ключові слова let та var. Оператор let оголошує локальну змінну, яка видима в межах блоку, де вона оголошена ({}). Оператор var має область видимості у межах усіє функції. Є ще ряд відмінностей між let та var, але тут вони не розглядаються. У нових скриптах рекомендується використовувати let .

Наведений нижче приклад створює (іншими словами: об’являє або означує) змінну з іменем message, і поміщає в неї дані, використовуючи оператор присвоювання = . Рядок збережеться в області пам’яті, зв’язаною зі змінною. Ми можемо отримати до неї доступ, використовуючи ім’я змінної.

let message1;
message1 = 'Hello'; // записати рядок в змінну
console.log(message1); // пише в консоль значення змінної
let message2 = 'Hello';//Можна суміщати об'явлення змінної і запис даних в один рядок.   
let user = 'John', age = 25, message3 = 'Hello';//можна кілька змінних об'являти в одному рядку
//або навіть так
let user1 = 'John',
  age1 = 25,
  message4 = 'Hello';

Оголошення const створює посилання на значення, доступне лише для читання. Що не гарантує незмінність значення, на котре вказує посилання, а лише той факт, що не можна повторно присвоїти будь-яке значення змінній з відповідним ім’ям.

Коли ви оголошуєте змінну за межами будь-якої функції, вона називається глобальною змінною, оскільки вона доступна для будь-якого іншого коду в поточному документі. Коли ви оголошуєте змінну в межах функції, вона називається локальною змінною, оскільки доступна лише в межах цієї функції.

Типи даних

Змінна в JavaScript може містити будь-які дані. У один момент там може бути текст, а в інший – число. Тобто під час виконання програми тип може змінюватися кілька раз (так звана “динамічна типізація”). У наведеному нижче коді, одній змінній присвоюють різні за типом значення. Оператор typeof повертає значення типу.

//в JS тип може змінюватися під час виконання програми
let message = "hello";		//це текст, і message тепер типу string
console.log (typeof(message));//string
message = 123456; 			//це число, і message тепер типу number 
console.log (typeof(message));//number

У JavaScript існує сім типів даних: number, string, boolean, null, undefined, object, symbol. Спочатку розглянемо базові (примітивні) типи, усі інші типи будуть розглянуті пізніше. Детальніший розгляд типів можна подивитися у розділі “Типи даних та літерали” з довідника.

У JavaScript примітивні типи даних number, string, boolean, подібно об’єктам мають методи і властивості, до яких можна звертатися через крапку. Для цього JavaScript при такому доступі створює спеціальний об’єкт-обгортку, який надає потрібну функційність, після чого видаляється. Ці об’єкти мають відповідні назви String, Number, Boolean і Symbol, і мають різний набір методів. Наприклад, у наведеному нижче фрагменті, метод toUpperCase з об’єкту типуString виводить рядок зі зміненим регістром літер на усі великі.

let message = "привіт";	
console.log (message);//привіт
console.log (message.toUpperCase());//ПРИВІТ

Особливими типами є null та undefined. Тип і значення null значить буквально нічого. Значення undefined значить, що змінна є, але їй не було присвоєне значення.

let x;
console.log(x); // виведе "undefined"

Булеві змінні та оператори

Булевий тип (boolean) може приймати значення: true (істина) і false (хибність). Можна записати у таку змінну результат операції порівняння або логічної операції.

let isGreater = 4 > 1;
console.log(isGreater); // буде true 
let a = true; // використання булевого літералу "true"

До булевих значень можна застосовувати логічні оператори “І” (&&), “АБО”(||), “НЕ”(!). Однак, оператори && та || насправді повертають значення одного з заданих операндів. Тому, якщо ці оператори використовуються зі значеннями не булевого типу, вони повернуть значення того самого типу. Оскільки логічні вирази обчислюються зліва направо, вони перевіряються на можливе “коротке замикання”, тобто обчислення проходить за наступними правилами:

Правила логіки гарантують, що ці обчислення завжди будуть правильними. Зауважте, що частина виразу будь-що не обчислюється, тому будь-які побічні ефекти від цих обчислень не відбудуться.

let a1 =  true && true;     // t && t вертає true
let a2 =  true && false;    // t && f вертає false
let a3 = false && true;     // f && t вертає false
let a4 = false && (3 == 4); // f && f вертає false
let a5 = 'Кіт' && 'Пес';    // t && t вертає Пес
let a6 = false && 'Кіт';    // f && t вертає false
let a7 = 'Кіт' && false;    // t && f вертає false
let o1 =  true || true;     // t || t вертає true
let o2 = false || true;     // f || t вертає true
let o3 =  true || false;    // t || f вертає true
let o4 = false || (3 == 4); // f || f вертає false
let o5 = 'Кіт' || 'Пес';    // t || t вертає Кіт
let o6 = false || 'Кіт';    // f || t вертає Кіт
let o7 = 'Кіт' || false;    // t || f вертає Кіт
let n1 = !true;  // !t вертає false
let n2 = !false; // !f вертає true
let n3 = !'Кіт'; // !t вертає false

Числові змінні

У JavaScript усі числові дані, як цілочисельні значення, так і числа з плаваючою комою представлені через тип даних (number). Для дуже великих цілих чисел (більше за модулем, ніж два в степені 53), застосовується спеціальний об’єкт - BigInt, у цій лекції не будемо його розглядати.

У якості літералів, крім звичайних чисел (15 чи 36.6) існують спеціальні числові значення: Infinity (нескінченність), -Infinity (мінус нескінченність) і NaN (не число).

console.log(1+2);//видасть 3
console.log(1.1+2.1);//видасть 3.2
console.log("2"+3);//видасть "23"
console.log("два"+3);//видасть "два3"
console.log(2/"0.5");//видасть 4
console.log("два"/3);//видасть NaN
console.log(1/0);//видасть Infinity

Цілочисельні літерали типу number можна виразити у різних формах. Це задається першою літерою літералу:

console.log(-345);		//-345
console.log(-0o77);		//-63
console.log(-0xF1A7);	//-61863
console.log(-0b11);		//-3

Літерали з плаваючою комою можуть задаватися у різному форматі:

console.log(3.1415926);	//3.1415926
console.log(-.123456789);//-0.123456789
console.log(-3.1E+12);	//-3100000000000
console.log(.1e-3);		//0.0001

Як наводилося вище, примітивні типи можуть використовуватися як об’єкти. Об’єкт-обгортка Number має багато корисних властивостей та методів. Наприклад метод toString виводить значення типу String у вказаній у дужках системі числення.

let a=0xF; //16-ковий формат
console.log (a.toString(2)); // 1111 - двійковий формат

Про об’єктні властивості та методи Number можна прочитати у наступній лекції.

Змінні string

Рядок (string) в JavaScript повинна бути взята в лапки.

let str = "Рядок в подвійних лапках";
let str2 = 'Можна використовувати одинарні лапки';
let phrase = `Зворотні лапки (зліва від '1') дозволяють вставляти значення змінних ${str}, інші лапки таке не дозвоялють робити`;

Крім звичайних символів до рядків можна також включати спеціальні, як це показано в наступному прикладі. Весь перелік символів доступний у розділі string з довідника.

console.log ("один рядок \n інший рядок");	//виведеться в два рядки

Об’єкт-обгортка String мають багато корисних методів і властивостей. Повний перелік наведений у довіднику. У прикладі нижче, деякі з них:

let a='Текст';
console.log (a.length);     //5 - довжина, тобто кільксть літер
console.log (a.toLowerCase());//текст - усі літери маленькі
console.log (a[2]);         //к - доступ до літери по номеру
for (let char of a='Текст') {//перебір усіх символів
    console.log(char); // Т,е,к,с,т
}
console.log(a.substring(0,3)); //Тек - підрядок з 0-го по 3-й не включно
console.log(a.substr(1,2)); //ек - підрядок з 1-го, 2 шт

Додатково про роботу зі змінними string та виразами можна почитати за цим посиланням або за цим.

Оператор typeof

Оператор typeof повертає тип аргумента. Він працює однаково з довма синтаксисами

typeof операнд
typeof (операнд)
console.log (typeof undefined);// "undefined"
console.log (typeof 0) ;// "number"
console.log (typeof true); // "boolean"
console.log (typeof "foo"); // "string"
console.log (typeof Symbol("id")); // "symbol"
conso;e.log (typeof (['Я','М']));//"object", бо масиви це об'єкти
console.log (typeof Math); // "object" - так як Math вбудований в JS обєкт для роботи з мат.операціями
console.log (typeof null); // "object" - хоч це не так
console.log (typeof console.log); // "function" - хоч формально такого типу немає, для методів і функцій повертається "function"

Перетворення типів

Найчастіше оператори і функції автоматично перетворюють передані їм значення (примітивного типу) до потрібного типу. Наприклад, console.log автоматично перетворює будь-яке значення до типу string. Математичні оператори перетворюють значення до чисел. Але є випадки, коли потрібно явно перетворити значення в очікуваний тип.

Можна використовувати функцію String (value), щоб явно перетворити значення до рядка:

let value = true;
console.log (typeof value); // boolean
value = String(value); 		// тепер це рядок, що дорівнює "true"
console.log (typeof value); // string

Можна використати функцію Number(value), щоб явно перетворити value в число. Якщо рядок не може бути перетворений в число, результатом буде NaN.

let str = "123"; console.log(typeof str); // string
let num = Number(str); console.log(typeof num); // стає числом 123, тому number
let age = Number("Будь який рядок без числа"); console.log(age); // NaN, перетворення не вдалося
console.log(Number("   123   ") ); // 123
console.log(Number("123z") );      // NaN (помилка читання числа в "z")
console.log(Number(true) );        // 1
console.log(Number(false) );       // 0

Майже усі математичні оператори виконують чисельне перетворення, за виключенням +. Якщо один із доданків є рядком, тоді всі інші приводяться до строчок і робиться конкатенація (з’єднання).

console.log("1.1" + "1.1");		//"1.11.1"
console.log((+"1.1") + (+"1.1"));//2.2   

У випадку, коли значення, що представляє число, знаходиться в пам’яті як рядок, існують методи для перетворення parseInt() та parseFloat().

console.log(Boolean(1)); // true
console.log(Boolean(0)); // false
console.log(Boolean("Привіт!")); // true
console.log(Boolean("")); // false
console.log(Boolean("0")); // true
console.log(Boolean(" ")); // пробіл це також true (любий непустий рядок це true)

Оператор присвоєння (=)

Лівосторонньому операнду = дається значення правостороннього виразу.

let x = 2 * 2 + 1;
console.log(x); 		// 5
let a, b, c;
a = b = c = 2 + 2;		//присвоєння ланцюжком, усім змінним буде присвоєно 4
let a1 = 1;				//1
let b1 = 2;				//2
let c1 = 3 - (a1 = b1 + 1);//0

Оператори порівняння

Оператор порівняння порівнює операнди та повертає логічне значення істинності порівняння. Операнди можуть бути числовими, рядками, логічними значеннями або об’єктами. Рядки порівнюються згідно стандартного лексикографічного порядку, з використанням значень Unicode.

У більшості випадків, якщо два операнди не належать до одного типу, JavaScript намагається привести їх до належного для порівняння типу. Зазвичай це призводить до числового порівняння операндів. Єдиними винятками у конвертації типів під час порівняння є оператори === та !==, які виконують перевірку на строгу рівність та строгу нерівність. Ці оператори не намагаються перед перевіркою на рівність привести операнди до спільного типу.

Наступний приклад показує як працюють оператори. Усі виведення в консоль будуть давати результат TRUE.

let var1 = 3; let var2 = 4;
//усі наведені нижче приклади виведуть значення TRUE;
console.log (3 == var1);	//рівність
console.log ("3" == var1);	//рівність
console.log ("3" == 3);		//рівність
console.log (var1 != 4);	//нерівність
console.log (var2 != "3");	//нерівність
console.log (3 === var1);	//строга рівність, оператори рівні і одного типу
console.log (var1 !== "3");	//строга нерівність, оператори одного типу, але нерівні 
console.log (3 !== '3');	//строга нерівність, оператори різного типу
console.log (var2 > var1);	//більше ніж
console.log ("12" > 2);		//більше ніж
console.log (var2 >= var1); //більше абл дорівнює
console.log (var1 < var2);  //менше ніж
console.log (var1 <= var2); //менше або дорівнює

Арифметичні оператори

Арифметичний оператор приймає числові значення (літерали чи змінні) в якості операндів та повертає єдине числове значення. Стандартними арифметичними операторами є додавання (+), віднімання (-), множення (*) та ділення (/). Ці оператори працюють так само, як і в більшості інших мов програмування, при використанні з числами з рухомою комою (зокрема, зауважте, що ділення на нуль повертає Infinity). Робота з іншими арифметичними операторами показана у прикладі нижче:

let x=3;
console.log (12 % 5);	//2, остача від ділення
console.log (++x);	console.log (x); //4(інкрменет, потім вивід), потім 4
console.log (x++);	console.log (x); //4(вивід потім інкремент), потім 5
console.log (--x);	console.log (x); //4(декрменет, потім вивід), потім 4
console.log (x--);	console.log (x); //4(вивід потім декремент), потім 3
console.log (-x);	//-3, унарний мінус робить знак протилежним
console.log (+"3");	//-3, унарний плюс перетворює операнд на число 
console.log (2**3);	//8, підносить до степеня 

Бітові оператори

Бітовий оператор опрацьовує свої операнди, як послідовність 32-х бітів (нулів та одиниць). Бітові оператори виконують операції над цими бітовими представленнями і повертають стандартні числові значення JavaScript. Кожен біт першого операнду ставиться у пару до відповідного біту другого операнду: перший до першого, другий до другого, і так далі. Наступний приклад показує результати таких операцій.

let a;
a = 0b1101 & 0b1000; console.log (a.toString(2));//1000, побітове І
a = 0b1101 | 0b1010; console.log (a.toString(2));//1111, побітове АБО
a = 0b1101 ^ 0b0001; console.log (a.toString(2));//1100, побітове виключне АБО, повертає 0 там де біти однакові
a = ~ 0xFFFF_FFF0; console.log (a.toString(2));//1111, інвертує біти
//при зсуві усі біти на краю куди зсовують "випадають", а звідки - заповнюються нулями 
a = 0b0011 << 3; console.log (a.toString(2));//11000, зсуває на три біти ліворуч
a = 0b11100 >> 4; console.log (a.toString(2));//1, зсуває на 4 біти праворуч, 
a = 0x8000_0000 >> 16; console.log (a.toString(16));//-8000, зсуває на 16 біт праворуч, але знак залишає 
a = 0x8000_0000 >>> 16; console.log (a.toString(16));//8000, зсуває на 16 біт праворуч, знак приймає як біт

Оператори з рядками

На додачу до операторів порівняння, які можуть застосовуватись до рядкових значень, оператор конкатенації (+) об’єднує значення двох рядків, повертаючи єдиний рядок. Наприклад,

console.log('мій ' + 'рядок'); // консоль виводить рядок "мій рядок".

Для конкатенації рядків може також застосовуватись скорочений оператор присвоєння += . Наприклад,

let mystring = 'алфа';
console.log (mystring += 'віт'); // повертає "алфавіт" та присвоює це значення mystring.

Умовний оператор

Умовний оператор - єдиний оператор у JavaScript, який приймає три операнди. У оператора може бути одне чи два значення, в залежності від умови. Використовує наступний синтаксис:

умова ? значення1 : значення2

Якщо умова дорівнює true, оператор повертає значення1. В іншому випадку - значення2. Умовний оператор можна використовувати будь-де, де використовується звичайний оператор. Наприклад:

let age = 19;
let status = (age >= 18) ? 'дорослий' : 'неповнолітній';
console.log (status); //дорослий

Ця інструкція присвоює значення “дорослий” змінній status, якщо значення age (вік) більше чи дорівнює 18. Інакше, вона присвоює змінній status значення “неповнолітній”.

Оператор кома

Оператор кома (,) обчислює обидва свої операнди та повертає значення останнього операнду. Цей оператор найчастіше використовується всередині циклу for, що дозволяє оновлювати більше однієї змінної на кожному проході циклу. Наприклад, якщо a є двовимірним масивом з 10 елементами по кожній стороні, наступний код використовує оператор кому, щоб оновити дві змінні одночасно. Код виводить значення діагональних елементів масиву:

for (var i = 0, j = 9; i <= j; i++, j--)
  console.log('a[' + i + '][' + j + ']= ' + a[i][j]);

Умовні інструкції

Як і в інших мовах if, дозволяє робити галуження.

let a=3; let b=2;
if (a>b) {
  console.log ("a>b");
} else {
  console.log ("a<=b");
}
//використання else if
if (a>b) {
  console.log ("a>b");
} else if (a<b) {
  console.log ("a<=b");
} else {
  console.log ("a=b");
} 

Конструкція switch замінює собою одразу кілька if. Вона має один або кілька блоків case і необов’язковий блок default.

switch(x) {
  case 'value1':  // if (x === 'value1')
    ...
    [break]
  case 'value2':  // if (x === 'value2')
    ...
    [break]
  default:		//якщо не знайдено жодного варіанту
    ...
    [break]
}

Приклад використання switch:

let a = 2 + 2;
switch (a) {
  case 3:
    console.log( 'Малувато' );
    break;
  case 4:
    console.log( 'В точку!' ); //виведе цей варіант
    break;					  //після чого перестане перебирати	
  case 5:
    console.log( 'Перебор' );
    break;
  default:
    console.log( "Нема таких значень" );
}

Якщо break немає, то виконання піде нижче по наступнимcase, при цьому інші умови ігноруються.

let a = 2 + 2;
switch (a) {
  case 3:
    console.log( 'Малувато' ); 
  case 4:
    console.log( 'В точку!' ); 	//виконається, бо a = 4
  case 5:
    console.log( 'Перебор' );	//виконається, бо немає `break`
  default:
    console.log( "Нема таких значень" );//виконається, бо немає `break`
}

Можна групувати кілька варіантів, тобто у наступному прикладі будь який з пунктів 3 або 5 приведе до виконання console.log('Трохи не попали!').

let a = 3;
switch (a) {
  case 4:
    console.log('Правильно!');
    break;
  case 3: // (*) групуємо оба case
  case 5:
    console.log('Трохи не попали!');
    break;
}

Цикли

Код з тіла циклу while виконується, поки умова виконується. Наприклад, цикл нижче виводить i, поки i < 3

let i = 0;
while (i < 3) { // виводить 0, потім 1, потім 2
  console.log( i );
  i++;
}

Одне виконання циклу називається ітерацією. Вище наведено 3 ітерації. Якщо тіло циклу має тільки одну інструкцію, оператори блоку {…} можна не писати

let i = 3;
while (i) console.log(i--);

Синтаксис do..while дає можливість виконати тіло циклу принаймні один раз.

do {
  // тіло циклу
} while (condition);

“Класичний” цикл for має наступний вигляд:

for (початок; умова; крок) {
  // ... тіло циклу ...
}

Цикл нижче виконує console.log(i) для i від 0 до (але не включаючи) 3:

for (let i = 0; i < 3; i++) { // виведе 0, потім 1, потім 2
  console.log(i);
}
частина    
початок i = 0 Виконується один раз при вході в цикл
умова i < 3 Перевіряється перед кожною ітерацією циклу. Якщо вона буде false - цикл зупиниться.
крок i++ Виконується після тіла циклу на кожній ітерації перед перевіркою умови.
тіло console.log(i) Виконується знову і знову, поки умова виконується true.

Можна використовувати існуючу змінну. Будь яка частина for може бути пропущена.

Інші інструкції циклів будуть розглянуті в розділі Масиви

Можна вийти з циклу у будь який момент за допомогою директиви break. Наприклад:

let sum = 0;
while (true) {
  let value = +prompt("Введіть число", '');
  if (!value) break; // (*)
  sum += value;
}
alert( 'Сумма: ' + sum );

Директива continue забезпечує перехід до наступної ітерації:

 for (let i = 0; i < 10; i++) {
  // якщо парне, пропустити іншу частину тіла циклу
  if (i % 2 == 0) continue;
  console.log(i); // 1, потім 3, 5, 7, 9
}

3. Основи роботи з функціями

Окрім вбудованих системних функцій є також можливість писати власні. Тут наведемо тільки базові можливості функцій, інші будуть даватися в одній із наступних лекцій.

Оголошення функції

Оголошення функції (функціональний оператор) означує функцію з вказаними параметрами.

function name([param[, param,[..., param]]]) {
   [statements]
}

Функції повинні бути в області видимості під час виклику, але оголошення функції може бути записаним нижче виклику, як у цьому прикладі:

console.log (sum(2)); 	//значення параметра b не задається, результат виведе 5
function sum(a, b=3) {	//b з передачою значення за замовченням
  return (a+b);
}

Виклик функції з передачею параметрів

У функцію можуть передаватися параметри за замовченням, як показано вище в прикладі для змінної b.

Примітивні параметри (такі, як число) передаються функціям за значенням, тобто якщо функція змінює значення параметра, ця зміна не відбувається в змінній, яка була передана при виклику.

let a=2;
console.log (test(a)); 	//3
console.log (a); 		//2
function test(a) {
  a=3;
  return (a);
}

Об’єкти передаються функції за посиланням. А отже функція може змінити цей об’єкт.

let a={val:2};
console.log (test(a)); 	//3
console.log (a.val); 	//3
function test(a) {
  a.val=3;
  return (a.val);
}

Функціональні вирази

Функції також можуть бути створені за допомогою функціональних виразів, які можна присвоювати змінним. Така функція може бути анонімною, тобто їй не обов’язково мати ім’я. Наприклад, square можна визначити як:

let square = function(number) { return number * number; };
let x = square(4); // повертає 16

Тим не менше, ім’я може бути надане у функціональному виразі, і може бути використане всередині функції, щоб звернутися до самої себе, або в налагоджувачі, щоб визначити функцію в трасуванні стеку:

var factorial = function fac(n) { return n < 2 ? 1 : n * fac(n - 1); };
console.log(factorial(3));

Підняття функції (використання раніше об’явлення) працює тільки для оголошення функції, а не для функціонального виразу.

Функціональні вирази зручні при передачі функції як аргументу до іншої функції. Наступний приклад показує функцію map, яка повинна отримати функцію як перший аргумент, а масив як другий аргумент.

function map(f, a) {
  let result = [], // Створення нового масиву
      i;
  for (i = 0; i != a.length; i++)
    result[i] = f(a[i]);
  return result;
}

Область видимості функції

Змінні, означені всередині функції, недоступні ззовні цієї функції. Проте, функція може звертатись до усіх змінних та функцій, означених у області видимості, де вона оголошена. Іншими словами, функція, оголошена у глобальній області видимості, може звертатись до усіх змінних, оголошених у глобальній області видимості. Функція, оголошена всередині іншої функції, має доступ до усіх змінних, оголошених у батьківській функції, а також до будь-якої змінної, до якої має доступ батьківська функція.

// Ці змінні визначені у глобальній області видимості
let num1 = 20, num2 = 3, name = 'Chamahk';
// Ця функція означена у глобальній області видимості
function multiply() {
  return num1 * num2;
}
console.log (multiply()); // Повертає 60
// Приклад вкладеної функції
function getScore() {
  let num1 = 2, num2 = 3;
  function add() {
    // використовуються змінні об'явлені в функції getScore   
    return name + ' scored ' + (num1 + num2);
  }
  return add();
}
console.log (getScore()); // Повертає "Chamahk scored 5"

4. Основи роботи з об’єктами

Використання об’єктів

object - у JavaScript це колекція властивостей і методів. Методом називається функція, яка є членом об’єкта. Властивість це значення або набір значень (у вигляді масиву або об’єкта), який є членом об’єкта і може містити будь який тип даних.

Доступ до властивостей та методів проводиться через крапку. Якщо це метод, він викликається з використанням дужок, якщо треба прочитати чи записати властивість - назва вказується без дужок.

let a = "текст"
console.log (a.toUpperCase());//ТЕКСТ, виклик методу toUpperCase(усі прописні) з дужками
console.log (a.length);     //5, читання властивості length(довжини) без дужок    

До імен властивостей, які не є дійсними ідентифікаторами, не можна отримати доступ до властивості через крапку (.), але можна можна це зробити через квадратні лапки (“[]”).

let unPropNames = {
  '': 'An empty string',
  '!': 'Bang!'
}
console.log(unPropNames.'');   // помилка SyntaxError: Unexpected string
console.log(unPropNames['']);  // An empty string
console.log(unPropNames.!);    // помилка SyntaxError: Unexpected token !
console.log(unPropNames['!']); // Bang!

Доступ через квадратні лапки також дає можливість доступатися до властивостей об’єкту через змінну або вираз.

let key = "likes birds";
user[key] = true; // те саме, що і user["likes birds"] = true;
let fruit = prompt("Який фрукт купити?", "apple");
let bag = {
  [fruit]: 5, // ім'я властивості буде взято зі змінної fruit 
};

Створення та редагування об’єктів

Крім великої кількості існуючих системних і бібліотечних об’єктів, можна використовувати свої об’єкти. Об’єкт можна створити через конструктор, або через літерал.

let user = new Object(); // синтаксис "конструктор об'єкта"
let user = {};  // синтаксис "літерал об'єкта"

Використання new вказує, що необхідно використати спеціальну функцію-конструктор з назвою Object. Конструктор Object створює об’єкт-обгортку для переданого значення. Якщо значенням є null або undefined, він створює і повертає порожній об’єкт, в іншому випадку — повертає об’єкт такого типу, який відповідає переданому значенню.

Специфікатор new може також використовуватися для виклику інших конструкторів (не Object ) - функцій, в яких прописуються інструкції створення об’єкту. Так досягається прототипування об’єктів, так як в конструкторі можна задати всю структуру об’єкту для всіх екземплярів. Детальніше про створення таких конструкторів буде в наступній лекції. Тут розглянемо використання існуючих конструкторів на прикладі Date.

const datebd = new Date('1978/10/23 03:45:00');	//день народження
let now = new Date();                           //зараз  
let myAge = parseInt (now - datebd)/(1000*60*60*24);//мс*с*хв*год*доба
console.log (myAge);							//кількість днів

У розділі роботи з масивами також розглянемо використання вбудованого конструктору Array для роботи з масивом.

Об’єктні літерали - це список з нуля або більше пар імен властивостей та методів об’єкту та асоційованих з ними значень, взятих у фігурні дужки ({}). Властивості, які також називають полями, мають ключ, який також називають ім’ям або ідентифікатором. Використовуючи літеральний синтаксис об’явлення можна одразу в об’єкт помістити кілька властивостей і методів через пару “ключ : значення”. Значення може бути будь якого типу, в тому числі об’єктом. Наприклад:

var sales = 'Toyota';
function carTypes(name) {
  if (name === 'Honda') {
    return name;
  } else {
    return "Вибачте, ми не продаємо " + name + ".";
  }
}
var car = { myCar: 'Saturn', 		//властивість - літерал 
           getCar: carTypes('Honda'),//властивість - результат функції 
           special: sales };		//властивість - змінна 
console.log(car.myCar);   // Saturn
console.log(car.getCar);  // Honda
console.log(car.special); // Toyota 

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

let a = {p1 : 10};  //створили об'єкт з однією властивістю
a.p2 = 11;          //добавили нову властивість зі значенням 11
a.sum = function (a) {return this.p1 + this.p2 + a};//добавили новий метод, що повертає суму 
console.log (a.sum(2));               //виведе 23

Видалення властивостей чи методу проводиться через delete

delete a.sum;

Можна використовувати числовий або рядковий літерал для назви властивості, або вкладати об’єкт всередину іншого. У наступному прикладі використовуються ці можливості.

let car = { manyCars: {a: 'Saab', b: 'Jeep'}, 7: 'Mazda' };
console.log(car.manyCars.b); // Jeep
console.log(car[7]); // Mazda

Імена властивостей об’єкта можуть бути будь-якими рядками, включаючи порожні. Якщо ім’я властивості не є дійсним ідентифікатором (не за правилами найменування змінних, наприклад включає пробіли) чи числом JavaScript, воно повинно бути вставлено в лапки.

Якщо необхідно властивості надавати значення з тим самим іменем, його можна не вказувати:

function makeUser(name, age) {
  return {
    name, // те саме, що і name: name
    age   // те саме, що і age: age
    // ...
  };
}

Присвоєння

Присвоєння для об’єктів працює не як дублювання змісту, а як копіювання за посиланням на той же самий об’єкт. Порівняння об’єктних змінних == або === показує, що ці змінні посилаються на той же об’єкт. Тобто:

let user = { name: "John" };
let admin = user;               // це друга змінна, яка посилається на той же об'єкт
console.log (admin === user);   //true
user.lastname = "Smith";        // нова властивість для user
console.log (admin.lastname);   //Smith, те саме значення бо це той же об'єкт

Перевірка наявності властивості чи методу

Оператор in повертає true, якщо вказана властивість або метод існує у вказаному об’єкті. Наприклад:

console.log ("log" in console);//true
console.log ("Log" in console);//false

Перевірка відповідності типу

Оператор instanceof повертає true, якщо вказаний об’єкт належить до вказаного типу. Наприклад:

let now = new Date();                             
let now1 = {DT:"2020/04/15 20:15:00"}
console.log (now instanceof Date);  //true
console.log (now1 instanceof Date); //false

Перебір властивостей та методів (for..in)

Перебір усіх властивостей та методів проводиться через for..in

let obj = {a: 1, b: 2, c: 3};   
for (const prop in obj) {
  console.log(`obj.${prop} = ${obj[prop]}`);
}
// Виведе:
// "obj.a = 1"
// "obj.b = 2"
// "obj.c = 3"

Розкриття об’єкту (Object Spread, ...)

Синтаксис ... перед об’єктами в JavaScript використовується для розширення об’єктів (Object Spread). Він дозволяє копіювати властивості з одного об’єкта в інший об’єкт або об’єднувати властивості з кількох об’єктів в один об’єкт.

Основні використання синтаксису ... перед об’єктами:

1) Копіювання об’єкта: За допомогою ... можна швидко створити поверхневу копію об’єкта, копіюючи всі його властивості в новий об’єкт.

const obj1 = { x: 1, y: 2 };
const obj2 = { ...obj1 };
console.log(obj2); // { x: 1, y: 2 }

2) Об’єднання об’єктів: За допомогою ... можна об’єднати властивості з кількох об’єктів в один об’єкт.

javascript
const obj1 = { x: 1 };
const obj2 = { y: 2 };
const mergedObj = { ...obj1, ...obj2 };
console.log(mergedObj); // { x: 1, y: 2 }

3) Додавання нових властивостей: За допомогою ... можна додавати нові властивості до об’єкта.

javascript
const obj1 = { x: 1 };
const obj2 = { ...obj1, y: 2 };
console.log(obj2); // { x: 1, y: 2 }

Зверніть увагу, що синтаксис ... перед об’єктами доступний з ECMAScript 2018 (ES9) і пізнішими версіями JavaScript.

Перебір властивостей методом keys()

Метод Object.keys() використовується для отримання масиву, що містить імена всіх перераховуваних властивостей об’єкту. Ось приклад використання методу Object.keys():

const obj = { a: 1, b: 2, c: 3 };

const keys = Object.keys(obj);

keys.forEach(function(key) {
  console.log(key + ': ' + obj[key]);
});

У цьому прикладі ми спочатку використовуємо Object.keys(obj), щоб отримати масив, що містить імена всіх властивостей об’єкту obj. Потім ми використовуємо forEach() для ітерації по цьому масиву і виведення кожної властивості та її значення. Результат буде наступним:

a: 1
b: 2
c: 3

Цей підхід дозволяє перебрати усі перераховувані властивості об’єкту, незалежно від того, чи є вони власними властивостями об’єкту чи успадкованими. Зауважте, що метод Object.keys() повертає тільки перераховувані властивості, а не властивості, що не перераховуються (наприклад, властивості, що використовуються для зберігання внутрішніх станів об’єкту).

Об’єднання об’єктів

Є кілька способів об’єднати два об’єкти в JavaScript. Один з них - використання методу Object.assign(), який дозволяє об’єднати властивості з одного або кількох об’єктів в один об’єкт. Наприклад:

const obj1 = { a: 1, b: 2 };
const obj2 = { c: 3, d: 4 };
const obj3 = Object.assign({}, obj1, obj2);

console.log(obj3); // { a: 1, b: 2, c: 3, d: 4 }

У цьому прикладі ми створили два об’єкти obj1 і obj2, а потім об’єднали їх в один об’єкт obj3, використовуючи метод Object.assign(). Першим параметром методу є пустий об’єкт {}, який буде місцем призначення для об’єднаних властивостей. Потім ми передаємо об’єкти, які ми хочемо об’єднати, як наступні параметри методу.

Є й інші способи об’єднати об’єкти, такі як оператор spread (...) або методи Object.keys(), Object.values() та Object.entries(), але метод Object.assign() є простим і ефективним способом зробити це.

Але Object.assign() об’єднує властивості тільки на одному рівні. Для об’єднання об’єктів на всіх рівнях вкладеності можна використовувати рекурсію. Можна написати функцію, яка проходиться по всіх властивостях об’єкту та рекурсивно викликає себе для кожної властивості, що також є об’єктом.

Ось приклад функції, яка об’єднує два об’єкти на всіх рівнях вкладеності:

function mergeObjects(obj1, obj2) {
  // перебираємо всі властивості obj2
  for (let prop in obj2) {
    if (obj2.hasOwnProperty(prop)) {
      // якщо властивість є об'єктом, рекурсивно викликаємо mergeObjects
      if (typeof obj2[prop] === 'object') {
        // якщо властивість також є об'єктом, викликаємо mergeObjects
        if (typeof obj1[prop] === 'object') {
          obj1[prop] = mergeObjects(obj1[prop], obj2[prop]);
        } else {
          obj1[prop] = obj2[prop];
        }
      } else {
        obj1[prop] = obj2[prop];
      }
    }
  }
  return obj1;
}

Ця функція бере два об’єкти як аргументи, а потім перебирає всі властивості другого об’єкта. Якщо властивість є об’єктом, то викликає саму себе рекурсивно з відповідними властивостями першого та другого об’єкта. Якщо властивість не є об’єктом, то просто копіюємо її з другого об’єкта в перший. Значення властивостей другого об’єкта будуть переписувати властивості першого.

Наприклад, якщо ми маємо два об’єкти:

let obj1 = {
  a: {b: 1,c: 2},
  d: 3
};

let obj2 = {
  a: {b: 4, e: 5},
  f: 6
};

Тоді можемо об’єднати їх, викликавши функцію mergeObjects:

let result = mergeObjects(obj1, obj2);
console.log(result);

Результатом буде об’єкт:

{
  a: {b: 4, c: 2,e: 5},
  d: 3,
  f: 6
}

5. Основи роботи з масивами

Створення та використання масивів

У JavaScript немає явного типу даних для масиву. Для роботи з масивами можна використовувати заздалегідь заданий об’єкт Array та його методи.

Об’єкт Array має ряд властивостей, наприклад довжину (length) та методи маніпулювання масивами, такі як з’єднання(joining), реверсування(reversing), сортування та інші.

У масиві можна зберігати елементи різного типу. Створення масиву можна проводити кількома способами. Наступні операції створюють еквівалентні масиви:

let arr1 = new Array(3, '19', {a: 35});
let arr2 = Array(3, '19', {a: 35});
let arr3 = [3, '19', {a: 35}];

Для створення масиву з ненульовою довжиною, але без будь-яких елементів, може бути використане будь-яка з наведених нижче інструкцій:

let arLength = 3;
let arr1 = new Array(arLength); 
let arr2 = Array(arLength);
// Це має точно такий же ефект 
let arr3 = []; arr3.length = arLength;

На рівні реалізації масиви JavaScript фактично зберігають свої елементи як стандартні властивості об’єкта, використовуючи індекс масиву як ім’я властивості.

До елементу масиву звертаються за його індексом у квадратних дужках.

let fruits = ["Яблуко", "Апельсин", "Слива"];
console.log( fruits[0] ); 	// Яблуко
fruits[2] = 'Груша'; 		// зміна елементу
console.log (fruits );		//Array(3) ["Яблуко", "Апельсин", "Груша"]
console.log( fruits.length); // 3
fruits[3] = 'Лимон'; 		//добавлення нового елементу
console.log (fruits ); 		// Array(4) ["Яблуко", "Апельсин", "Груша", "Лимон"]
console.log( fruits.length); // 4

Властивість length завжди повертає індекс останнього елементу плюс один. У властивість length можна також записувати значення, що задає кількість елементів. Введення значення, коротшого за кількість збережених елементів, скорочує масив. Написання 0 спустошує масив повністю.

Для перебору елементів масиву можна використовувати операції for.

В JavaScript є спеціальний оператор ... (spread operator), який дозволяє розгорнути масив або інший ітерабельний об’єкт на окремі елементи. Це дозволяє легко комбінувати елементи з одного масиву з іншим масивом або додавати додаткові елементи в середину масиву.

Ось приклад використання оператора spread для з’єднання двох масивів:

const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combined = [...arr1, ...arr2];

console.log(combined); // [1, 2, 3, 4, 5, 6]

У цьому прикладі ми створили два масиви arr1 та arr2. За допомогою оператора spread ми розгорнули обидва масиви в окремі елементи та об’єднали їх в один новий масив combined. Результатом є новий масив, який містить елементи з arr1 та arr2.

Оператор spread також можна використовувати для передачі аргументів до функції:

function sum(a, b, c) {
  return a + b + c;
}

const numbers = [1, 2, 3];
const result = sum(...numbers);

console.log(result); // 6

Багатовимірні масиви

Можна створювати та працювати з багатовимірними масивами. Наступний код створює багатовимірний масив.

let a = new Array(4)
for (let i = 0; i < 4; i++) {
  a[i] = new Array(4)
  for (let j = 0; j < 4; j++) {
    a[i][j] = '[' + i + ', ' + j + ']'
  }
}
let ar1 = [[1,2,3],[4,5,6]];//двовимірний масив розмірність 2 на 3
console.log (ar1.length);   //2
console.log (ar1[0].length);//3
console.log (ar1[1]);       //Array(3) [4, 5, 6]
console.log (ar1[1][1]);    //5

Властивості та методи масивів

У таблиці нижче наведений перелік деяких методів та властивостей масивів. Повний перелік можна знайти за цим посиланням . Деякі з цих методів описані нижче.

Таблиця 2.1. Деякі з методів масивів.

Метод Призначення
concat поєднання масивів
copyWithin додає дрібну копію частини масиву в іншу позицію в тому ж масиві
entries повертає новий об’єкт ітератора масиву (Array Iterator), який містить пари ключ-значення для кожного індексу в масиві.
every перевіряє, чи всі елементи масиву відповідають умові, що задана функцією, яка передається як аргумент
fill заповнює (змінює) всі елементи масиву з початкового індексу до кінцевого статичним значенням
filter створює новий масив з усіма елементами, що пройшли перевірку вказаною функцією
find повертає значення першого елемента в масиві, що задовільняє передану функцію тестування
findIndex повертає індекс першого елемента у масиві, який задовольняє надану перевірочну функцію
flat створює новий масив який містить всі елементи вкладених масивів до вказаної глибини
flatMap аналогічно послідовному виклику map() та flat() з глибиною 1
forEach виконує надану функцію один раз для кожного елемента масиву
from створює новий екземпляр Array з подібного до масиву або ітерабельного об’єкта
includes з’ясовує, чи масив містить елемент із вказаним значенням
indexOf повертає перший індекс, за яким даний елемент був знайдений в масиві
isArray з’ясовує, чи є передане значення є масивом
join створює та повертає рядок, що об’єднує всі елементи масиву
keys вертає новий об’єкт перебирача ключів (індексів) масиву
lastIndexOf повертає останній індекс, за яким заданий елемент було знайдено у масиві
length встановлює або повертає кількість елементів у цьому масиві
map створює новий масив з результатами виклику наданої функції на кожному елементі масиву
of створює новий екземпляр Array з заданої кількості аргументів
pop видаляє останній елемент масиву та повертає цей елемент
prototype Дозволяє додавати властивості до масивів.
push додає один або більше елементів у кінець масиву та повертає нову довжину масиву
reduce виконує вказану функцію для кожного елемента масиву та повертає єдине значення
reduceRight застосовує функцію до акумулятора та кожного елемента масиву (справа наліво), зменшуючи його до єдиного значення
reverse транспонує масив , змінюючи послідовність елементів на протилежну
shift видаляє перший елемент з масиву і повертає цей елемент
slice повертає дрібну копію частини масиву у новий масив
some з’ясовує, чи містить масив хоч один елемент, для якого зазначена функція
sort відсортовує елементи масиву
splice змінює вміст масиву, видаляючи існуючі та/або додаючи нові елементи
toLocaleString повертає рядок, що відображає елементи масиву
toSource повертає рядкове представлення першокоду масиву
toString повертає рядкове представлення заданого масиву та його елементів
unshift додає один або декілька елементів на початок масиву
values повертає новий об’єкт ітератора масиву (Array Iterator), який містить значення кожного елемента масиву

Перебір елементів for..of

Окрім for, інструкції For..of перебирають елементи в ітерабельних (перелічувальних) змінних (масивах, рядках) .

let fruits = ["Яблуко", "Апельсин", "Слива"];
for (let fruit of fruits) {
  console.log (fruit);
}
let iterable = 'ой';
for (let value of iterable) {
  console.log(value);
}

Перебір елементів за допомогою методу forEach()

Метод forEach() в JavaScript використовується для ітерації (перебору) елементів масиву і виклику заданої функції з кожним елементом масиву. Синтаксис методу forEach() виглядає наступним чином:

array.forEach(function(element, index, array) {
  // код для обробки кожного елемента
});

У цьому синтаксисі:

Функція, передана в метод forEach(), викликається для кожного елемента масиву один раз. Вона може містити код для обробки або виконання певних дій з кожним елементом масиву. Ось невеликий приклад використання методу forEach():

const array = [1, 2, 3, 4, 5];
array.forEach(function(element) {
  console.log(element);
});

В цьому прикладі кожен елемент масиву буде виведений на консоль. Результатом буде:

1
2
3
4
5

Метод forEach() виконує ітерацію по всіх елементах масиву в порядку їх послідовності. Він не повертає новий масив, а просто виконує дії з кожним елементом. Зауважте, що метод forEach() не може бути припинений раніше, тобто немає можливості використовувати break або return для припинення його виконання. Якщо вам потрібно припинити ітерацію, вам може знадобитися використовувати цикл for або while з відповідними умовами перевірки

Видалення та вставлення елементів

Оператор delete може видалити елемент за вказаним індексом у масиві. Наприклад:

let fruits = ["Яблуко", "Апельсин", "Слива"];
delete fruits[1];
console.log( fruits);   // Array(3) ["Яблуко", …, "Слива"]
console.log( fruits[1]);//undefined

Зверніть увагу, що кількість елементів в масиві залишилася незмінною, а елемент з індексом 1 став undefined.

Масиви в JavaScript підтримують методи роботи як з чергами, так і з стеками:

let fruits = ["Яблуко", "Апельсин", "Груша"]; 
fruits.pop(); console.log(fruits); 		// Array(2) ["Яблуко", "Апельсин"]
fruits.push("Груша"); console.log(fruits); // Array(3) ["Яблуко", "Апельсин", "Груша"] 
fruits.shift(); console.log(fruits); 		//Array(2) ["Апельсин", "Груша"]  
fruits.unshift("Яблуко"); console.log(fruits);//Array(3) ["Яблуко", "Апельсин", "Груша"] 

рис.2.6. Порівняння швидкості роботи методів добавлення та видалення елементів з масивів.

Можна добавляти кілька елементів

let fruits = ["Яблуко"];
fruits.push("Апельсин", "Груша");
fruits.unshift("Ананас", "Лимон");
console.log(fruits);	//Array(5) ["Ананас", "Лимон", "Яблуко", "Апельсин", "Груша"]

Слід звернути увагу на те, що методи push/pop виконуються швидше ніж shift/unshift.

Якщо необхідно видалити елемент масиву в середині масиву, можна скористатися його методами: splice) або slice.

Метод splice змінює вміст масиву, видаляючи існуючі та/або додаючи нові елементи.

//Видалення 2-ох елементів, починаючи з індексу 2
let myFish = ['parrot', 'anemone', 'blue', 'trumpet', 'sturgeon'];
let removed = myFish.splice(2, 2);
console.log (myFish);// ["parrot", "anemone", "sturgeon"] 
console.log (removed);// ["blue", "trumpet"]

//Видалення 0 елементів, починаючи з індексу 2, і вставлення "drum"
myFish = ['angel', 'clown', 'mandarin', 'sturgeon'];
removed = myFish.splice(2, 0, 'drum');
console.log (myFish);// ["angel", "clown", "drum", "mandarin", "sturgeon"] 
console.log (removed);// нічого не виведе, жодний елемент не було видалено

Метод slice повертає дрібну копію частини масиву у новий масив, починаючи з begin і до end (не включаючи end), де begin та end є індексами елементів масиву. Початковий масив не змінюється.

Перетворення масиву в масив за вказаним правилом (map)

Метод map() є одним з найбільш корисних методів масивів у JavaScript. Він створює новий масив, використовуючи результати виклику зазначеної функції з кожним елементом вихідного масиву.

Синтаксис методу map() виглядає наступним чином:

array.map(function(currentValue, index, array) {
  // повертає елементи для нового масиву
})

Цей метод приймає функцію, яка викликається для кожного елемента вхідного масиву. Функція повинна повертати значення для нового масиву. Опціональні параметри функції - це значення поточного елемента (currentValue), індекс поточного елемента (index) та вихідний масив (array).

Приклад використання методу map():

const numbers = [1, 2, 3, 4, 5];

const doubledNumbers = numbers.map(function(num) {
  return num * 2;
});

console.log(doubledNumbers); // [2, 4, 6, 8, 10]

У цьому прикладі ми маємо масив чисел numbers. Ми викликаємо метод map() на цьому масиві і передаємо функцію, яка повертає кожен елемент, помножений на 2. Результатом є новий масив зі значеннями [2, 4, 6, 8, 10], що містить усі елементи вихідного масиву, помножені на 2.

Перевірка на наявність елементу (some)

Метод some є вбудованим методом масиву і використовується для перевірки, чи задовольняє хоча б один елемент масиву певному умовному виразу. Синтаксис методу some виглядає наступним чином:

array.some(callback(element[, index[, array]])[, thisArg])

Метод some повертає булеве значення true, якщо хоча б один елемент масиву задовольняє умову виразу в callback. В іншому випадку, якщо жоден елемент не задовольняє умову, повертається false.

Отримання результату методу some можна використовувати для прийняття рішень, перевірки умови наявності певного елемента в масиві або фільтрації масиву за певною умовою.

Звичайний приклад використання методу some може виглядати так:

const numbers = [1, 2, 3, 4, 5];

// Перевіряємо, чи є хоча б одне парне число в масиві
const hasEvenNumber = numbers.some(num => num % 2 === 0);

console.log(hasEvenNumber); // true

У цьому прикладі ми використовуємо метод some, щоб перевірити, чи є хоча б одне парне число в масиві numbers. Функція num => num % 2 === 0 виконує перевірку на парність чисел. Оскільки у масиві є число 2, яке задовольняє умову, метод some повертає значення true.

Метод filter

Метод filter використовується для створення нового масиву, в якому знаходяться елементи вихідного масиву, що відповідають певному умовному виразу. Синтаксис методу filter:

const newArray = array.filter(callback(element, index, array));

Де:

Функція callback повертає true для елементів, які мають бути включені у новий масив, і false для елементів, які мають бути виключені.

Ось приклад використання методу filter:

const numbers = [1, 2, 3, 4, 5];

// Фільтруємо парні числа
const evenNumbers = numbers.filter(num => num % 2 === 0);

console.log(evenNumbers); // [2, 4]

У цьому прикладі ми використовуємо метод filter, щоб створити новий масив evenNumbers, який містить лише парні числа з вихідного масиву numbers. Функція num => num % 2 === 0 виконує перевірку на парність чисел. Як результат, новий масив evenNumbers містить елементи [2, 4], оскільки ці числа задовольняють умову.

6. Додаткові можливості функцій

Функції-стрілки =>

Вище було розглянуто два синтаксиса створення функцій - шляхом оголошення та за допомогою функціонального виразу. Ще одним синтаксисом створення функцій подібно до функціонального виразу є «функції-стрілки» або «стрілочні функції» (arrow functions).

let sum1 = (a, b) => a + b;				//стрілочна функція
let sum2 = function(a, b) {return a + b};//аналогічна форма через функціональний вираз
console.log(sum1(1, 2) ); // 3
console.log(sum2(1, 2) ); // 3

При використанні одного аргументу, запис буде ще коротше:

let double1 = function(n) { return n * 2 }
let double2 = n => n * 2; //анаогічний попередньому

Якщо немає аргументів, це матиме вигляд:

let sayHi = () => console.log("Hello!");

Такі функції зручні для простих однорядкових дій. Однак вони мають додаткові можливості, які наразі розглядати не будемо.

Залишкові параметри та arguments

Якщо кількість аргументів може варіюватися, тобто якісь з аргументів не є обов’язковими, можна скористатися залишковими параметрами. Залишкові параметри (rest parameters) - це представлення усіх фактичних параметрів при виклику функції у вигляді одного масиву. При означенні функції, ці параметри вказуються після ... і якщо використовуються, повинні бути в кінці списку параметрів. Після залишкових параметрів не можна вказувати інші.

function showName(firstName, lastName, ...titles) { //titles - масив залишкових параметрів
  console.log( firstName + ' ' + lastName ); // Юлій Цезарь
  // Інші параметри підуть в масив
  // titles = ["Консул", "Імператор"]
  console.log( titles[0] ); // Консул
  console.log( titles[1] ); // Імператор
  console.log( titles.length ); // 2
}
showName("Юлій", "Цезарь", "Консул", "Імператор");

Усі аргументи функції знаходяться в псевдо-масиві arguments під своїми порядковими номерами. Зрештою, при оголошенні функції, аргументи можна взагалі не вказувати і перебирати їх через arguments.

function showName() { //формальні параметри не вказуються
  console.log (arguments.length); //4
  for (let arg of arguments)
  { 
  console.log(arg); // послідовно буде виводити усі аргументи
  }
}
showName("Юлій", "Цезарь", "Консул", "Імператор");

Функції зворотного виклику та асинхронність

Багато функцій JavaScript та його середовищ виконання одразу повертають результат, ще до того як вона виконала цільову дію. Це корисно тоді, коли завершення обробки функції займає багато часу, яке при очікуванні буде гальмувати виконання програми.

Наприклад, функція setTimeout повинна виконати якусь дію через вказаний інтервал часу. Для того щоб вказати цю дію, вона записується як інструкції в функції, яка буде викликатися через цей інтервал. Наприклад:

let fn = function () {console.log ("пройшло 2 секунди")};
console.log ("Запускаємо таймер");
setTimeout(fn, 2000);
console.log ("Таймер запущено, чекаємо...");

У даному прикладі, функція fn містить інструкцію, яку необхідно виконати через 2 секунди. При виклику setTimeout ця функція передається в якості аргументу разом з інтервалом (задається в мілісекундах). Замість того, щоб чекати 2 секунди, функція setTimeout одразу обробляється і передає керування наступній інструкції, що йде за нею. У цей час десь на рівні ОС запускається окремий програмний потік, задача якого відслідкувати спрацювання таймеру. Однак цей потік не зупиняє виконання програми JavaScript, вона виконується далі. Коли системний потік з таймером виявить, що час пройшов, він викличе функцію fn.

Таким чином наведена вище програма спочатку виведе повідомлення “Запускаємо таймер”, потім “Таймер запущено, чекаємо…” і через 2 секунди “пройшло 2 секунди”. Така функція, яка запускається на виконання, одразу віддає керування потоку, що її визвав, ще до завершення результату називається асинхронною. Функція, яка передається в якості аргументу для її виклику називається функцією зворотного виклику (колбек, callback). У наведеному вище прикладі функція setTimeout є асинхронною, а fn - функцією зворотного виклику.

Наведений вище код з використанням функцій стрілок може виглядіти наступним чином:

console.log ("Запускаємо таймер");
setTimeout(() => console.log ("пройшло 2 секунди"), 2000);
console.log ("Таймер запущено, чекаємо...");

Асинхронно також працює функція setInterval, яка запускає функцію, з вказаною періодичністю:

let fn = function () {console.log ("пройшло 2 секунди")};
console.log ("Запускаємо таймер");
setInterval(fn, 2000);
console.log ("Таймер запущено, чекаємо...");

Асинхронність є дуже корисною характеристикою тих функцій, які працюють з зовнішніми засобами. Якщо дані необхідно кудись записати, скільки це займе часу. Нижче наведений приклад, де показані в тактах ЦПУ час, що потребується для цього (числа умовні, але порядок буде такий самий).

Таблиця 14.1. Порівняльна таблиця часу, який необхідний для виконання різних операцій.

Операція запису в Кількість тактів CPU (приблизно)
CPU Registers 3 такти
L1 Cache 8 тактів
L2 Cache 12 тактів
RAM 150 тактів
Диск 30,000,000 тактів
Мережа 250,000,000 тактів

Якщо б операції доступу (читання/запису) по мережі до даних реалізовувалися через синхронні функції, програмний потік JavaScript просто б постійно зависав. Замість цього асинхронна функція запускається з передачею функції зворотного виклику і програма далі працює. У цей час ОС виконує необхідну операцію. Як тільки вона буде оброблена, буде викликана функція зворотного виклику.

Створення тіла функції через New

Конструктор Function створює новий об’єкт Function. Прямий виклик конструктора може створювати функції динамічно, але має проблеми з безпекою та схожі з eval (але менш значні) проблеми з продуктивністю. Однак, на відміну від eval, конструктор Function створює функції, які виконуються тільки у глобальній області видимості.

new Function (arg1, arg2, ...argN,functionBody)
//у змінній strfn знаходиться код функції
let strfn = "let y = a+b+c; console.log ('Значення суми = ' + y + '!')";
let fno = new Function ("a","b","c", strfn);
fno (100,10,1);    //Значення суми = 111!

Додатково про це можна прочитати за цим або за цим посиланням.

Додатково про властивості і методи функцій як об’єктів наведено нижче.

7. Об’єкти

На минулій лекції були коротко розглянуті призначення об’єктів, їх створення через літерал та робота з властивостями та методами. Тут продовжимо розглядати об’єкти та їх призначення.

Методи об’єктів

Окрім властивостей об’єктів, які надають можливість доступитися до певного його параметру, можна користуватися певними діями, які може робити об’єкт. Такі дії в JavaScript представлені властивостями-функціями об’єкта.

let user = {
  name: "Джон",
  age: 30
};
user.sayHi = function() {
  console.log("Привіт!");
};
user.sayHi(); // Привіт!

У наведеному вище прикладі властивості user.sayHi об’єкта був присвоєний функціональний вираз (Function Expression). Тепер цю властивість можна використовувати для виклику функції. Властивості-функції об’єкту називають методом цього об’єкту.

Альтернативний спосіб через оголошення функції матиме вигляд:

let user = {
  name: "Джон",
  age: 30
};
// спочатку оголошуємо
function sayHi() {
  console.log("Привіт!");
};
// потім добалвяємо в якості метода
user.sayHi = sayHi;
user.sayHi(); // Привіт!

Існують також короткі синтаксиси для методів при створенні об’єктів через літерал:

let user1 = {
  sayHi: function() { 		//метод створюється при створенні об'єкту 
    console.log("Привіт!");
  }
};
let user2 = {
  sayHi() { 				// те саме, що і "sayHi: function()"
    console.log("Привіт!");
  }
};
user1.sayHi();user2.sayHi();

До методів об’єктів також можна доступатися не тільки через крапку, але і через прямокутні дужки, адже це також властивості. Це дає можливість вказувати метод через змінну, наприклад:

let user = {
  sayHi() {console.log("Привіт!")}
};
let method = "sayHi";
user[method]();

Слід звернути увагу, що звернення до методу без дужок, значить звернення до самої функції, а не її виклик, наприклад.

let user = {
  sayHi() {console.log("Привіт!")}
};
let method = user.sayHi; //назначаємо змінній функцію 
method(); 				//викликаємо функцію  - Привіт!
console.log (user.sayHi);//sayHi() { … }
console.log (method);	//sayHi() { … }

Ключове слово this

Як правило, методу об’єкта необхідний доступ до інформації, яка зберігається в об’єкті, щоб виконати з нею які-небудь дії (у відповідності з призначенням методу). Наприклад, коду в об’єкті необхідний доступ до інформації, яка зберігається в тому ж об’єкті, щоб виконати з нею які-небудь дії (у відповідності з призначенням методу). Наприклад, коду всередині user.sayHi() може знадобитися ім’я користувача, яке також зберігається в цьому об’єкті.

Для того щоб звернутися до свого ж об’єкту, використовується ключове слово this. Наприклад,

let user = {
  name: "Джон",
  age: 30,
  sayHi() {
    // this - це "плинний об'єкт"
    console.log("Привіт, мене звати " + this.name + " !");
  }
};
user.sayHi(); // Привіт, мене звати Джон !

Тут під час виконання user.sayHi() значеннямthis буде user (посилання на об’єкт user). Можна також звертатися безпосередньо до об’єкту user, але назва об’єкту може бути різною, а нас цікавить саме цей об’єкт, в якому викликається метод.

Значення this розраховується під час виконання і залежить від контексту.

Конструктори об’єктів

Літеральний синтаксис {...} дозволяє створювати один унікальний об’єкт. Але часто необхідно створити багато однотипних об’єктів. Це можна робити з використанням функції-конструктора і ключового слова new.

Функції-конструктори призначені для побудови об’єктів. До них є певні вимоги:

Коли функція викликається як new User(...), відбувається наступне:

  1. Створюється новий порожній об’єкт, який присвоюється this
  2. Виконується код функції. Зазвичай він модифікує this, добавляє туди нові властивості
  3. Повертається значення this

Іншими словами, виклик new User(...) робить приблизно наступне:

function User(name) {
  // this = {};  (неявно)
  // добавлення властивостей, методів і додаткові дії інціалізації і т.п.  
  this.name = name;
  this.isAdmin = false;
  // return this;  (неявно)  
}
let user = new User("Вася");
console.log(user.name); // Вася
console.log(user.isAdmin); // false

Таким чином, результат виклику new User("Вася") буде той самий об’єкт, що і

let user = {
  name: "Вася",
  isAdmin: false
};

Однак, на відміну від літерального способу, створення об’єктів з такими самими властивостями тепер зводиться до виклику функції-конструктора, а не до повторення літералів.

// літеральний
let user1 = {name: "Вася",isAdmin: false};// для кожного користувача повний перелік властивостей
let user2 = {name: "Міша", isAdmin: false};

//через конструктор 
function User(name) { this.name = name; this.isAdmin = false} // один конструктор
let user3 = new User("Вася");//створення властивостей для кожного користувача через виклик конструктору 
let user4 = new User("Міша");

Не важко помітити, що коли кількість властивостей та об’єктів зростає, використання конструктору значно спрощує створення однотипних об’єктів. Функція-конструктор призначена і використовується саме для створення об’єктів.

Якщо в функції-конструкторі повертається значення через return це повинно бути об’єктною змінною, інакше буде повернено this.

У конструкторі можна також створювати методи.

function User(name) {
  this.name = name;
  this.sayHi = function() {console.log( "Мене звати " + this.name )};
}
let user1 = new User("Вася");
user1.sayHi(); // Мене звати: Вася

Для складних об’єктів є ще один синтаксис - класи, які у даному курсі не будуть розглядатися.

Object

Об’єкт Object є вбудованим типом даних у JavaScript, який дозволяє зберігати дані у вигляді пар ключ-значення. Він має багато методів та властивостей, які можуть бути використані для роботи з об’єктами. Ось кілька найбільш вживаних методів та властивостей об’єкта:

  1. Object.keys(obj): Повертає масив, що містить всі ключі (властивості) об’єкта obj.
  2. Object.values(obj): Повертає масив, що містить всі значення властивостей об’єкта obj.
  3. Object.entries(obj): Повертає масив, що містить всі пари ключ-значення властивостей об’єкта obj.
  4. Object.assign(target, source): Копіює властивості з одного або кількох джерел (source) до цільового об’єкта (target), повертаючи змінений цільовий об’єкт.
  5. Object.hasOwnProperty(prop): Перевіряє, чи має об’єкт властивість з ім’ям prop. Повертає true, якщо така властивість існує безпосередньо в об’єкті, і false - в іншому випадку.
  6. Object.create(proto): Створює новий об’єкт з вказаним прототипом proto.
  7. Object.defineProperty(obj, prop, descriptor): Додає або змінює властивість prop об’єкта obj, використовуючи вказаний дескриптор descriptor.
  8. Object.freeze(obj): Заморожує об’єкт obj, що робить його властивості недоступними для зміни або видалення.
  9. Object.seal(obj): Запечатує об’єкт obj, що дозволяє змінювати значення властивостей, але не дозволяє додавати нові або видаляти існуючі властивості.
  10. Object.getPrototypeOf(obj): Повертає прототип об’єкта obj.

Функція Object.values(x) використовується для отримання масиву, що містить значення властивостей об’єкта x. Вона повертає новий масив, який містить значення у тому ж порядку, в якому вони перераховані в об’єкті.

Наприклад, розглянемо наступний об’єкт:

const person = {
  name: 'John',
  age: 30,
  profession: 'Engineer'
};
const values = Object.values(person);
console.log(values);
// Вивід: ['John', 30, 'Engineer']

8. Вбудовані об’єкти JavaScript

У JavaScript є багато вбудованих об’єктів. У цьому розділі розглянемо кілька з них.

Об’єкт Number

Вбудований об’єкт Number представляє об’єкт-обгортку для змінних та констант однойменного типу. Будь які змінні та літерали можна використовувати як об’єкт Number. Наприклад, можна звернутися до методу toString літералу:

console.log ((255).toString(16)); // ff - 16-ковий формат
console.log ((0xFF).toString()); // 255 - 10-ковий формат

Об’єкт вміщує ряд властивостей, такі як масимальне числове значення, не-число(NaN) та безкінечність (infinity) та інші. У таблиці нижче деякі з них:

Таблиця 14.2. Властивості об’єкту Number

Властивість Опис
Number.NaN Спеціальне значення “не-число”
Number.NEGATIVE_INFINITY Спеціальне від’ємне безкінечне число; повертається при переповненні
Number.POSITIVE_INFINITY Спеціальне додатне безкінечне число; повертається при переповненні

Ви не можете змінювати значення цих властивостей, але можете використовувати їх для читання.

Об’єкт Number також надає множину методів для роботи з числами, зокрема:

Таблиця 14.3. Методи об’єкту Number

Метод Опис
Number.parseFloat() Приймає рядок в якості аргументу та повертає число з плаваючою комою, яке вдалося розпізнати. Аналог глобальній функції parseFloat().
Number.parseInt() Приймає рядок в якості аргументу та поверає ціле число в заданій системі числення, якщо аргумент вдалося розпізнати. Аналог глобальній функції parseInt().
Number.isFinite() Визначає, чи передане значення є кінцевим числом.
Number.isInteger() Визначає, чи передане число є цілим.
Number.isNaN() Визначає, чи передане число є NaN. Більш надійніша версія оригінальної глобальної функції isNaN().
Number.isSafeInteger() Визначає, чи передане значення є числом, що є безпечним цілим.
Nuber.toString(radix) Вертає рядкове предствлення числа у вказаній у дужках (radix) системі числення
let a=0xF; //16-ковий формат
console.log (a.toString()); // 15 - 10-ковий формат
console.log (a.toString(2)); // 1111 - двійковий формат
console.log (a.toString(16)); // f - 16-ковий формат

Прототип Number надає методи для отримання інформації з об’єкту Number в різноманітних форматах. Наступній таблиці представлені методи з Number.prototype.

Таблиця 14.4. Методи об’єкту Number.prototype.

Методи Опис
toExponential(fractionDigits) Повертає рядок, що представляє число в експоненціальній нотації з fractionDigits цифр після коми
toFixed(digits) Повертає рядок, що представляє число з плаваючою комою з digits цифр після коми
toPrecision(precision) Повертає рядок, що представляє число із заданою точністю у позначені плаваючої коми з precision кількістю цифр.
let numObj = 77.1234;
console.log(numObj.toExponential());  //'7.71234e+1'
console.log(numObj.toExponential(2)); //'7.71e+1'
console.log(numObj.toFixed());       // '77'
console.log(numObj.toFixed(1));      // '77.1'
console.log(numObj.toPrecision())    // '77.1234'
console.log(numObj.toPrecision(5))   // '77.123'
console.log(numObj.toPrecision(2))   // '77'
console.log(numObj.toPrecision(1))   // '8e+1'

Об’єкт Math

Вбудований об’єкт Math має властивості та методи для математичних констант та функцій.

На відміну від інших глобальних об’єктів, Math() не є конструктором. Тобто необхідно використовувати безпосередньо методи об’єкту Math.

let a = new Number();//так можна робити
console.log (a.toString());//0
let b = new Math(); // помилка Math is not a constructor

Всі поля і методи Math статичні. Тобто до сталої Пі потрібно звертатись Math.PI, а функцію синуса викликати через Math.sin(x), де x є аргументом статичного методу. Всі константи задані із максимальною для дійсних чисел у JavaScript точністю.

Нижче наведені кілька властивостей та методів, усі інші можна подивитися у довіднику Через властивості доступні деякі константи, зокрема, які наведені в таб.14.5.

Таблиця 14.5. Константи Math.

Властивість Опис
Math.E Стала Ейлера, основа натуральних логарифмів. Приблизно дорівнює 2.718.
Math.PI Значення відношення довжини кола до його діаметру, наближено дорівнює 3.14159.
Math.SQRT2 Значення квадратного кореня від 2, наближено 1.414.
let a = Math.E;
console.log (a);	//2.718281828459045

Математичні функції представлені через методи, які наведені у таблиці 14.6:

Таблиця 14.6. Методи об’єкту Math.

Метод Опис
Math.asin(x) Повертає арксинус числа.
Math.sin(x) Повертає значення синуса аргументу. Через аналогічні методи доступні усі тригонометричні функції
Math.abs(x) Повертає абсолютне значення (модуль) числа.
Math.sqrt(x) Повертає додатне значення квадратного кореня від аргументу.
Math.cbrt(x) Повертає кубічний корінь числа.
Math.pow(x, y) Повертає результат піднесення x до степеня y.
Math.ceil(x) Повертає число, округлене “до більшого”.
Math.floor(x) Повертає результат округлення “до меншого”, тобто відкидує дробову частину.
Math.round(x) Повертає значення аргументу, округлене до найближчого цілого.
Math.trunc(x) Повертає цілу частину аргументу, відкидаючи всю дробову частину.
Math.log10(x) Повертає логарифм за основою 10 від аргументу.
Math.random() Повертає псевдовипадкове число з-поміж 0.0 і 1.0.
Math.sign(x) Повертає знак поданого числа. Визначає, чи являється аргумент додатним числом (вертає 1), від’ємним (вертає -1), чи дорівнює 0 (вертає 0).

Варто зазначити, що тригонометричні функції (sin(), asin() ..) очікують або повертають значення у радіанах. Для конвертації достатньо пам’ятати, що 1 кутовий градус - це (Math.PI / 180) радіан.

console.log (Math.random());        //випадкове число від 0 до 1
console.log (Math.sin(Math.PI/10));//0.3090169943749474
console.log (Math.sign(-15.34));    //-1

Об’єкт Date

JavaScript не має окремого типу даних для збереження дат. Тим не менше, для роботи з датою та часом можна використовувати об’єкт Date та його методи . Він має велику кількість методів для встановлення, отримання та маніпулювання датами, але не має жодних властивостей.

JavaScript зберігають дату, як кількість мілісекунд, що минули з 00:00:00 1 січня 1970 року. Значення в часу в мілісекундах також називається TimeStamp.

Об’єкт Date має окремі методи для підтримки UTC (всесвітній час) та місцевого часу. UTC (узгоджений всесвітній час) означає час встановлений світовим стандартом. Натомість місцевий час — це час того комп’ютера, на якому виконується код JavaScript.

Для того, щоб створити власний екземпляр об’єкту Date можна викликати однойменний конструктор:

let dateObjectName = new Date([parameters]);

де dateObjectName ім’я змінної в яку присвоюється створене значення з типом Date; це може бути як новий об’єкт, або як властивість існуючого об’єкту.

parameters (в наведеному вище синтаксисі) може бути представлений одним з наступних значень:

new Date();//Пусто: створюється сьогоднішня дата та час. 
new Date(value);//кількість мілісекунд з 1970 - TimeStamp 
new Date(dateString);//Набір цілих значень для року, місяця і дня
new Date(year, month[, day[, hours[, minutes[, seconds[, milliseconds]]]]]); //Набір цілих значень для року, місяця, дня, години, хвилини та секунди.

value — ціле число, що вказує кількість мілісекунд з 1 січня 1970 року 00:00:00 за UTC без врахування високосних секунд. Це те саме, що час Unix, але зважайте на те, що більшість функцій часу й дати Unix рахують у секундах.

dateString — Рядок, що вказує дату й час. Має бути у форматі, що розпізнається методом Date.parse() (IETF-compliant RFC 2822 timestamps, і також різновид ISO8601).

Зверніть увагу, що month задається від 0 (січень) до 11 (грудень)

Приклад:

let DT1 = new Date(); //теперішня дата
let DT2 = new Date(1587227373000);//TimeStamp  
console.log (DT2);  //Sat Apr 18 2020 19:29:33 GMT+0300 (GMT+03:00)
let DT3 = new Date("2020, 4, 18 19:29:33");  
console.log (DT3);  //Sat Apr 18 2020 19:29:33 GMT+0300 (GMT+03:00)
let DT4 = new Date(2020, 3, 18, 19, 29, 33); // 3 - Квітень, бо 0 - січень  
console.log (DT4);  //Sat Apr 18 2020 19:29:33 GMT+0300 (GMT+03:00)

Виклик Date як функції, тобто без оператора new, повертає теперішню дату та час як рядок (string).

console.log (Date());	//виведе плинну дату та час

Наступні методи працюють для глобального об’єкту Date, але не для об’єктів створених за допомогою конструктора Date.

Таблиця 14.7. Методи об’єкту Date.

Метод Опис
Date.now() Вертає ціле число, що позначає поточний час — кількість мілісекунд від 00:00:00 за UTC 1 січня 1970 року без врахування високосних секунд.
Date.parse() Розбирає текстовий запис (рядок) із датою (часом) та повертає кількість мілісекунд між 00:00:00 за UTC 1 січня 1970 та зазначеною миттю у часі. Високосні секунди не враховуються.
Date.UTC() Приймає ті самі параметри, що й найдовша форма конструктора (від 2 до 7), та вертає кількість мілісекунд між 00:00:00 1 січня 1970 року за UTC та зазначеною миттю у часі без врахування високосних секунд.
tsDT1 = Date.now();
console.log (tsDT1);         //1587225137339 
tsDT2 = Date.parse("2020, 4, 18 19:29:33");
console.log(tsDT2);          //1587227373000
tsDT3 = Date.UTC(2020, 3, 18, 19, 29, 33);
console.log (tsDT3);         //1587238173000
console.log ((tsDT3-tsDT2)/(60*60*1000));//3 - години різниці

Методи об’єкта Date для роботи з датами та часом діляться на наступні категорії::

За допомогою методів групи get та set можна встановлювати секунди, хвилини, години, дні місяця, дні тижнів, місяці та роки окремо. Слід звернути увагу на метод getDay, який повертає день тижня, але не існує відповідного методу setDay, оскільки день тижня встановлюється(вираховується) автоматично. Всі ці методи використовують цілі числа для представлення відповідних даних, як показано нижче:

Методи get наведені в наступній таблиці:

Таблиця 14.8. Методи get об’єкту Date.

Метод Опис
getFullYear(), getMonth(), getDate(), getDay(), getHours(), getMinutes(), getSeconds(), getMilliseconds() отримання відповідно року, місяця, дати, дня тижня, години, хвилини, секунди, мілісекунди
getTime() отримання TimeStamp
getUTC те саме що і get тільки для часу UTC
getTimezoneOffset() зміщення в хвилинах місцевої часової зони, що налаштована в пристрої (ПК) відносно UTC
let DT1 = new Date("2020, 4, 18 19:29:33");
console.log (DT1.getFullYear());//2020
console.log (DT1.getMonth());   //3 - квітень, бо 0 -січень
console.log (DT1.getDate());    //18
console.log (DT1.getDay());     //6 - субота (0 - неділя)
console.log (DT1.getTime());    //1587227373000 - TimeStamp
console.log (DT1.getHours());   //19
console.log (DT1.getMinutes()); //29
console.log (DT1.getSeconds()); //33
console.log (DT1.getTimezoneOffset());//-180 - 3 години до UTC

Методи set наведені в наступній таблиці

Таблиця 14.9. Методи set об’єкту Date.

Метод Опис
setFullYear(), setMonth(), setDate() , setHours(), setMinutes(), setSeconds(), setMilliseconds() встановлення відповідно року, місяця, дати, години, хвилини, секунди, мілісекунди
setTime() встановлення часу через мілісекунди (TimeStamp)
Date.prototype.setUTC те саме що і set але для часу UTC
let DT1 = new Date(0); console.log (DT1);  //Jan 01 1970 03:00:00
DT1.setFullYear (2020);//можна DT1.setFullYear (2020, 3, 18) щоб задати повністю дату
DT1.setMonth(3);//3 - квітень, бо 0-січень;можна DT1.setMonth(3, 18), щоб задати ще і дату
DT1.setDate(18); 
DT1.setHours(19);//години, можна DT1.setHours(19,29,33) щоб задати повінстю час 
DT1.setMinutes(29);//хвилини, можна DT1.setMinutes(29,33) щоб задати ще хвилини
DT1.setSeconds(33);//секунди, можна DT1.setSeconds(33,500), щоб задати мс 
console.log (DT1);                          //Apr 18 2020 19:29:33
DT1.setTime(1000);                          //1587227373000 - TimeStamp
console.log (DT1);                          //Jan 01 1970 03:00:01

Наступні методи перетворюють дату в один з форматів:

Таблиця 14.10. Методи to об’єкту Date.

Метод Опис
toString() перетворює в строкове представлення дати та часу в форматі ECMA-262
toDateString(), toTimeString() перетворює в строкове представлення відповідно дати та часу в форматі перетворює в строкове представлення дати та часу в форматі ECMA-262
toISOString() перетворює в строкове представлення дати та часу в форматі ISO 8601
toJSON() перетворює в строкове представлення дати та часу аналогічно попередньому
toLocaleString() перетворює в строкове представлення дати та часу відповідно до національних налаштувань (задаються в аргументах)
toLocaleTimeString(), toLocaleDateString() перетворює в строкове представлення відповідно дату та час до національних налаштувань (задаються в аргументах)
toUTCString() перетворює в строкове представлення UTC
valueOf() перетворення в примітивний тип, тобто в TimeStamp
let DT1 = new Date("2020, 4, 18 19:29:33");
console.log (DT1.toString());       //Sat Apr 18 2020 19:29:33 GMT+0300 (GMT+03:00)
console.log (DT1.toDateString());   //Sat Apr 18 2020
console.log (DT1.toTimeString());   //19:29:33 GMT+0300 (GMT+03:00)
console.log (DT1.toLocaleString("en-US")); //4/18/2020, 7:29:33 PM
console.log (DT1.toLocaleString()); //2020-4-18 19:29:33
console.log (DT1.toLocaleDateString());//2020-4-18
console.log (DT1.toLocaleTimeString());//19:29:33
console.log (DT1.toISOString());    //2020-04-18T16:29:33.000Z
console.log (DT1.toUTCString());    //Sat, 18 Apr 2020 16:29:33 GMT 
console.log (DT1.toJSON());         //2020-04-18T16:29:33.000Z
console.log (DT1.valueOf());        //1587227373000

Враховуючи, що дата по суті представлена в числовому форматі, з ними можна проводити операції, як з числами.

let DT1 = new Date("2020, 4, 18 19:29:33");
let DT2 = new Date("2019, 4, 18 19:29:33");
console.log (DT2>DT1); //false 
console.log (DT2<DT1); //true
console.log (DT1-DT2); //31622400000 мс
console.log ((DT1-DT2)/(24*60*60*1000)); //366 днів

Об’єкт JSON

Об’єкт JSON містить методи розбору (parsing) JavaScript Object Notation (JSON) та навпаки - перетворення значень у JSON. Він не має конструкторів

Таким чином він має всього два методи.

Ці методи JSON працюють з об’єктами, масивами, рядками, числами, булевими значеннями.

Метод JSON.stringify(student) перетворює об’єкт student у рядок. Отриманий рядок json називається JSON-форматованим або серіалізованим об’єктом. Ми можемо відправити його по мережі або помістити у сховище даних (БД).

Об’єкт у форматі JSON має кілька важливих відмінностей від об’єктного літералу:

console.log( JSON.stringify('test') ) // "test" рядок в подвійних лапках
console.log( JSON.stringify([1, 2, 3]) ); // [1,2,3]

let meetup = {
  title: "Conference",
  room: {number: 23, participants: ["john", "ann"]}
};
console.log ( JSON.stringify(meetup) );
/* вийде рядок:
{"title":"Conference","room":{"number":23,"participants":["john","ann"]}}
*/

JSON є незалежною від мови специфікацією для даних, тому JSON.stringify пропускає деякі специфічні властивості об’єктів JavaScript, а саме:

Однак це обмеження можна обійти через використання replacer.

Якщо необхідно налаштувати які саме властивості необхідно кодувати та вказати відступи можна використати повний синтаксис JSON.stringify:

let json = JSON.stringify(value[, replacer, space])

JSON.parse розбирає (парсить) рядок із JSON, створюючи відповідне до його вмісту значення чи об’єкт. Якщо при виклику в параметрі reviver (другий необов’язковий параметр) вказати функцію, то її буде використано для перетворення створеного об’єкта та його властивостей перед поверненням.

Синтаксис:

let value = JSON.parse(text[, reviver]);

Наприклад:

let txt = "[0, 1, 2, 3]"; //текст, що вміщує масив в форматі JSON
numbers = JSON.parse(txt);
console.log (numbers[1] ); // 1
let txt2 = '{ "name": "John", "age": 35, "isAdmin": false, "friends": [0,1,2,3] }';
user = JSON.parse(txt2);
console.log ( user.friends[1] ); // 1

Параметр reviver можна використати для керування розбором. Наприклад, необхідно отримати об’єкт meetup , який передається у вигляді рядку JSON. При цьому об’єкт містить об’єкт Date. При перетворенні в об’єкт, вийде наступний результат:

//на вузлі відправника
let meetupsend= {title:"Conference", date:new Date("2020-11-30T12:00:00.000Z")};
console.log(meetupsend.date.getDate()); //30
let str = JSON.stringify (meetupsend);
console.log (str);//{"title":"Conference","date":"2020-11-30T12:00:00.000Z"}

//на вузлі отримувача
let meetup = JSON.parse(str);
console.log(meetup.date.getDate()); // TypeError: meetup.date.getDate is not a function

Виникає помилка, бо значенням meetup.date є рядок, а не об’єкт Date, тому що JSON.parse не міг знати який саме тип було використано при перетворенні в JSON. У цьому випадку можна скористатися параметром reviver , який буде викликати функцію для перетворення кожного ключа.

let str = '{"title":"Conference","date":"2020-11-30T12:00:00.000Z"}';
let meetup = JSON.parse(str, function(key, value) {
  if (key == 'date') return new Date(value);
  return value;
});
console.log( meetup.date.getDate() ); // 30 

Об’єкт Function

У JavaScript функції - це також об’єкти, які мають властивості і методи.

let fno = function fn1(a,b,c){console.log (a+b+c);}
console.log (fno.name);     //fn1 - ім'я функції
console.log (fno.length);   //3 - кількість параметрів
console.log (fno.toString());//повертає першокод функції, вивід буде наступний   
//function fn1(a,b,c){console.log (a+b+c);}
fno.call (this, 100,10,1);    //111, викликає фукнкцію, this передається для контекста

Об’єкт Buffer

Буфер - область пам’яті, яка використовується для тимчасового збереження бінарних даних (I/O, файли, мережа …). Для роботи з буфером використовується глобальний клас Buffer, який надає для цього ряд методів.

let buf1 = Buffer.alloc(8,0xFF); // виділити буфер на 8 байт і заповнити
buf1.write('ABC', 2);//записати 65,66,67 с 2-го байта
let buf2 = Buffer.from('DEF'); //отримати буфер з рядку => 68 69 70
let buf3 = Buffer.from([1,2]); //отримати буфер з масиву
let buf4 = Buffer.concat([buf1,buf3,buf2],buf1.length + buf2.length + buf3.length); 
console.log (buf4.length); //24
console.log (buf4.toJSON().data);//виведе масив значень байтів буферу

Буфер можна вважати байтовою послідовністю. Для інтерпретації цієї послідовності як даних певного типу, є різноманітні методи, наприклад readInt16BE(offset) інтерпретує 16-біт буферу починаючи з offset як 16-бітний Integer в форматі Big Endian. Ось кілька прикладів:

const buf = Buffer.from([1, 2, 3, 4, 0, 5]);
console.log(buf.readInt16BE(4)); //виведе 5 0x0005
console.log(buf.readInt16LE(4)); //виведе 1280 0x0500
buf.writeFloatBE(36.5, 0); //запише 36.5 в форматі BigEndian
console.log(buf.readFloatBE(0)); //виведе 36.5
console.log(buf.readFloatLE(0)); //виведе 6.549669022254195e-42

Усі методи наведені в документації

Корисні посилання

Запитання для самоперевірки

  1. Що таке рушій JavaScript?
  2. Наведіть приклади області використання Java Script.
  3. Наведіть приклади середовища розробки та налагодження Java Script.
  4. Поясніть принципи типізації даних в JS.
  5. Перерахуйте типи даних в JS.
  6. Розкажіть про перетворення числових типів в рядки та навпаки.
  7. Розкажіть про оператори порівняння.
  8. Розкажіть про арифметичні оператори.
  9. Як проводяться операції з рядками в JS?
  10. Розкажіть про умовні оператори та конструкції.
  11. Як в JS організовані цикли?
  12. Розкажіть про роботу з функціями в JS.
  13. Розкажіть про роботу з об’єктами в JS.
  14. Розкажіть про роботу з масивами в JS.
  15. Розкажіть про додаткові можливості for для роботи з об’єктами та масивами.
  16. Що таке стрілочні функції?
  17. Розкажіть про функції зворотного виклику та асинхронність.
  18. Розкажіть як в середині об’єкта звертатися до власних методів та властивостей.
  19. Розкажіть про конструктори об’єктів.
  20. Назвіть кілька властивостей і методів об’єкту Number.
  21. Назвіть кілька властивостей і методів об’єкту Math.
  22. Назвіть кілька властивостей і методів об’єкту Date.
  23. Яке призначення об’єкту JSON?
  24. Назвіть кілька властивостей і методів об’єкту Function.

Матеріали лекції

Теоретичне заняття розробив Олександр Пупена.