Записки по JavaScript

Строчный можно поместить в блочный, но блочные не могут быть вложенные.

// Строчный комментарий
 
/*
  Блочный комментарий
*/
  • Переменные регистрозависимы, от "A" до "z".
  • Строгой типизации нет, переменные универсальные, им можно присваивать различные типы.JavaScript является динамически типизированным.
  • Имя должно начинаться с
    • буквы
    • нижнего подчёркивания (_)
    • или знака доллара ($)
    • последующие символы могут также быть цифрами (0-9).
  • Имя не может начинаться на цифру
  • Принято использовать "верблюжью нотацию" — пишут в нижнем регистре, символ верхнего регистра используется только на стыке слов, например userName, myFirstScript, myVar. Но первый символ должен быть в нижнем регистре.
  • Исключение - "pascal notation", используется в функциях конструкторов, начинается с символа верхнего регистра MyVar
  • Названия констант пишут в верхнем регистре.
  • Константы менять нельзя, но значение объектов в константах менять можно.
  • Переменные объявлять не обязательно, они объявляются при первом присвоении, но лучше так не делать.
    • В коде непонятно, это присвоение идёт новой переменной или уже существующей
    • Область видимости. Если var, то в блоках кода {var myVar01;let myVar02;} переменная myVar01 глобальная, если let, то myVar02 локальная.
    • Лучше включить strict mode "use strict"; в начале.
const PI;
var myVar01; //Устаревшее объявление.
let myVar01; //Современное объявление(рекомендуется использовать).
let myVar1 = 33; // Лучше использовать объявление с присвоением.

Если переменная объявлена, но значение не присвоено, то по умолчанию оно будет равно "undefined".

Операции и операторы

ОперацияОписание
+сложение
-вычитание
*умножение
/деление
%остаток от деления, 5%2 → 1

Выполняется справа → налево.

age = 30 + 1.5*2
p = age + 2

Вычисляется умножение, потом сложение и присваивается переменной age.

Переменная принимает значение "NaN" - "Not a number", когда вычисляется ошибочное значение, но это числовой тип.

age = 'abc'
 
p = (age +2) *2;
console.log(age);
ДействиеТип
1/0 Infinity
-1/0 -Infinity
0/0 NaN
ТипОписание
undefined Тип данных, которые не определены
nullНулевой тип данных, можно обнулять значение переменной
intЧисловые:целые, вещественные, бесконечность. Можно ипользовать разделили в больших числах. 123_000_000 == 123 миллиона
string
boolean
Объекты
Массив
Функция
Регулярные выражения

Посмотреть тип значения переменной

typeof age 
Math.round() 
Math.floor() 
Math.ceil() 
 
"100" + 1 = "1001"     // + интерпретируется, как конкатенация по операнду слева.
x = parseInt("100")+1  //100, преобразование в целое из строки. "100asdfdf" будет корректно обработано, т.к. обработка идёт с начала строки.
x = parseFloat("200")  // в вещественное
x = 10;
console.log(x);
x = x + 6; //Работает справа налево, в начале вычисляется x+6 и присваивается x
x +=6; //Тоже самое, что и выше. Сокращённый оператор присваивания. "Синтаксический сахар", когда одно действие можно записать по разному.
 
x=1;
 
//++ - плохая конструкция, которую трудно читать. UB - undefined behavior
//Разница проявится при присвоении
++x; //префиксный инкремент
x++; //постфиксный инкремент
//Оператор присваивания 
 
let a;
let b = a = 5; //Разбирается справа налево, a=5, b=a 5
let b = a++ = 5; //Разбирается справа налево, a=5, b=a 5, потому что ++ будет после присваивания
let b = ++a = 5; //Разбирается справа налево, a=5, b=a 6, потому что присвоение будет для a+1
 
let a = 10;
//let b = (a = a + 1 ) + (a = a + 1)
//b = (11) + (12)
 
//let b = ++a + ++a
//b = 11 + 12 
 
