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

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, а не через умову переривання в голові цикла — з алгоритмом щось не так, і його варто оптимізувати.

Last updated