Comment on page

Нагадайки-поясняйки

Escape characters

\n — перенос рядка
console.log("1\n2\n3\n4");
\tтабуляція (горизонтальна)
console.log("1\t2\t3\t4");
\ — екранування наступного символа. Екранований символ просто відображається як текст, а не використаний як спеціальна штука з мови програмування. Наприклад:
console.log("ці лапки \"заекрановані\", тому все відобразиться");
console.log("ці лапки не "заекрановані", тому буде помилка");
/*
Помилка буде, бо без екрану це перетворюється на 2 окремих рядка
"ці лапки не "
та
", тому буде помилка"
а між ними - якась незрозуміла для комп'ютера фігня, яка все ламає
*/
Увага: \n, \t та інші escape characters треба екранувати разом із їх бекслешем:
console.log("Перенос рядка")
console.log("екранований: a1\\nb");
console.log("не екранований: a\nb");
console.log("\nТабуляція")
console.log("екранована: a\\tb");
console.log("не екранована: b\tb");
// і т.д. і т.п.
Не лякайтесь посилання: крім \n, \t та екранованих лапок вам на Zero інші escapes не знадобляться :)
p.s. Конкретно лапки можна вставляти в рядки й без екрану, якщо вони різних типів:
console.log("одинарні 'в' подвійних");
console.log('подвійні "в" одинарних');
console.log(`а у бектіках 'взагалі' "що'''" завгодно"''чудить" можна :)`);
// https://ru.wikipedia.org/wiki/%D0%93%D1%80%D0%B0%D0%B2%D0%B8%D1%81
// це та кнопка, де тільда (~), тільки без Shift (у EN або US розкладках)

Про =, == та ===

Літерали

Літерал — це "голе" значення. Умовно кажучи: те, що можна покласти до змінної. Наприклад:
let n = "222";
n = 100500;
console.log(n + ", " + 12345);
n = true;
У цьому прикладі літерали — це:
  • "222"
  • 100500
  • ", "
  • 12345
  • true

Скорочення

Числа

Штуки типу i = i + 2 або i = i + "*"варто скорочувати через +=.
Довго
Коротко
i = i + 123
i += 123
i = i + "*"
i += "*"
i = i + ":)"
i += ":)"
i = i + "\n"
i += "\n"
i = i - 15
i -= 15
i = i * -3
i *= -3
i = i / 7.98
i /= 7.98
Це працює і зі змінними, і з літералами. Звісно, змінна може називатись не лише i
Якщо треба додати / відняти саме одиницю, є ще коротші форми: інкремент та декремент.
Дуже довго
Довго
Гарнюня
i = i + 1
i += 1
i++
i = i - 1
i -= 1
i--
Увага: = в інкременті не треба. Треба просто i++ або i--: як є, без нічого. Умовно кажучи, у цих спецсимволах = вже "вшитий під капотом".
Запис типу i = i++ або i = i-- працюватиме, але це неправильно і нівелює саму суть скорочення запису.
Запустіть код та читайте консоль
let n = 2; // замість 2 може бути будь-яке число
console.log("Початкове значення: " + n);
console.log("тип n зараз: " + typeof n)
console.put("\n" + n);
n += 27; // те саме, що й n = n + 27
console.log(" += 27 🠖 " + n)
console.put("\n" + n);
n -= 8; // n = n - 8
console.log(" -= 8 🠖 " + n);
console.put("\n" + n);
n *= 3.14; // n = n * 3.14
console.log(" *= 3.14 🠖 " + n);
console.put("\n" + n);
n /= 0.123; // n = n / 0.123
console.log(" /= 0.123 🠖 " + n);
// до речі, += працює ще й для конкатенації (зклеювання рядків)
console.put("\n" + n);
n += "якийсь рядок";
console.log(" += \"якийсь рядок\" 🠖 " + n);
// але після цього, звісно, n сама стане рядком
console.log("\nтип n зараз: " + typeof n)
// тобто:
console.put("\n" + n);
n += 100500;
console.log(" += 100500 🠖 " + n + "\n");
console.put ("від 0, n++: ");
n = 0;
while (n < 10) {
console.put(n++ + " ");
}
console.put("\nвід 0, ++n: ");
n = 0;
while (n < 10) {
console.put(++n + " ");
}

boolean