//let b = a++ + +aa
//b= 10 + 12
 
let b = ++a + a++
//b = 11 + 11
let userName;
 
userName = prompt("Введите имя:","Иван");
age = parseInt(prompt("Введите возраст:","54"));
 
let result = "Привет, " + userName + "!"
let a = true;
let b = false;
 
//ЛОГИЧЕСКОЕ И
let c = a && b; // False
 
//ЛОГИЧЕСКОЕ ИЛИ
c = a || b;  // True
 
//ЛОГИЧЕСКОЕ НЕ
c = !a;   //False
c = !(a && b) // !a || !b // True
(n /2 ) == (n - 5) // Оператор сравнения

При сравнении JavaScript будет делать приведение типов, поэтому существует опратор ===

ОператорОписание
==сравнение на равенствоtrue, если равны, false, если не равны
!=сравнение на неравенствоtrue, если не равны, false, если равны
===сравнение на равенство с учётом типа.всегда false, если типы разные, true если типы и значения равны
!==сравнение на равенство с учётом типа.всегда true, если типы разные, true если типы и значения равны
>больше
<меньше
>=больше или равно
меньше или равно
let x = 50;
(x > 0) && < (x <=100) // true

Работают с целыми числами

let a = 5; //0101
let b = 7; //0111
  • 1 если оба разряда 1
  • 0 если оба разряда 0
  • 0 если один из разрядов 0
let c = a & b; //0101
5
 
==== БИТОВОЕ ИЛИ ====
  *  1 если один из разрядов 1
  *  0 если оба разряда  0
<code JavaScript>
с = a | b; //0111 
7
  • Меняет все биты на противоположные
  • Знак целого числа хранится, как бит, поэтому знак тоже поменяется на противоположный.
с = ~a; 
-6
  • 1 в несовпадающих битах
  • 0 в совпадающих битах
c = a ^ b; 
2 //0010

Сдвигаем все биты вправо и указываем на сколько разрядов

c = a >> 2; // 0101 >> 2 == 0001 
с = a << 2; // 0101 << 2 == 010100

Тип данных СТРОКОВОГО ЛИТЕРАЛА

  • Нет отдельного типа - символ
  • Между двойными и одинарными кавычками разницы нет
  • Внутри строк работают ESC последовательности "\n", "\t", "\u002F"(символ по коду)
  • + это символ конкатенации, "Привет"+ "Сергей"
  • Можно использовать шаблоны, используются backtick - обратные апострофы, let result = `Привет, ${userName}!`
let result = 'Привет "+ userName + "!"
let result = `Привет, ${userName}!`

Условный оператор if (ветвление)

Правило ЛЖИ(что считается ложью)

  • false
  • undefined
  • null
  • 0
  • Nan(Not a Number) 0/0
  • ''(две кавычки)

Всё остальное → ИСТИНА

  • 1/0 Infinity
let n = 10;
if (n > 0) 
    console.log("n больше нуля");
 
if (n > 0) {
    console.log("n больше нуля");
    console.log("n > 0 ");
}
let n = -10;
 
if (n > 0) {
    console.log("n больше нуля");
} else
    if (n > -100) 
        console.log("n больше -100");
    else    
        console.log("n меньше -100");
let x = 1 / 0
if (x) 
   console.log("True");
else    
   console.log("False");
let s = (n==0) ? "Ноль" : "Не ноль";

(||=) Оператор логического OR присвоения проверяет правый операнд и назначает его левому, если слева операнд равен false. (&&=) Оператор логического AND присвоения проверяет правый операнд и назначает его левому, если слева операнд равен true.

const a = { duration: 50, title: '' };
 
a.duration ||= 10;
console.log(a.duration);
 
50 //Значение a.duration существует, значит True и значение 10 не присваивается.
 
a.title ||= 'title is empty.';
console.log(a.title);
 
"title is empty." //Значение a.title пустое, значит false и присваевается значение 'title is empty.'
 
 
// AND
 
let (z) =20;
 
if (z)
    z=5;
// или    
z &&=5  // присваевает, если z истина
 
 
let secondUserName;
secondUserName ??= "Unknown"; // присваивает значение, если secondUserName Undefined или null
 
console.log(secondUserName);

Оператор ветвления switch

  • switch прыгает на значение и выполняются все case до конца.
  • break выполняет выполнение switch
  • Если switch внутри функции и является последним оператором, то можно использовать return вместо break;
let n = 2;
 
switch (n) {
    case -1: // тогда и -1 и 1 сработает
    case 1:
          console.log("1");
          break;
    case 2:
          console.log("1");
          break;
    default: // если значение не было найдено
          console.log("значение не найдено.")
          break; //можно не писать.
}

Циклы

  • Инициализация "счётчика"
  • Проверка условия
  • Выполнение инструкций
  • Изменение "счётчика"
  • break - останавливает выполнение цикла
  • continue останавливает выполнение текущей итерации и переходит к следующей итерации
for (let i =1; i <= 10; i++){
     if(i == 3) continue;
     if(i == 8) break;
     document.write(i);
     document.write('<br>');
}
document.write("<table border=1>");
 
for(let i=1; i<=10;i++){
    document.write("<tr>");
 
    for(let y=1; y<=10;y++){
        document.write(`<td>${i}.${y}</td>`);
    }
    document.write("</tr>");
}
 
document.write("</table>");

Цикл с условиями while / do while

  • Условие проверяется перед каждой итерацией.
  • Если условие не равно True, то итерация цикла выполняться не будет
let a =2;
while (a < 1000){   
 document.write(a);
 document.write('<br>');
  a*=2; // a = a* 2
}
  • Гарантировано выполняется одна итерация
let a = 2000;
do {   
 document.write(a);
 document.write('<br>');
  a*=2; // a = a* 2
} while (a < 1000)

Цикл (foreach)

console.log(navigator.userAgent);
 
document.write(`<h1 style="color: rgb(226, 116, 43);">foreach</h1><p>`);
 
document.write("<p>"+navigator.userAgent+"</p>");
document.write("<p>"+navigator.language+"</p>");
 
for(let property in navigator){
 
    document.write("<p>"+property+":"+navigator[property]+"</p>");
 
}
document.write("</p>")

Функции (function)

  • Функцию можно вызывать до её объявления, т.к. интерпретатор в начале смотрит все определения и потом запускает. Поддерживается в функциях задекларированных классическим способом.
  • Анонимные функции можно вызывать после определения
  • return оператор завершения функции.
  • returnов может быть несколько, но желательно, чтобы был один.
  • Переменные в функции являются локальными, если они объявлены внутри функции с помощью let или var;
  • window.переменная - Обращение к глобальной переменной, но переменная должна быть определена через var.
  • Если не использовать var или let внутри функции и переменной присвоить значение в теле функции, то она станет глобальной. Поэтому лучше использоваться strict mode
document.write(`<h1 style="color: rgb(226, 116, 43);">Function</h1><p>`);
 
function sayHello(userName,age){
   // age ??= 18 // Default value, если значение слева пустое, то присваиваем 18
    if(age)
        document.write(`<p style="font-size:28px;">Hello, ${userName}. Возраст: ${age}</p>`)
    else    
        document.write(`<p style="font-size:28px;">Hello, ${userName}</p>`)
}
 
sayHello('Sergey',34);
sayHello('Pavel',92);
sayHello('Анна');
 
function average(a,b){
 
let avg = (a+b)/2;
 
return avg;
 
}
 
let x= average(12,34)
 
document.write(`<p style="font-size:28px;">x= ${x}</p>`)
  • Функцию можно положить в переменную и вызывать, как переменную.
  • Это позволяет передавать функцию, как параметр.
function bar(){
    let z=10;
    return z;
    }
 
    let barVar = bar
 // bar()  не вызывается
    document.write(barVar()); // вызов функции, которая лежит в переменной.
 
    document.write( typeof barVar); // посмотреть тип, будет function
  • Функция может быть анонимной, когда опускается название функции.
  • Анонимные функции можно вызывать после определения.