! — логічне заперечення, логічне "не".
let n;
// !n — те саме, що "не n", "щось протилежне n"
n = true; // спробуйте змінити це значення, пограйтесь
console.log("n = " + n);
console.log("!n = " + !n);
// !n не змінює вміст змінної
console.log("n все ще = " + n);
// щоб змінити:
n = !n;
console.log("хоба: n = " + n);
// у if, циклах та інших умовах, які зводяться до boolean, можна ще так:
if (n) {
console.log("if(n) спрацював, n = " + n + " зараз");
}
if (!n) {
console.log("if(!n) спрацював, n = " + n + " зараз");
}
if (n) {
console.log("а це IF з if-else, n = " + n + " зараз");
} else {
console.log("а це ELSE з if-else, n = " + n + " зараз");
}

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

Тернарний оператор — це коротка (та з парою особливих фіч) заміна if else для випадків, коли є лише 2 можливих варіанти розгалуження, а кожна дія може бути записана одним рядком коду.
У звичайного if else структура така:
if(якщо тут true) {
то виконай цей код
} else {
інакше, тобто якщо false, виконай цей код
}
А тернарним оператором те ж саме записується так:
якщо тут true ? то виконай цей код : інакше, тобто якщо false, виконай цей код
Тобто, наприклад
let number = 3;
let message;
if (number > 3) {
message = "number більше 3";
} else {
message = "number менше або дорівнює 3";
}
З тернарником скорочується до
let number = 3;
let message = number > 3 ? "number більше 3" : "number менше або дорівнює 3";
А якщо звернути увагу, що в обох варіантах початок та кінець повторюються, можна ще коротше:
let number = 3;
let message = "number " + (number > 3 ? "більше" : "менше або дорівнює") + " 3";
Тут слід пам'ятати, що тернарник, на відміну від if else — це окрема незалежна сутність (як масив, наприклад). І тому, якщо треба результат передати до функції, не треба викликати її саму в тернарнику двічі поспіль. Можна сам тернарник передати, як аргумент
😉
Тобто, краще не писати так:
let number = 3;
number > 3 ? console.log("number більше 3") : console.log("number менше або дорівнює 3");
А замість того писати так:
let number = 3;
console.log("number " + (number > 3 ? "більше" :" менше або дорівнює") + " 3");

Вкладені тернарники

Як if else можуть вкладатись одне в одного, так і тернарники можуть бути вкладеними в інші тернарники.
let number = 3;
console.log("number " + (number > 3 ? "більше" : number < 3 ? "менше" : "дорівнює") + " 3");
Кількість рівнів вкладеності необмежена.
У цій кракозябрі легко заплутатись, але тернарник у більшості випадків нормально розуміє перенос рядків, тому можна записати якось так:
let number = 3;
console.log("number " +
(number > 3 ? "більше"
: number < 3 ? "менше"
: "дорівнює")
+ " 3");
// або взагалі так, щоб максимально схоже на if else
console.log("number " +
(number > 3
? "більше"
: number < 3
? "менше"
: "дорівнює")
+ " 3");
Це теж далеко не зразок читабельності. Але вже набагато більше схоже на знайомі if else. Та одночасно непогано демонструє вкладені один у одного тернарники.
Проте, якщо доводиться щось таке писати, то тернарник майже 100% буде не найкращим рішенням. Тому що він про скорочення, а тут не дуже й коротко; ну а без переносів це не дуже читабельно.
Тож дійте за обставинами, керуйтесь здоровим глуздом і не туліть всюди тернарники замість if else без необхідності, а просто тому, що це якась прикольна штука
😉

Остача від ділення націло (%)