function test(){
        document.write("<br> Test");
    }
    let testVar=test();
    test();
    testVar();
 
    //или
    //анонимная функция
    let testVar1 = function (){
        document.write("<br> Test1");
    }
 
    testVar1();

Объекты

  • Сложная программная сущность, которая группирует внутри себя несколько переменных, под некоторыми уникальными в рамках этого объекта именами.
  • Объкты могут содержать свойства(поля) и методы(функции), оперирующая данными этого объекта.
  • Функции конструкторы с большой буквы, например Object
  • . - Обращение к свойствам и методам объекта
  • this - указатель на текущий объект
let book1 = new Object();
console.log(typeof book1);
 
book1.title = 'Улитка на склоне';
book1.price = 300;
 
function renderBook(){
    document.write(`<p>Название:${this.title} Цена:${this.price}</p>`)
}
 
book1.render = renderBook; //добавляем функцию в объект
 
book1.render();
  • Элементы массива могут иметь разные типы, но это плохая практика.
  • Массив имеет динамический размер, в остальных языках он фиксированный
  • Массив в JavaScript это коллекция
  • Нумерация начинается с 0
  • Существует метод .at(-1) и тогда можно обращаться к элементам с отриц. значением и концом. появилось в 22 году
let colors = ['red','green','blue'];
let colors1  = new Array('red','green','blue'); //устаревший способ
 
console.log(colors);
console.log(colors.length); //Длина массива
 
colors[0] = 'pink'; // Замена первого элемента массива
 
console.log(colors[0]);
 
console.log(colors[colors.length-1]);  //Последний элемент массива
 
for(let i = 0; i < colors.length; i++){
    document.write(`<br> ${colors[i]}`);
}
 
let s = colors.join("-") //Объединение в строку с разделителем
console.log(s);
 
let stateStr="Россия.США.Китай.Германия.Япония"
let states = stateStr.split(".")
 
console.log(states)
console.log(states.sort())  //Сортировка
states = states.join("#")
console.log(states)
 
// Удаление последнего элемента
console.log( colors.join())
let last = colors.pop()  //Удаление последнего элемента и помещение его в переменную last
console.log( colors.join())
console.log( last)
 
// Добавить в конец массива элемента
console.log( colors.join())
console.log( colors.push('yellow'))
console.log( colors.join())
 
//Удалить и добавить первый элемент
console.log( colors.unshift('grey')); //Добавление в начало
console.log( colors.join())
 
console.log( colors.shift(0)); // Удаление первого элемента со сдвигом всех элементов
console.log( colors.join())
 
colors.reverse() //Реверсно переставить элементы
console.log( colors.join())
 
//Сортировка чисел, по умолчанию сортирует, как строки
let nums = [123, 2, 34, 55, 99, 45, 24, 71, 64];
console.log(nums.join());
nums.sort();
console.log(nums);
 
//Надо передать функцию сравнения для сортировки
function compareNumbers(x,y){
    if (x > y) return 1; 
    if (x < y) return -1;
    if (x == y) return 0;
}
nums.sort(compareNumbers);  // Теперь сортирует, как числа.
console.log(nums);
 
//Можно переделать функцию 
function compareNumbers(x,y){
   // if (x > y) return 1; 
   // if (x < y) return -1;
   // if (x == y) return 0;
   return x - y;
}
nums.sort(compareNumbers);
console.log(nums);
 
// Можно описать анонимной функцией
 
let cmp = function (x,y){
    return x - y;    
}
nums.sort(cmp);
console.log(nums);
 
// реверсная сортировка, сортировка по убыванию
let cmpr = function (x,y){
    return y - x;    
}
nums.sort(cmpr);
console.log(nums);
 
//Анонимная функция
nums.sort(function (x,y){return x-y;});
console.log(nums);
 
//Cтрелочная функция =>, Лямбда выражение в других языков
nums.sort((x,y)=>{return x-y;});
nums.sort((x,y)=>x-y); //убираем return и фигурные скобки, если функция имеет только один return
console.log(nums);
 
 
colors.findIndex( e=>e.length==3); //Ищет с начала и возвращает индекс
colors.find( e=>e.length==3);      //Ищет с начала и возвращает значение
 
//ECMA23 
colors.findIndexLast( e=>e.length==3); //Ищет с концца и возвращает индекс
colors.findLast( e=>e.length==3);      //Ищет с конца и возвращает значение
 
 
 
//группировка
let g = Object.groupBy(books, (element,index)=>element.price>100?'high':'low')
console.log(g);
 
//группировка
let g1 = Maps.groupBy(books, (element,index)=>element.price>100?'high':'low')
console.log(g1);

Объекты. Конструкторы.

//Функции конструкторы принято называть с большой буквы.
 
 
function renderBook(){
 
    document.write(`${this.title} - ${this.price}`);
 
}
 
function Book(title,price){
 
    this.title = title;
    this.price = price;
    let _title = title;  // Присвоение локальной переменной для защиты от изменения. Старый способ. По точке этого свойства не будет, доступно только методам объекта.
 
    //this.render = renderBook;
 
    this.render = function(){
        document.write(`${this.title} - ${this.price}<br>`);        
    };
 
    this.GetTitle = function(){return _title;} //новый способ, создание метода, возвращающего приватное свойство
 
}
 
 
let book1 = new Book('Улитка на склоне', 300);
let book2 = new Book('Лестница в небо',  200);
 
book1.render(); //this === book1
book2.render(); //this === book2
 
 
 
book1.title = "Над пропостью во ржи"
book1.render(); //this === book1

Прототипы [[prototype]]

  • Элемент ненумерованного списка в каждом объекте JavaScript может быть поле prototype
  • Мы не можем к нему обращаться, он недоступен.
  • Если нет свойсва в prototype, то будет искать в prototype прототипа.

render добавлено в prototype

function renderBook(){
    document.write(`${this.title} - ${this.price}`);
}
 
function Book(title,price){
 
    this.title = title;
    this.price = price;
    let _title = title;  // Присвоение локальной переменной для защиты от изменения. Старый способ. По точке этого свойства не будет, доступно только методам объекта.
 
//    this.render = renderBook;
//    this.render = function(){
//       document.write(`${this.title} - ${this.price}<br>`);        
//    };
    this.GetTitle = function(){return _title;} //новый способ, создание метода, возвращающего приватное свойство
}
let book1 = new Book('Улитка на склоне', 300);
let book2 = new Book('Лестница в небо',  200);
 
//book1.render(); //this === book1
//book2.render(); //this === book2
//book1.title = "Над пропостью во ржи"
//book1.render(); //this === book1
 
Book.prototype.render = function(){
    document.write(`${this.title} - ${this.price}<br>`);        
}
book1.render(); //this === book1  в объекте render нет, а в прототипе есть.
book2.render(); //this === book2

Классы

  • Классы это надстройка над прототипами
  • 20 лет в JavaScript не было понятия классов
  • Класс - понятие типов данных объектов
  • В JavaScript нет понятия разных типов данных для объктов, они формально все одного типа - object.
  • Вся объектная модель основывалась на прототипах.
  • Классы появились с 2015 года в JS.
  • Классы в JS это надстройка, технически JS всё так же работает с прототипами.
class Book {
    #title; // # - Private field since ECMAScript 2022
    price = 0;
    constructor(title, price){
        this.#title = title; 
        this.price = price;
    }
    render(){
        document.write(`${this.#title} -${this.price} <br>`)
    }
}
let book1 = new Book('Улитка на склоне', 300);
let book2 = new Book('Лестница в небо',  200);
 
book1.render(); //this === book1
book2.render(); //this === book2