Якщо ви гуглили, що воно таке, то, скоріш за все, натикались на статтю з Вікіпедії. Якщо зрозуміли - круто. Але якщо той математичний канцелярит не зайшов, є простіше пояснення.
Є a % b . Беремо найбільше число, кратне b, яке менше або дорівнює a . Віднімаємо від a це число. Отримана різниця - і є результат. На прикладі 10 % 3: найближче до 10, яке можна націло розділити на 3 (тобто кратне 3) - це 9. 10 - 9 = 1. Тобто10 % 3 буде 1.
Ось демо-код. Повводьте туди різні числа, в т.ч. від'ємні та не цілі (6.21 і т.п.). І дивіться, що буде.
let number, divider, operatorResult, loopResult;
number = +prompt("Введіть число");
while (typeof number !== "number" || isNaN(number)) {
number = +prompt("Некоректне число, введіть нормальне");
}
divider = +prompt(`${number} % `);
while (typeof divider !== "number" || isNaN(divider) || divider === 0) {
divider = +prompt("На це не можна ділити. Спробуйте знову\n" + number + "%");
}
operatorResult = number % divider;
console.log(`${number} % ${divider} => ${operatorResult}\n`);
console.log("Чому так:");
loopResult = Math.abs(number);
if (divider < 0 || number < 0) {
console.log("Увага! Від'ємні беремо по модулю\n");
divider = Math.abs(divider);
}
while (divider > 0 && loopResult >= divider) {
console.log(`${loopResult} - ${divider} = ${loopResult - divider}`);
loopResult -= divider;
}
console.log(`\nОстача (${loopResult}) менша за дільник (${divider}).`);
if (number < 0) {
loopResult = -loopResult;
}
console.log(`От вона і є результат: ${loopResult}`);
if (number < 0 && loopResult !== 0) {
console.log("(вже не по модулю, звісно)");
}
Яке у цього практичне застосування? Та дуже просте. Якщо остача a % b === 0, то a ділиться без остачі на b. Тобто a кратне b. Так, наприклад, можна перевірити число на парність/непарність, визначивши, чи кратне воно двійці. Або можна дізнатись, скільки мінімум автобусів потрібно для перевозки групи a туристів, якщо в кожному автобусі - максимум b місць. Або ще щось таке, придумайте самі :)

Складання у змінну

Інколи (навіть часто), коли в циклах щось виводимо на консоль, замість 100500 разів просто виводити щось на кожній ітерації, корисніше та зручніше — складати значення до змінної (додавати до неї, конкатенувати з нею) а потім один раз виводити саме цю змінну.
Тобто замість
for(let i = 0; i < 10; i++) {
console.put(i);
}
якось типу так
let buffer = "";
for(let i = 0; i < 10; i++) {
buffer += i;
}
console.log(buffer);

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

Найчастіші процедури на Zero

let n;
console.log()
n = "123"; // рядок
n = +n; // число
n = +"123" // так теж можна
// ЧИСЛО В РЯДОК
n = 123; // це число
n += ""; // будь-що складаємо з рядком — отримуємо рядок

Таблиця конвертацій

Табличка звідси
Значення
Перетворене на число
Перетворене на рядок
Перетворене на boolean
false
0
"false"
false
true
1
"true"
true
0
0
"0"
false
1
1
"1"
true
"0"
0
"0"
true
"000"
0
"000"
true
"1"
1
"1"
true
"20"
20
"20"
true
""
0
""
false
"twenty"
NaN
"twenty"
true
NaN
NaN
"NaN"
false
Infinity
Infinity
"Infinity"
true
-Infinity
-Infinity
"-Infinity"
true
null
0
"null"
false
undefined
NaN
"undefined"
false
[ ]
0
""
true
[20]
20
"20"
true
[10,20]
NaN
"10,20"
true

Скоуп

Скоуп (область видимості) — це шматок кода всередині{ }
for(1) {
for(2) {
while(3) {
}
}
for(4) {
}
}
Цикли 2, 3 та 4 входять до скоупу 1. 3 входить до скоупів 1 та 2. 4 входить до скоупу 1.
І є ще глобальний скоуп, куди входять вони всі разом. Це "нульовий" рівень, тобто ВЕСЬ код в редакторі. Можете уявити, що весь код в IDE загорнутий у верховні невидимі { }.
{ // це типу умовна "невидима верховна дужка" початку глобального скоупу
for(1) {
for(2) {
while(3) {
}
}
for(4) {
}
}
} // а це — верховна дужка кінця. теж умовна і невидима
Змінні зі внутрішнього скоупу "бачать" зовнішні. Але із зовнішнього скоупу не видно внутрішні.
let n = 100500;
for (let i = 0; i < 10; i++) {
n++; // так можна, бо ми звертаємось "назовні"
console.log(i); // так можна, бо i оголошена в цьому ж скоупі
for(let j = 0; j < 10; j++) {
console.log(i); // можна, бо теж ідемо в зовнішній скоуп
console.log(j); // можна
}
console.log(j); // а так не можна, буде помилка, бо в цьому скоупі вже нема j
}
Тобто правило просте: назовні — бачимо, всередину — ні.