Строки

  • Строки в JavaScript являются незименяемым объектом, т.е. все методы создают на основе строки новую строку, которую можно присвоить переменной.
  • В 2021 году появился метод ReplaceAll - замена всех вхождений подстроки в строке.
let Result = "My Very Long String!"
 
let a = Result.indexOf("Long1")
 
console.log(Result);
console.log(Result.length); //Длина строки
console.log(Result.charAt(0)); //Символ по номеру
console.log(Result.toUpperCase());   //Перевод в верхний регистр
console.log(Result.indexOf("Long")); //Поиск подстроки, номер симовола или ложь(-1) если не найдено
 
document.write(navigator.userAgent);
 
let isChrome = navigator.userAgent.indexOf("Chrome") >=0;
 
console.log(isChrome);
 
//Подстрока
console.log(Result);
console.log(Result.substring(3,9)); //Very L с 3 по 9
console.log(Result.substring(3));   //Very.. с 3 по 9
 
//Замена
console.log(Result);
console.log(Result.replace("Long","Short"));

Обработка ошибок

let n = - 101;
console.log(n)
console.log('Before error')
 
if (n < 0 || n > 100){
       throw new Error('n out of [0,100]') 
 
    console.log('After error')
 
}
//let n = - 101;
try{
    if (n < 0 || n > 100){
        console.log(n)
        console.log('Before error')
 
        throw new Error('n out of [0,100]') 
        console.log('After error')  
 }
}
catch(e){
    console.log(e)
}

Регулярные выражения

  • RegExp - два способа описания регулярного выражения, через RegExp и через '/'
let phone = '011-45-78';
 
let reg = RegExp('\\d\\d\\d-\\d\\d-\\d\\d')
 
let reg3 = /[1-9]\d\d\d-\d\d-\d\d/ig;
 
let reg1 = RegExp('\\d{3}-\\d{2}-\\d{2}')
 
let reg2 = RegExp('[1-9]\\d{2}-\\d{2}-\\d{2}')
 
if(reg3.test(phone)){
    console.log("Yes")
}else{
    console.log("No")
}

JSON

//JSON  JavaScript notation
//{} - eq пустой объект
 
let book1 = {
    title: "Улитка на склоне",
    price: 300,
    render: function(){
 
            document.write(this.title + ' - '+ this.price + '<br>');
    }
 
};   
 
book1.render();
let s = JSON.stringify(book1); // преобразование к строке, называется сериализацией
console.log(s);
console.log(typeof s);
 
let book2 = JSON.parse(s); // из строки в объект, десереализация
console.log(book2);
 
let books = [
    {
        title: "Улитка на склоне",
        price: 300,
    },{
        title: "Лестница в небо",
        price: 100,
   },{
        title: "Понедельник начинается в субботу",
        price: 200,
   }
]
 
console.log(books);
console.table(books);
 
document.write('<table border=1 >')
document.write(`<tr><td>Title</td><td>Price</td></tr>`)
 
books.forEach(element => {
    document.write(`<tr><td>${element.title}</td><td>${element.price}</td></tr>`)      
});
 
document.write('</table>')

Работа с датами

let now = new Date('2025/02/05');
console.log(now);
 
let myDayOfWeek = ('0' + now.getDay()).slice(-2); // День недели. slice - добивание 0 слева
console.log(myDayOfWeek);
 
let myDay = ('0' + now.getDate()).slice(-2); // День месяца
console.log(myDay);
 
let myMonth = ('0' + (now.getMonth()+1)).slice(-2); //Месяц. Начинается с 0
console.log(myMonth);
 
var firstday = ("0"+(now.getDate() - now.getDay() + (now.getDay() == 0 ? -6 : 1))).slice(-2)
 
console.log(firstday);
 
var lastday = ("0"+(now.getDate()+ (7 - now.getDay()) )).slice(-2)
console.log(lastday)
 
 
let currentDate = `${firstday}.${myMonth}-${lastday}.${myMonth}`
console.log(currentDate); // "17-6-2022"