Цикли

Тіло цикла — все, що знаходиться всередині цикла { тут }
for() {
тіло цикла
}
while() {
тіло цикла
}
do {
тіло цикла
} while();

Структура циклів

  • Якщо треба повторити дії конкретну кількість разів, то юзаємо for.
  • Якщо вказати кількість повторів заздалегідь неможливо: while.
  • Якщо треба зробити щось незалежно від обставин як мінімум 1 раз, а далі — по ситуації: do while

for

for(1; 2; 3)
1: місце для створення однієї або кількох змінних (якщо кілька — через кому)
2: вираз або змінна, який цикл конвертує в boolean (див. табличку вище). Доки 2 буде true, цикл повторюватиметься. Якщо стане false — цикл перерветься.
3: дії, які виконаються в кінці кожної ітерації
for (let i = 0, j = 10, k = true; i < 25; i++, j--, k = !k) {
console.log(`i=${i}\tj=${j}\tk=${k}`)
if (i > 0 && i % 5 == 0) {
console.log(":)\n")
}
}
У цьому прикладі:
1: let i = 0, j = 10, k = true 2: i < 25 3: i++, j--, k = !k
Увага: 1, 2 та 3 входять в скоуп цикла, в якому вони створені.

while

Те саме, що for, але має лише блок 2. 1 Треба руками записувати перед циклом десь ззовні, а 3 — в тілі циклу (дляповної тотожності — в самому кінці перед }.
let i = 0, j = 10, k = true;
while (i < 25) {
console.log(`i=${i}\tj=${j}\tk=${k}`)
if (i > 0 && i % 5 == 0) {
console.log(":)\n")
}
i++;
j--;
k = !k;
}

do while

do {
X
} while (Y);
Що б там не було, бодай 1 раз виконати все, що в X. Потім перевірити, чи Y == true. Якщо так — повторювати X доти, доки Y == true
let i = 0, j = 10, k = true;
do {
console.log(`i=${i}\tj=${j}\tk=${k}`)
if (i > 0 && i % 5 == 0) {
console.log(":)\n")
}
i++;
j--;
k = !k;
} while (i < 25);

continue та break

continue: "ігнорувати весь код в наступних рядках та перейти до наступної ітерації цикла"
break: "завершити цикл" (точно так же, як якщо б в блоку 2 стало false)
Увага: continue та break впливають лише на свій скоуп і не чіпають зовнішній. Умовно:
for(let i = 0; i < 10; i++) {
for(let j = 0; j < 10; j++) {
if (щось) {
break; // тут перерветься лише j-цикл. А i-цикл продовжиться
}
if (ще щось) {
continue; // перейде до наступної ітерації j, а на i не вплине
}
}
}
Брейкати зовнішні скоупи можливо, але це не знадобиться на Zero.
А взагалі варто уникати continue та break. В тому сенсі, що, якщо можливо без них — робіть без них. Вони не про "коли завершити" а "якщо щось трапилось — завершити".
Повну різницю між ними зрозумієте на курсі P2P CS.
Ну типу, навіщо робити якось так:
let i = 0;
while (true) {
console.log(i);
if (i == 10) {
break;
}
i++;
}
// або взагалі
for (let i = 0; ; i++) {
console.log(i);
if (i == 10) {
break;
}
}
коли сама структура циклів підштовхує робити так:
let i = 0;
while (i <= 10) {
console.log(i);
i++;
}
//ну і відповідно
for (let i = 0; i <= 10; i++) {
console.log(i);
}
break / continue доречні, коли треба зреагувати на щось, що виникає внаслідок роботи самого циклу. Наприклад:
let inputText;
let counterA = 0;
let counterB = 0;
while (true) {
counterA++;
inputText = prompt("Введи щось");
if (inputText == "пропусти") {
alert("Ок, на цій ітерації counterB не збільшуємо");
continue;
}
if (inputText == "зупинись") {
break;
}
counterB++;
}
console.log("counterA = " + counterA + "\ncounterB = " + counterB);
Коротше кажучи, у більшості випадків, якщо цикл переривається через break, а не через умову переривання в голові цикла — з алгоритмом щось не так, і його варто оптимізувати.