Главная страница » JS (jQuery) » Конспект по JS темам для middle разработчика

Конспект по JS темам для middle разработчика

Содержание скрыть
  1. 1 Ядро языка (JS core)
    1. 1.1 var vs let
    2. 1.2 Ключевое слово const
    3. 1.3 Стрелочные функции (arrow functions)
      1. 1.3.1 Особенности стрелочных функций:
    4. 1.4 Классы (classes)
      1. 1.4.1 Наследование классов
      2. 1.4.2 Создание объекта на основе класса-наследника
    5. 1.5 Rest и Spread операторы
      1. 1.5.1 Rest оператор
      2. 1.5.2 Spread оператор
    6. 1.6 Деструктуризация (destructuring)
    7. 1.7 Шаблонные строки (template strings)
    8. 1.8 Тип данных BigInt
    9. 1.9 Тип данных Symbol
    10. 1.10 Объект Math
    11. 1.11 NaN
    12. 1.12 Object methods
    13. 1.13 Директива “use strict”
  2. 2 Scope
    1. 2.1 Поднятие (hoisting)
    2. 2.2 Лексическая область видимости (lexical scoping)
    3. 2.3 Сборка мусора (garbage collection)
  3. 3 Объектно-ориентированный подход в javascript (OOP)
    1. 3.1 constructors vs factories
    2. 3.2 Inheritance (наследование)
    3. 3.3 Прототипное наследование
    4. 3.4 Наследование vs композиция
  4. 4 Парадигмы программирования в javascript
  5. 5 Рекурсия
  6. 6 Функции map/reduce
  7. 7 Mutable/immutable
  8. 8 Чистая функция (pure function)
  9. 9 Promises
  10. 10 Async/await
  11. 11 Обработка ошибок в асинхронном коде
  12. 12 ECMAScript 2016-2021
    1. 12.1 ECMAScript 2017 (ES2017)
    2. 12.2 ES2018
    3. 12.3 ES2019
    4. 12.4 ES2020
    5. 12.5 ES2021
  13. 13 TypeScript
    1. 13.1 Базовые понятия и принципы
    2. 13.2 Классы в TS
    3. 13.3 Интерфейсы
    4. 13.4 Наследование в TS
    5. 13.5 Type guards
    6. 13.6 Mixins
    7. 13.7 Поделиться ссылкой:

Ядро языка (JS core)

var vs let

Основное отличие между let и var в ECMAScript 6 заключается в области видимости переменных.

Ключевое слово var используется для объявления переменной в функциональной области видимости, а также в глобальной области видимости. При использовании var переменная будет доступна в любой части функции или в любом месте кода за пределами функции.

Ключевое слово let, с другой стороны, используется для объявления переменной в блочной области видимости, которая ограничивается фигурными скобками, например, в циклах или блоках условных операторов. Это означает, что переменная объявленная с помощью let будет видна только внутри блока, в котором она была объявлена.

Также важно отметить, что переменная, объявленная с помощью let, не может быть повторно объявлена в той же области видимости, в отличие от переменной, объявленной с помощью var.

Вот пример, который демонстрирует разницу между let и var:


function example() {
    var x = 1;

    if (true) {
        var x = 2;
        console.log(x); // 2
    }

    console.log(x); // 2
}

function example2() {
    let y = 1;

    if (true) {
        let y = 2;
        console.log(y); // 2
    }

    console.log(y); // 1
}

В функции example переменная x объявлена с помощью var, поэтому ее значение изменяется и доступно внутри всей функции.

В функции example2 переменная y объявлена с помощью let, поэтому ее значение доступно только внутри блока, в котором она была объявлена. Когда код выходит из блока, значение переменной y снова становится равным 1.

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

В ECMAScript 6 используется для объявления переменных, которые нельзя переназначить. То есть, когда переменная объявлена с помощью const, ее значение не может быть изменено после ее инициализации.

В отличие от let или var, переменные, объявленные с помощью const, не могут быть переназначены. Это означает, что после инициализации значения переменной с помощью const, вы не можете изменить это значение. Кроме того, переменная, объявленная с помощью const, должна быть инициализирована в момент ее объявления.

Например:


const PI = 3.14159265359;
PI = 3.14; // TypeError: Assignment to constant variable.

В этом примере переменная PI объявлена с помощью const и ей присвоено значение числа Пи. Попытка изменить значение PI приведет к ошибке типа “TypeError: Assignment to constant variable”.

Также важно отметить, что переменная, объявленная с помощью const, не может быть повторно объявлена в той же области видимости, и не может иметь того же имени, что и другая переменная, объявленная с помощью let или var.

Использование const предпочтительно в тех случаях, когда вы хотите создать переменную, значение которой не будет изменяться в процессе выполнения программы, например, для объявления констант, таких как значения математических констант, кодов ошибок и других значений, которые не должны меняться.

Стрелочные функции (arrow functions)

Являются новой конструкцией в ECMAScript 6 и предоставляют более короткий и удобный синтаксис для определения функций.

Особенности стрелочных функций:

  • Короткий синтаксис: Стрелочные функции имеют более короткий синтаксис, чем обычные функции, что делает код более понятным и удобным.
  • Не имеют своего контекста: В отличие от обычных функций, у которых есть свой контекст (this), у стрелочных функций нет своего контекста, что позволяет избежать некоторых ошибок и упрощает работу с контекстом.
  • Не могут быть вызваны с new: Стрелочные функции не могут быть вызваны с оператором new, поскольку у них нет своего контекста и не могут создавать новый объект.
  • Не могут использоваться для определения методов объекта: Стрелочные функции не могут использоваться для определения методов объекта, поскольку у них нет своего контекста и this будет ссылаться на глобальный объект.
  • Не имеют своих собственных аргументов: Стрелочные функции не имеют своих собственных аргументов, а используют аргументы из родительской области видимости.
  • Не могут быть именованы: Стрелочные функции не могут быть именованы и должны быть присвоены переменной или использованы как анонимная функция.

Примеры использования:

Создание функции:


const sum = (a, b) => a + b;
console.log(sum(2, 3));

Использование функции внутри метода массива:


const numbers = [1, 2, 3];
const squared = numbers.map(num => num ** 2);
console.log(squared);

Классы (classes)

Классы в JavaScript появились в ECMAScript 6 и представляют собой синтаксический сахар над прототипным наследованием. Классы позволяют создавать объекты с заданными свойствами и методами, а также использовать наследование для создания новых классов на основе существующих.

Создание класса:


class MyClass {
    constructor() {
    // конструктор класса
    }

    myMethod() {
    // метод класса
    }
}

В примере выше определен класс MyClass, который имеет конструктор и метод myMethod. Конструктор выполняется при создании нового объекта на основе класса, а методы доступны всем объектам этого класса.

Создание объекта на основе класса:

const myObject = new MyClass();

В примере выше создается новый объект myObject на основе класса MyClass.

Наследование классов


class ChildClass extends ParentClass {
    constructor() {
        super(); // конструктор класса наследника
    }

    childMethod() {
    // метод класса наследника
    }
}

В примере выше класс ChildClass наследует свойства и методы класса ParentClass. Конструктор класса наследника вызывает конструктор класса-родителя с помощью ключевого слова super. Класс наследника может добавлять свои собственные методы и свойства.

Создание объекта на основе класса-наследника

const myChildObject = new ChildClass();

В примере выше создается новый объект myChildObject на основе класса ChildClass.

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

Rest и Spread операторы

Rest и Spread операторы – это новые возможности в JavaScript, которые появились в ECMAScript 6 и позволяют более гибко работать с массивами и объектами.

Rest оператор

Rest оператор используется для передачи оставшихся аргументов функции в виде массива. Он обозначается тремя точками … и следует за последним аргументом функции.


function myFunction(first, second, ...others) {
    console.log("First:", first);
    console.log("Second:", second);
    console.log("Others:", others);
}

myFunction(1, 2, 3, 4, 5);
// Output:
// First: 1
// Second: 2
// Others: [3, 4, 5]

В примере выше функция myFunction принимает первые два аргумента (first и second) и остальные аргументы собирает в массив others с помощью оператора …. При вызове функции с аргументами 1, 2, 3, 4, 5, первые два аргумента будут присвоены переменным first и second, а остальные аргументы будут собраны в массив others.

Spread оператор

Spread оператор используется для распаковки массивов и объектов, т.е. он превращает массив или объект в список отдельных значений. Он также обозначается тремя точками …

Распаковка массива:


const myArray = [1, 2, 3];
const newArray = [...myArray, 4, 5];

console.log(newArray); // Output: [1, 2, 3, 4, 5]

В примере выше массив myArray распаковывается с помощью оператора … и добавляются два новых элемента 4 и 5. Получившийся массив записывается в переменную newArray.

Распаковка объекта:


const myObject = { a: 1, b: 2, c: 3 };
const newObject = { ...myObject, d: 4, e: 5 };

console.log(newObject); // Output: { a: 1, b: 2, c: 3, d: 4, e: 5 }

В примере выше объект myObject распаковывается с помощью оператора … и добавляются два новых свойства d и e. Получившийся объект записывается в переменную newObject.

Rest и Spread операторы позволяют удобно работать с массивами и объектами и улучшают читаемость и понимание кода.

Деструктуризация (destructuring)

Позволяет извлекать значения из объектов и массивов и присваивать их переменным.

Извлечение значений из объекта:


const user = { name: 'John', age: 30 };
const { name, age } = user; 
console.log(name, age); // Output: 'John', 30 

Извлечение значений из массива:


const numbers = [1, 2, 3];
const [a, b, c] = numbers;
console.log(a, b, c);

Шаблонные строки (template strings)

Улучшенный способ создания строковых литералов начиная со спецификации ES6.

Шаблонные строки позволяют вставлять переменные и выражения в строку без использования оператора конкатенации +.

Шаблонные строки обозначаются обратными кавычками (“`) и могут содержать выражения, заключенные в фигурные скобки {} перед которыми указывается оператор ‘знак долара’ $.

Пример:


const name = "John";
const age = 30;

const message = `My name is ${name} and I'm ${age} years old.`;

console.log(message); // Output: "My name is John and I'm 30 years old."

В примере выше создается строка message, которая содержит значения переменных name и age, вставленные в строку с помощью фигурных скобок и оператора ${}.

Шаблонные строки также могут использоваться для многострочного текста, без необходимости использования символов переноса строки (\n).

Пример:


const message = `
Hello,
World!
`;

console.log(message); // Output: "Hello,\nWorld!"

Шаблонные строки – это удобный и читаемый способ работы со строками в JavaScript, особенно при необходимости вставлять переменные и выражения.

Тип данных BigInt

Новый тип данных, добавленный в ECMAScript 2020 (ES2020), который позволяет представлять целые числа произвольной длины и точности.

В отличие от стандартного числового типа данных number, BigInt может представлять числа больше, чем максимальное значение типа number (2^53 – 1). BigInt поддерживает все стандартные математические операции, такие как сложение, вычитание, умножение и деление.

Для создания значения BigInt используется суффикс n, добавленный к числу.

Пример:


const bigNumber = 1234567890123456789012345678901234567890n;
console.log(bigNumber); // Output: 1234567890123456789012345678901234567890n

BigInt также поддерживает преобразование из других типов данных, таких как строка и number.

Пример:


const str = "1234567890123456789012345678901234567890";
const bigNumberFromString = BigInt(str);
console.log(bigNumberFromString); // Output: 1234567890123456789012345678901234567890n

const number = 1234567890123456789012345678901234567890;
const bigNumberFromNumber = BigInt(number);
console.log(bigNumberFromNumber); // Output: 1234567890123456789012345678901234567890n

Поддерживает сравнение с другими значениями BigInt.

Пример:


const bigNumber1 = 100n;
const bigNumber2 = 200n;

console.log(bigNumber1 < bigNumber2); // Output: true
console.log(bigNumber1 === bigNumber2); // Output: false

Важно отметить, что BigInt и number – это разные типы данных, и нельзя выполнять операции между ними напрямую. Если необходимо производить операции между значениями BigInt и number, необходимо явно преобразовывать типы данных.

Тип данных Symbol

Является примитивным типом данных, который представляет уникальный идентификатор. Он был введен в ECMAScript 6 как способ создания не перечисляемых свойств в объектах.

Символ создается с помощью функции Symbol(), и каждое значение Symbol уникально.

Символы могут использоваться в качестве ключей свойств в объектах. При использовании символа в качестве ключа свойства он создает не перечисляемое свойство, которое нельзя получить с помощью обычных методов доступа к свойствам объекта (например, точечной нотации или квадратных скобок).

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

Символы могут быть использованы в качестве ключей в объектах WeakMap, который является особенным типом объектов, где ключи должны быть объектами и не сохраняются в памяти, если на них нет ссылок.

Объект Math

Представляет математические функции и константы. Перечень наиболее часто используемых функций и констант, доступных в объекте Math:

  • Math.PI: представляет число π (3.141592653589793)
  • Math.abs(x): возвращает абсолютное значение числа x
  • Math.ceil(x): округляет число x в большую сторону до ближайшего целого числа
  • Math.floor(x): округляет число x в меньшую сторону до ближайшего целого числа
  • Math.round(x): округляет число x до ближайшего целого числа
  • Math.max(x1, x2, …, xn): возвращает наибольшее из переданных чисел x1, x2, …, xn
  • Math.min(x1, x2, …, xn): возвращает наименьшее из переданных чисел x1, x2, …, xn
  • Math.pow(x, y): возвращает значение числа x в степени y
  • Math.sqrt(x): возвращает квадратный корень числа x
  • Math.random(): возвращает случайное число между 0 и 1
  • Math.sin(x): возвращает синус угла x
  • Math.cos(x): возвращает косинус угла x
  • Math.tan(x): возвращает тангенс угла x
  • Math.log(x): возвращает натуральный логарифм числа x
  • Math.exp(x): возвращает экспоненту числа x

Пример использования:


const x = 5;
const y = -3;
console.log(Math.abs(y)); // 3
console.log(Math.ceil(x)); // 5
console.log(Math.floor(x)); // 5
console.log(Math.max(x, y)); // 5
console.log(Math.min(x, y)); // -3
console.log(Math.pow(x, 2)); // 25
console.log(Math.sqrt(x)); // 2.23606797749979
console.log(Math.random()); // случайное число между 0 и 1

NaN

В JavaScript NaN означает “Not a Number” (не число) и является значением, которое представляет результат математической операции, которую невозможно выполнить. Например, если мы попытаемся выполнить математическую операцию, используя некорректные аргументы, результатом будет значение NaN.

Например, попытка выполнить деление на ноль:


const result = 1 / 0; // Infinity
const notANumber = 0 / 0; // NaN

NaN используется, чтобы указать, что результат операции не является числом, например, при попытке выполнить математические операции с нечисловыми значениями:


const notANumber1 = 10 / "hello"; // NaN
const notANumber2 = Math.sqrt(-1); // NaN

Для проверки, является ли значение NaN, можно использовать функцию isNaN():


console.log(isNaN(notANumber1)); // true
console.log(isNaN(notANumber2)); // true
console.log(isNaN(10)); // false

console.log(isNaN(notANumber1)); // true
console.log(isNaN(notANumber2)); // true
console.log(isNaN(10)); // false

Заметьте, что функция isNaN() возвращает true также в случае, если аргументом является нечисловое значение, поэтому перед использованием её нужно привести аргумент к числу, например, с помощью функции parseFloat() или parseInt().

Object methods

В JavaScript объект представляет собой коллекцию свойств, каждое из которых имеет имя (строка) и значение, которое может быть любым типом данных, включая другой объект.

Вот несколько методов объекта в JavaScript:

  • Object.keys(obj) – возвращает массив строк, представляющих имена перечисляемых свойств объекта obj.
  • Object.values(obj) – возвращает массив значений перечисляемых свойств объекта obj.
  • Object.entries(obj) – возвращает массив массивов, где каждый подмассив содержит пару “ключ-значение” для каждого перечисляемого свойства объекта obj.
  • Object.assign(target, …sources) – копирует значения всех перечисляемых свойств объекта source в объект target, затирая при этом существующие свойства с тем же именем. Возвращает объект target.
  • Object.freeze(obj) – замораживает объект obj, делая его свойства неизменяемыми.
  • Object.seal(obj) – запечатывает объект obj, предотвращая добавление новых свойств и удаление существующих. Существующие свойства можно изменять.
  • Object.hasOwnProperty(prop) – возвращает логическое значение, указывающее, содержит ли объект obj свойство с именем prop.
  • Object.is(value1, value2) – возвращает логическое значение, указывающее, равны ли value1 и value2. Этот метод использует тот же алгоритм сравнения, что и оператор ===.
  • Object.fromEntries(entries) – создает новый объект, используя массив entries в качестве источника пар “ключ-значение”. Каждый элемент массива entries должен быть подмассивом длиной 2, содержащим ключ и соответствующее ему значение.

Директива “use strict”

Это директива (строка, начинающаяся с “use strict”), которая включает строгий режим (strict mode) в JavaScript. Строгий режим представляет собой набор ограничений, которые обеспечивают более безопасное и четкое выполнение JavaScript кода. Эта директива может быть использована в начале скрипта, функции или блока кода.

Строгий режим включает следующие ограничения:

  • Запрет на использование необъявленных переменных.
  • Запрет на удаление свойств, которые не могут быть удалены.
  • Запрет на переопределение свойств, которые не могут быть переопределены.
  • Запрет на использование ключевых слов в качестве идентификаторов.
  • Запрет на использование функций в качестве конструкторов без использования ключевого слова “new”.
  • Запрет на использование функций с одинаковыми именами в одной области видимости.
  • Запрет на изменение значений некоторых встроенных объектов, таких как Object, Array, Math.
  • Использование директивы “use strict” позволяет разработчикам избегать ошибок и неоднозначностей в своем коде, делая его более читаемым, надежным и безопасным.

Scope

Поднятие (hoisting)

В JavaScript термин “hoisting” означает поведение, при котором объявления переменных и функций перемещаются вверх их соответствующих областей видимости движком JavaScript во время фазы компиляции, до выполнения кода. Это означает, что независимо от того, где в коде были объявлены переменные и функции, они будут доступны во всей области видимости.


function example() {
    console.log(a); // undefined
    var a = 1;
    console.log(a); // 1
}

example();

В этом примере переменная a объявлена после первого console.log(), но до ее присваивания значения 1. Однако благодаря hoisting, объявление переменной a будет перемещено в начало области видимости функции example(), и первый console.log() выведет undefined, потому что переменная еще не была присвоена значению. Второй console.log() выведет 1, потому что значение было присвоено переменной a во время выполнения функции.

Лексическая область видимости (lexical scoping)

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

В JavaScript лексическая область видимости реализуется через использование замыканий (closures). Замыкание создается, когда функция объявляется внутри другой функции, и вложенная функция сохраняет доступ к переменным своей внешней функции, даже после того, как внешняя функция завершила свое выполнение.

Например:


function outerFunction() {
    var outerVar = "I'm in the outer function!";

    function innerFunction() {
        var innerVar = "I'm in the inner function!";
        console.log(innerVar);
        console.log(outerVar);
    }

    innerFunction();
}

outerFunction();

В этом примере функция innerFunction() находится внутри функции outerFunction(). Это означает, что innerFunction() имеет доступ к переменной outerVar, объявленной внутри outerFunction(), благодаря лексической области видимости. Когда мы вызываем innerFunction() внутри outerFunction(), она выводит значения обеих переменных, и выводит:


I'm in the inner function!
I'm in the outer function!

Сборка мусора (garbage collection)

Процесс автоматического поиска и удаления неиспользуемых объектов из памяти компьютера.

В языках программирования, таких как JavaScript, объекты создаются динамически во время выполнения программы, и их память должна быть выделена и освобождена явно. Если объект больше не используется в программе, но память, выделенная для его хранения, не была освобождена, это может привести к утечкам памяти и замедлению работы программы.

Сборка мусора происходит автоматически в JavaScript и некоторых других языках программирования, без явного управления программистом. JavaScript-движок периодически проверяет все объекты в памяти и удаляет те, на которые нет ссылок из активного кода. Ссылка – это переменная или другой объект, который указывает на объект в памяти. Если объект больше не имеет ссылок на себя, он считается неиспользуемым и может быть удален из памяти.

JavaScript не предоставляет явного управления памятью, так как сборка мусора является автоматическим процессом, выполняемым в фоновом режиме внутри JavaScript-движка. Однако есть несколько способов, которыми программист может оптимизировать использование памяти в своем коде:

  1. Использование let и const вместо var – объявление переменных с помощью let и const позволяет ограничить область их видимости, что позволяет JavaScript-движку освобождать память, выделенную для этих переменных, как только они выходят за пределы своей области видимости.
  2. Уменьшение использования глобальных переменных – глобальные переменные могут занимать больше памяти, чем локальные переменные, поэтому программист должен стремиться к уменьшению их использования.
  3. Определение переменных в начале функции – определение всех переменных в начале функции позволяет снизить вероятность утечек памяти и повысить читаемость кода.
  4. Очистка ссылок на объекты – когда объект больше не нужен в программе, ссылки на него должны быть удалены. Это может быть достигнуто путем присвоения переменной значения null или undefined.
  5. Использование методов массивов, таких как splice() и slice(), вместо операторов delete и конкатенации массивов – это может сократить использование памяти.
  6. Использование кэширования и мемоизации для предотвращения повторного вычисления данных и уменьшения использования памяти.

Мемоизация (memoization) в JavaScript – это оптимизация, которая заключается в сохранении результатов выполнения функций для предотвращения повторного вычисления этих результатов в будущем.

Идея мемоизации заключается в том, что при вызове функции с определенными аргументами результат может быть кэширован в памяти, чтобы при следующем вызове функции с теми же самыми аргументами, результат можно было получить непосредственно из кэша без повторного выполнения функции.

Мемоизация может быть полезна для ускорения работы функций, которые имеют дорогостоящие вычисления или обращения к базе данных. Примером может служить функция расчета факториала:


function factorial(n) {
    if (n === 0 || n === 1) {
        return 1;
    } else {
        return n * factorial(n - 1);
    }
}

При больших значениях n эта функция может занимать много времени на выполнение, так как она вычисляет факториал каждого числа заново. Можно оптимизировать эту функцию, используя мемоизацию:


function factorial(n, cache = {}) {
    if (n === 0 || n === 1) {
        return 1;
    } else if (cache[n]) {
        return cache[n];
    } else {
        const result = n * factorial(n - 1, cache);
        cache[n] = result;
        return result;
    }
}

Здесь мы используем дополнительный параметр cache, который хранит результаты выполнения функции для каждого значения n. Если значение уже находится в кэше, мы возвращаем его, а если нет, то вычисляем и сохраняем результат в кэше для последующего использования.

Мемоизация может быть реализована различными способами в JavaScript, например, с помощью замыканий, объектов или декораторов.

В целом, хороший стиль программирования и правильное использование языка помогут избежать утечек памяти и оптимизировать использование памяти в JavaScript.

Объектно-ориентированный подход в javascript (OOP)

constructors vs factories

В JavaScript конструкторы и фабрики (constructors and factories) используются для создания объектов, но они имеют некоторые различия в своей реализации и использовании.

Конструкторы – это функции, которые используются для создания новых объектов. Они используют оператор new и имеют имена, начинающиеся с большой буквы, например:


function Person(name, age) {
    this.name = name;
    this.age = age;
}

Конструкторы создают новый объект this и добавляют свойства и методы в этот объект. Затем они возвращают этот объект по умолчанию, если не был указан другой объект в качестве возвращаемого значения.

Фабрики – это функции, которые также используются для создания объектов, но они возвращают новый объект, не используя оператор new и не создавая конструкторы. Фабрики могут использовать любые другие функции и методы JavaScript для создания объектов. Пример:


function createPerson(name, age) {
    return {
        name: name,
        age: age,
        sayHello: function() {
            console.log('Hello, my name is ' + this.name);
        }
    };
}

Фабрики могут использовать любые другие функции и методы JavaScript для создания объектов, а также могут возвращать различные типы объектов в зависимости от входных данных и условий.

Конструкторы и фабрики имеют различия в том, как они создают и возвращают объекты. Конструкторы создают объект с помощью new, добавляют свойства и методы непосредственно в объект this и возвращают его по умолчанию. Фабрики возвращают новый объект, используя любые другие функции и методы JavaScript для создания объектов, а также могут возвращать различные типы объектов в зависимости от входных данных и условий.

Какой из этих подходов использовать – зависит от задачи, которую вы пытаетесь решить. Если вы хотите создать объекты с помощью новых конструкторов и добавить им методы и свойства непосредственно в объект this, то конструкторы – подходящий выбор. Если же вы хотите создавать объекты с помощью функций и методов JavaScript и возвращать различные типы объектов в зависимости от входных данных и условий, то фабрики могут быть более подходящим выбором.

Inheritance (наследование)

Наследование в JavaScript – это механизм, с помощью которого один объект может унаследовать свойства и методы от другого объекта. Оно достигается через цепочку прототипов, где объекты наследуют свойства и методы от своих прототипов.

Для создания наследования в JavaScript используется ключевое слово extends. С помощью него можно создать класс-наследник, который будет наследовать свойства и методы от родительского класса. Например:


class Animal {
    constructor(name) {
        this.name = name;
    }

    speak() {
        console.log(`${this.name} makes a noise.`);
    }
}

class Dog extends Animal {
    speak() {
        console.log(`${this.name} barks.`);
    }
}

const d = new Dog('Mitzie');
d.speak(); // Mitzie barks.

В этом примере класс Dog является наследником класса Animal, и наследует его свойство name и метод speak(). Однако, метод speak() переопределен в классе Dog, и теперь при вызове метода speak() у объекта класса Dog, выводится сообщение “Mitzie barks.”.

Также можно создать объект, который будет наследовать свойства и методы от другого объекта с помощью метода Object.create(). Например:


const animal = {
    name: '',
    speak() {
        console.log(`${this.name} makes a noise.`);
    }
};

const dog = Object.create(animal);
dog.name = 'Mitzie';
dog.speak(); // Mitzie makes a noise.

В этом примере объект dog наследует свойство name и метод speak() от объекта animal, который был передан методу Object.create().

Прототипное наследование

Прототипное наследование в JavaScript – это механизм наследования, который основывается на использовании прототипов объектов. Каждый объект в JavaScript имеет свойство __proto__, которое указывает на его прототип. Прототип объекта может быть другим объектом, который будет использоваться для наследования свойств и методов.

В прототипном наследовании, если свойство или метод не найдены в самом объекте, JavaScript будет искать их в прототипе объекта, затем в прототипе прототипа и так далее, пока не будет найден искомый элемент или не будет достигнут конец цепочки прототипов.

Например, допустим, у нас есть объект animal, который имеет свойство name и метод speak():


const animal = {
    name: '',
    speak() {
        console.log(`${this.name} makes a noise.`);
    }
};

Мы можем создать новый объект dog, который будет наследовать свойства и методы объекта animal:


const dog = {
    name: 'Mitzie'
};

dog.__proto__ = animal;
dog.speak(); // Mitzie makes a noise.

В этом примере мы устанавливаем прототип объекта dog на объект animal, используя свойство __proto__. Теперь, когда мы вызываем метод speak() у объекта dog, JavaScript сначала ищет этот метод в самом объекте dog. Но поскольку метод не найден, JavaScript продолжает поиск в прототипе объекта dog, который является объектом animal. В итоге метод speak() находится в объекте animal, и выводится сообщение “Mitzie makes a noise.”.

Прототипное наследование также используется в классах, которые были введены в ECMAScript 2015. В классах используется ключевое слово extends, которое указывает, какой класс будет являться прототипом для наследования свойств и методов.

Наследование vs композиция

Наследование и композиция – это два основных концепта объектно-ориентированного программирования.

Наследование – это механизм, который позволяет создавать новый класс на основе существующего класса. Новый класс, известный как подкласс, наследует все свойства и методы существующего класса, известного как суперкласс. Подкласс может добавлять новые свойства и методы или переопределять существующие. Наследование – это способ повторного использования кода и создания более специализированных классов из существующих.

Композиция, с другой стороны, – это механизм, который позволяет объектам состоять из других объектов. Другими словами, объект может содержать один или несколько объектов других классов. Это позволяет создавать более гибкий и модульный код, поскольку объекты могут быть комбинированы различными способами для создания разных систем.

Пример композиции в JavaScript может выглядеть так:

Предположим, у нас есть класс “Машина”, который имеет свойства, такие как “марка”, “модель” и “год выпуска”. Теперь мы хотим создать класс “Водитель”, который имеет свойства “имя”, “возраст” и “опыт вождения”.

Вместо того, чтобы наследовать свойства “марка”, “модель” и “год выпуска” из класса “Машина” в класс “Водитель”, мы можем использовать композицию и создать объект “Машина” внутри объекта “Водитель”. Это означает, что объект “Водитель” будет содержать объект “Машина” как одно из своих свойств.

Код может выглядеть примерно так:


class Car {
    constructor(make, model, year) {
       this.make = make;
       this.model = model;
       this.year = year;
    }
}

class Driver {
    constructor(name, age, experience, car) {
        this.name = name;
        this.age = age;
        this.experience = experience;
        this.car = car;
    }
}

let car = new Car("Toyota", "Camry", 2022);
let driver = new Driver("John", 30, 5, car);

В этом примере, объект “driver” содержит объект “car” в своем свойстве “car”. Таким образом, мы можем получить доступ к свойствам “марка”, “модель” и “год выпуска” машины, используя свойство “car” объекта “driver”, например:


console.log(driver.car.make); // выведет "Toyota"

Одна из главных проблем, которую композиция решает по сравнению с наследованием в JavaScript, связана с тем, что наследование может привести к созданию очень глубокой иерархии классов. Это может привести к тому, что код становится трудным для понимания и поддержки.

Кроме того, наследование приводит к тому, что подкласс наследует все свойства и методы суперкласса, даже те, которые ему не нужны. Это может привести к перегруженности объектов и затратам на ресурсы.

Композиция же позволяет создавать объекты, используя уже существующие объекты и комбинируя их свойства и методы. Таким образом, мы можем создавать более гибкие и легко поддерживаемые объекты, состоящие из более мелких и простых частей. Кроме того, композиция позволяет объектам использовать только те свойства и методы, которые им необходимы, что повышает их эффективность и экономит ресурсы.

В целом, композиция позволяет создавать более гибкие, модульные и легко поддерживаемые объекты, чем наследование. Композиция также является более гибким подходом, который позволяет создавать объекты, которые лучше отвечают специфическим потребностям приложения.

Парадигмы программирования в javascript

JavaScript имеет несколько парадигм программирования, помимо объектно-ориентированного программирования (ООП), включая следующие:

  1. Функциональное программирование: Эта парадигма предполагает использование функций как основной единицы кода, которые могут быть переданы в другие функции и возвращать другие функции в качестве результата. Функциональное программирование обычно используется для создания высокоуровневых и абстрактных функций, которые могут быть повторно использованы в различных частях программы.
  2. Реактивное программирование: Эта парадигма использует потоки данных и событий для обработки изменений в программе. Реактивное программирование используется для создания реактивных интерфейсов, где изменения пользовательского ввода мгновенно обновляют соответствующие части интерфейса.
  3. Асинхронное программирование: JavaScript поддерживает асинхронную обработку кода, где выполнение кода может быть приостановлено, пока ожидается ответ от внешнего API или другой операции. Это делает возможным использование колбэков, обратных вызовов и промисов для управления асинхронным кодом.
  4. Модульное программирование: JavaScript позволяет разделять код на модули, что делает его более организованным и управляемым. Это позволяет разработчикам создавать переиспользуемый код и избегать конфликтов имен и других проблем, связанных с глобальными переменными.
  5. Императивное программирование: Эта парадигма программирования подразумевает написание кода, который описывает, как программа должна выполняться, а не то, что она должна делать. Это стиль программирования, в котором определенная последовательность инструкций изменяет состояние программы.
  6. Декларативное программирование в JavaScript предполагает описание того, что нужно сделать, а не как это делать. В отличие от императивного программирования, где разработчик определяет каждый шаг выполнения программы, декларативное программирование подразумевает определение желаемого результата и использование готовых абстракций для достижения этого результата.

Рекурсия

Рекурсия является одной из фундаментальных концепций функционального программирования, и она широко используется в JavaScript для решения различных задач.

Рекурсия – это процесс, при котором функция вызывает саму себя с некоторыми изменениями параметров. В функциональном программировании рекурсия используется для итерации по структурам данных, таких как списки, деревья и графы.

Вот пример рекурсивной функции на JavaScript, которая вычисляет факториал числа:


function factorial(n) {
  if (n === 0 || n === 1) {
    return 1;
  } else {
    return n * factorial(n - 1);
  }
}

В этом примере, если переданное число равно 0 или 1, функция возвращает 1. В противном случае она вызывает саму себя с аргументом n – 1 и умножает результат на n. Таким образом, при каждом вызове функции factorial аргумент n уменьшается на 1, пока не достигнет 0 или 1.

Рекурсия также может быть использована для обхода и поиска элементов в деревьях и графах, а также для сортировки массивов и других структур данных.

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

Функции map/reduce

Функции map и reduce являются часто используемыми в JavaScript функциями высшего порядка, которые позволяют работать с массивами более эффективно и удобно.

Функция map позволяет применить указанную функцию к каждому элементу массива и получить новый массив с результатами этой операции. Вот пример использования map для возведения в квадрат каждого элемента массива:


const arr = [1, 2, 3, 4, 5];
const squaredArr = arr.map((num) => num * num);
console.log(squaredArr); // [1, 4, 9, 16, 25]

В этом примере мы передаем функцию, которая принимает каждый элемент массива и возвращает его квадрат, в качестве аргумента для функции map. Результатом выполнения map является новый массив, содержащий квадраты исходных элементов.

Функция reduce используется для агрегации значений в массиве и получения единственного результата. Она принимает два аргумента: функцию-аккумулятор и начальное значение. Вот пример использования reduce для нахождения суммы элементов массива:


const arr = [1, 2, 3, 4, 5];
const sum = arr.reduce((acc, num) => acc + num, 0);
console.log(sum); // 15

В этом примере мы передаем функцию, которая принимает аккумулятор и текущий элемент массива, складывает их и возвращает новое значение аккумулятора, в качестве первого аргумента для функции reduce. Вторым аргументом мы передаем начальное значение аккумулятора, равное 0. Результатом выполнения reduce является сумма всех элементов массива.

Функции map и reduce могут быть объединены в цепочки, чтобы выполнить сложные операции на массивах, такие как фильтрация, манипуляции и агрегация данных. Кроме того, они могут быть использованы с функциональными конструкциями, такими как стрелочные функции и лямбда-выражения, для более лаконичного кода.

Mutable/immutable

В программировании существует два типа структур данных: изменяемые (mutable) и неизменяемые (immutable).

Изменяемые структуры данных – это те, которые могут быть изменены после создания. Примерами изменяемых структур данных в JavaScript являются объекты и массивы. При изменении изменяемой структуры данных, её исходное состояние также меняется.


let arr = [1, 2, 3];
arr.push(4);
console.log(arr); // [1, 2, 3, 4]

В этом примере мы создаем массив и добавляем в него новый элемент с помощью метода push. Это изменяет исходный массив, добавляя новый элемент в его конец.

Неизменяемые структуры данных – это те, которые не могут быть изменены после создания. Примерами неизменяемых структур данных в JavaScript являются строки и числа. При изменении неизменяемой структуры данных, создается новая структура данных с новым значением.


let str = "Hello, world!";
let newStr = str.replace("world", "JavaScript");
console.log(str); // "Hello, world!"
console.log(newStr); // "Hello, JavaScript!"

В этом примере мы создаем строку и затем создаем новую строку, заменяя слово “world” на “JavaScript”. Исходная строка не меняется, а создается новая строка с новым значением.

Неизменяемые структуры данных являются предпочтительными в функциональном программировании, так как они помогают избежать ошибок и упрощают проектирование программ. В JavaScript также существуют неизменяемые структуры данных, такие как Map и Set, которые позволяют работать с данными без изменения их исходного состояния.

Чистая функция (pure function)

Чистая функция (pure function) – это функция, которая при одинаковых входных аргументах всегда возвращает одинаковый результат и не имеет побочных эффектов на программу. Другими словами, чистая функция не изменяет состояние программы и не зависит от состояния программы в момент ее вызова.

Пример чистой функции:


function add(a, b) {
  return a + b;
}

В этом примере функция add принимает два аргумента a и b и возвращает их сумму. Эта функция всегда будет возвращать одинаковый результат при одинаковых аргументах, и не изменяет состояние программы.

Пример нечистой функции:


let count = 0;
function increment() {
  count++;
  return count;
}

В этом примере функция increment увеличивает значение переменной count на 1 и возвращает новое значение count. Эта функция изменяет состояние программы, поэтому она не является чистой функцией.

Использование чистых функций в функциональном программировании имеет несколько преимуществ, таких как упрощение тестирования и повышение читаемости и понятности кода. Кроме того, чистые функции являются безопасными для использования в многопоточных или параллельных системах, где изменение состояния программы может привести к ошибкам и непредсказуемому поведению.

Promises

Асинхронное программирование в JavaScript осуществляется с помощью промисов (promises), которые представляют собой обещание (promise) выполнения операции в будущем. Промисы позволяют отложить выполнение операции до тех пор, пока не будут выполнены определенные условия, например, завершение загрузки данных или выполнение других операций.

Промисы представлены классом Promise, который имеет два состояния: pending (ожидание) и fulfilled (выполнено). Когда промис находится в состоянии ожидания, он ожидает выполнения операции, которую он представляет. Когда операция завершается успешно, промис переходит в состояние выполнено и возвращает результат выполнения операции. Если операция не выполнена успешно, промис переходит в состояние rejected (отклонено) и возвращает ошибку.

Пример использования промиса:


let promise = new Promise(function(resolve, reject) {
  // Асинхронная операция, например, загрузка данных
  setTimeout(function() {
    resolve("Данные успешно загружены");
  }, 3000);
});

promise.then(function(result) {
  console.log(result);
}).catch(function(error) {
  console.error(error);
});

В этом примере мы создаем новый промис, который представляет собой асинхронную операцию загрузки данных. Когда данные успешно загружены, промис возвращает строку “Данные успешно загружены”. Мы вызываем метод then на промисе, чтобы обработать результат выполнения операции и выводим его в консоль. Если операция не выполнена успешно, мы используем метод catch для обработки ошибки.

Промисы также позволяют использовать цепочку вызовов для выполнения нескольких операций последовательно:


let promise = loadData()
  .then(function(data) {
    return processData(data);
  })
  .then(function(result) {
    return saveData(result);
  })
  .catch(function(error) {
    console.error(error);
  });

В этом примере мы загружаем данные с помощью функции loadData, затем обрабатываем их с помощью функции processData, сохраняем результат с помощью функции saveData, и обрабатываем любые ошибки, которые могут возникнуть в процессе выполнения операций.

Async/await

Async/await – это функциональность в JavaScript, которая позволяет писать асинхронный код с помощью промисов, упрощая его чтение и написание. По сути, является синтаксическим сахаром над промисами и позволяет писать асинхронный код в стиле синхронного.

В традиционном асинхронном программировании на JavaScript разработчики часто используют обратные вызовы (callbacks). Однако, это может привести к созданию громоздкого и трудночитаемого кода, который может быть труден для отладки и сопровождения.

Async/await, с другой стороны, предоставляет более лаконичный и выразительный способ написания асинхронного кода в JavaScript. Он позволяет писать код, который выглядит как синхронный, но при этом продолжает выполняться асинхронно.

Async/await использует ключевые слова async и await для обозначения асинхронных операций. Ключевое слово async указывается перед функцией, которая содержит асинхронный код, а ключевое слово await используется внутри этой функции для приостановки ее выполнения до тех пор, пока не будет выполнен промис. Когда код встречает ключевое слово await, выполнение функции приостанавливается до тех пор, пока промис, переданный в await, не завершится, и его результат не будет возвращен. Если промис успешно завершается, результат возвращается, и выполнение функции продолжается.

Обработка ошибок в асинхронном коде

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

Для обработки ошибок в асинхронном коде в JavaScript часто используется конструкция try/catch, которая позволяет ловить ошибки в синхронном коде. Однако, когда речь идет об асинхронных операциях, эта конструкция может не сработать.

Вместо этого, для обработки ошибок в асинхронном коде в JavaScript используется конструкция try/catch/finally в сочетании с промисами (promises). В этом случае мы вызываем асинхронную функцию с помощью промиса, а затем используем метод catch для ловли ошибок:


async function getData() {
  try {
    const response = await fetch('https://example.com/data');
    const data = await response.json();
    return data;
  } catch (error) {
    console.error('Ошибка при получении данных:', error);
  } finally {
    console.log('Операция получения данных завершена');
  }
}

В этом примере мы используем try/catch для ловли ошибок при получении данных с помощью метода fetch. Если произошла ошибка, мы выводим ее в консоль с помощью console.error. Затем мы используем блок finally, который будет выполнен независимо от того, произошла ошибка или нет.

Если в асинхронной функции возникла ошибка, промис перейдет в состояние rejected, и управление передастся в блок catch. Если ошибки не произошло, промис перейдет в состояние fulfilled, и управление вернется в функцию getData.

Важно помнить, что try/catch не может ловить ошибки, которые возникают в другой части программы, например, в другой функции. В этом случае нужно использовать конструкцию try/catch в сочетании с промисами внутри каждой асинхронной функции, которая может вызвать ошибку.

ECMAScript 2016-2021

ECMAScript 2016, также известный как ES6, был выпущен в июне 2015 года и внес множество новых функций в язык. Некоторые из ключевых функций, включенных в ES6, включают:

  1. Let и Const для объявления переменных
  2. Стрелочные функции для более короткой записи функций
  3. Расширенный синтаксис объектов, включая сокращенную запись методов и свойств
  4. Шаблонные строки для удобной работы со строками
  5. Деструктуризация для извлечения значений из объектов и массивов
  6. Spread и Rest операторы для удобной работы с массивами и объектами
  7. Классы для создания объектов с помощью прототипного наследования
  8. Модульная система для организации кода в более мелкие и управляемые блоки
  9. Промисы для обработки асинхронного кода
  10. Генераторы для создания итераторов.

Кроме новых возможностей, введенных в ES6, последующие версии стандарта также добавляли новые функции и усовершенствования.

ECMAScript 2017 (ES2017)

Был выпущен в июне 2017 года и включал небольшое количество новых функций, среди которых:

  1. async/await: новый синтаксис для работы с асинхронным кодом, который упрощает написание и понимание асинхронного кода. Он позволяет использовать ключевые слова async и await для написания асинхронных функций и ожидания выполнения асинхронных операций.
  2. Object.values()/Object.entries(): новые методы объекта, которые позволяют получить массив значений или массив пар ключ-значение объекта. Это удобно для итерации по значениям объекта или для преобразования объекта в массив.
  3. String.prototype.padStart()/String.prototype.padEnd(): новые методы строки, которые позволяют добавлять символы в начало или конец строки, чтобы ее длина стала равной заданной. Это удобно для форматирования вывода или для выравнивания текста.
  4. Shared Memory and Atomics: новые API для работы с разделяемой памятью между потоками, которые позволяют эффективно обмениваться данными между несколькими потоками выполнения JavaScript. Это удобно для работы с многопоточными приложениями, такими как игры или приложения для обработки мультимедиа.
  5. trailing commas in function parameter lists and calls: новое правило, которое позволяет добавлять запятую в конец списка параметров функции или вызова функции без ошибки. Это удобно при работе с большими списками параметров, так как позволяет избежать ошибок при добавлении или удалении параметров.

ES2018

  1. Spread и Rest операторы для объектов: Оператор расширения (spread) позволяет развернуть объект и передать его свойства в другой объект или массив. Оператор сбора (rest) позволяет собрать несколько свойств объекта в один новый объект или массив.
  2. Методы объекта Object.entries() и Object.values(): Добавлены методы, которые возвращают массивы ключей и значений объекта соответственно. Это удобно для итерации по объектам и работе с ними.
  3. Функции padStart() и padEnd() для работы со строками: Добавлены функции, которые позволяют дополнять строки пробелами или другими символами в начале или конце строки до заданной длины. Это удобно для форматирования строк и выравнивания текста.
  4. Асинхронные итераторы: Добавлена возможность использовать итераторы для асинхронного кода. Это позволяет работать с асинхронными данными, такими как потоки, в цикле for await…of.
  5. RegExp named capture groups: Добавлена поддержка именованных групп в регулярных выражениях. Это упрощает извлечение данных из текста с помощью регулярных выражений.
  6. Оператор rest для параметров функции: Добавлена поддержка оператора rest для параметров функции, который позволяет передавать переменное количество аргументов в функцию в виде массива.

ES2019

  1. Метод Array.prototype.flat(): Добавлен метод flat(), который позволяет “сгладить” многомерный массив в одномерный массив путем удаления вложенных массивов.
  2. Методы Array.prototype.flatMap() и String.prototype.trimStart()/String.prototype.trimEnd(): Добавлены методы, которые упрощают работу со строками и массивами. Метод flatMap() комбинирует методы flat() и map(), позволяя сразу применять функцию к элементам массива и сгладить его. Методы trimStart() и trimEnd() удаляют пробелы с начала и конца строки.
  3. Деструктуризация объектов с использованием rest-оператора: Ранее, при использовании деструктуризации объектов, нельзя было извлечь оставшиеся свойства в новый объект. В ES2019 добавлен rest-оператор для деструктуризации объектов, который позволяет извлечь оставшиеся свойства и поместить их в новый объект.
  4. Операторы try/catch без аргументов: В ES2019 стало возможным использовать операторы try/catch без указания аргументов. Это позволяет легко и просто обрабатывать ошибки в коде.
  5. Метод Object.fromEntries(): Добавлен метод, который позволяет преобразовать массив пар ключ-значение в объект. Это удобно для преобразования данных из одного формата в другой.

ES2020

  1. Методы объектов, которые позволяют преобразовывать ключи в строковый формат: Добавлены методы Object.fromEntries(), Object.getOwnPropertyDescriptors() и Object.getPrototypeOf(), которые позволяют работать с ключами объектов как со строками.
  2. Optional chaining: Добавлена возможность использования оператора ?. для обращения к свойствам объекта, даже если они не определены. Это упрощает проверку наличия свойств вложенных объектов.
  3. Nullish coalescing: Добавлена возможность использовать оператор ?? для проверки наличия значения переменной, и если ее значение равно null или undefined, заменить его на заданное значение. Это упрощает работу с значениями по умолчанию.
  4. Метод String.prototype.matchAll(): Добавлен метод, который позволяет выполнить глобальный поиск всех совпадений регулярного выражения в строке. Это удобно для поиска всех вхождений итерируемого объекта.
  5. BigInt: Добавлен новый тип данных BigInt, который позволяет работать с целыми числами произвольной длины. Это решает проблему ограничений на размер чисел в JavaScript.
  6. Методы Promise.allSettled() и globalThis: Добавлен метод Promise.allSettled(), который возвращает массив объектов с результатами всех промисов, независимо от того, успешно они завершились или нет. Также был добавлен глобальный объект globalThis, который предоставляет доступ к глобальному объекту независимо от среды выполнения (например, в браузере или на сервере).

ES2021

  1. Методы массивов .flatMap() и .join(): Добавлены новые опции для метода .flatMap() для облегчения обработки данных и новая опция разделителя для метода .join().
  2. Операторы логического присваивания (||= и &&=): Добавлены операторы логического присваивания, которые позволяют упростить код, который присваивает значение переменной только в том случае, если она не определена.
  3. Promise.any(): Добавлен новый метод Promise.any(), который принимает массив промисов и возвращает результат первого завершенного промиса.
  4. WeakRefs: Добавлена возможность создания слабых ссылок на объекты, которые не будут участвовать в работе сборщика мусора.
  5. Числовые разделители: Добавлены разделители для чисел, которые позволяют упростить чтение больших чисел в коде.
  6. String.prototype.replaceAll(): Добавлен метод, который позволяет заменять все вхождения подстроки в строке.

TypeScript

Базовые понятия и принципы

TypeScript – это язык программирования, который расширяет функциональность JavaScript путем добавления статической типизации. Это позволяет облегчить разработку приложений, улучшить качество кода и снизить количество ошибок, связанных с типами данных.

Основные понятия TypeScript:

  • Типы данных: TypeScript поддерживает несколько типов данных, таких как boolean, number, string, object, array и т.д. Это позволяет определить тип переменной, константы, параметра функции или возвращаемого значения функции.
  • Интерфейсы: Интерфейсы в TypeScript определяют форму объекта. Они могут содержать свойства, методы и типы данных, которые объект должен реализовать.
  • Классы: Классы в TypeScript позволяют определять объекты с конкретными свойствами и методами. Они могут иметь конструкторы, статические методы, методы доступа и многое другое.
  • Функции: TypeScript поддерживает типизацию параметров функций и возвращаемых значений. Это позволяет облегчить чтение и понимание кода, а также улучшить качество и безопасность кода.
  • Модули: Модули в TypeScript позволяют группировать связанный код в отдельные файлы для удобства работы. Они могут содержать классы, функции, интерфейсы и т.д.
  • Преобразование типов: TypeScript предоставляет возможность преобразования типов данных, что позволяет управлять потерей данных во время преобразования.

Классы в TS

Классы в TypeScript представляют собой способ определения объектов с конкретными свойствами и методами. Они используются для создания объектов, которые могут иметь состояние и поведение, что делает их удобными для использования в различных приложениях.

Основные концепции, связанные с классами в TypeScript:

  • Конструкторы в TypeScript определяются с помощью ключевого слова “constructor” и используются для инициализации свойств объекта при его создании. Они могут принимать параметры, которые передаются при создании объекта.
  • Свойства класса определяют состояние объекта. Они могут иметь различные типы данных, такие как number, string, boolean и т.д. Они определяются внутри класса, но вне методов.
  • Методы класса определяют поведение объекта. Они могут быть публичными, приватными или защищенными и могут иметь параметры и возвращаемые значения.
  • Классы в TypeScript могут наследовать свойства и методы от других классов. Для этого используется ключевое слово “extends”. Наследующий класс может переопределить методы и свойства родительского класса и добавить свои собственные.
  • Абстрактные классы в TypeScript представляют собой шаблоны для других классов и не могут быть использованы для создания объектов напрямую. Они используются для определения общих свойств и методов, которые должны быть реализованы в наследующих классах.
  • Статические свойства и методы: Статические свойства и методы класса являются общими для всех объектов класса. Они могут быть использованы без создания объекта класса и используются для хранения общих данных и функциональности.

Интерфейсы

Интерфейсы в TypeScript представляют собой сущности, которые описывают форму или формат объекта. Они не являются частью сгенерированного кода JavaScript и не влияют на исполнение программы во время выполнения, но обеспечивают проверку типов во время компиляции.

Интерфейсы в TypeScript определяются с помощью ключевого слова “interface” и содержат список свойств и их типов. Например, простой интерфейс для объекта пользователя может выглядеть так:


interface User {
  name: string;
  age: number;
  email: string;
}

Затем можно использовать этот интерфейс для определения типов объектов, которые соответствуют формату пользовательских данных. Например:


const user: User = {
  name: 'John Smith',
  age: 30,
  email: 'john@example.com'
};

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


interface MathFunction {
  (x: number, y: number): number;
}

Затем можно использовать этот интерфейс для определения типа функции, которая соответствует этой сигнатуре:

const add: MathFunction = (x, y) => x + y;

Интерфейсы также могут расширяться другими интерфейсами, что позволяет создавать более сложные интерфейсы, содержащие несколько свойств и методов.

Использование интерфейсов в TypeScript помогает улучшить структуру кода, сделать его более понятным и легким для поддержки. Проверка типов во время компиляции помогает предотвратить ошибки в работе программы, связанные с неправильным использованием объектов и функций.

Наследование в TS

Наследование – это механизм, позволяющий классу наследовать свойства и методы другого класса. В TypeScript наследование реализуется с помощью ключевого слова extends.

Рассмотрим пример базового класса Animal:


class Animal {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
  move(distanceInMeters: number = 0) {
    console.log(`${this.name} moved ${distanceInMeters}m.`);
  }
}

Затем создадим производный класс Dog, который наследует свойства и методы класса Animal:


class Dog extends Animal {
  bark() {
    console.log('Woof! Woof!');
  }
}

В этом примере класс Dog наследует свойства и методы класса Animal. Кроме того, он определяет новый метод bark(), который не находится в базовом классе Animal.

Теперь можно создать экземпляр класса Dog и использовать как его собственные методы, так и методы, унаследованные от базового класса Animal:


const dog = new Dog('Buddy');
dog.bark(); // "Woof! Woof!"
dog.move(10); // "Buddy moved 10m."

В этом примере метод bark() является уникальным для класса Dog, но метод move() унаследован от базового класса Animal.

Наследование в TypeScript может быть множественным, то есть класс может наследовать свойства и методы сразу от нескольких базовых классов. Однако TypeScript не поддерживает множественное наследование классов с помощью ключевого слова extends. Вместо этого можно использовать комбинацию наследования классов и интерфейсов.

В целом, наследование является одним из ключевых понятий ООП, которое позволяет создавать более гибкий и переиспользуемый код.

Type guards

TypeScript type guards (гварды типов) – это способ сужения типа переменной или выражения внутри условного блока кода. Они используются в TypeScript для улучшения проверки типов во время компиляции и облегчения разработки.

Например, если у вас есть переменная value с типом unknown, вы можете использовать следующий код, чтобы проверить ее тип и выполнить соответствующие действия:


function doSomething(value: unknown) {
  if (typeof value === 'string') {
    // value теперь имеет тип string
    console.log(value.toUpperCase());
  } else if (Array.isArray(value)) {
    // value теперь имеет тип массива
    console.log(value.length);
  } else {
    // value все еще имеет тип unknown
    console.log('Unsupported type');
  }
}

В этом примере typeof и Array.isArray() являются гвардами типов, которые проверяют тип переменной value и позволяют выполнить соответствующие действия внутри условного блока кода.

Mixins

Mixin – это концепция в объектно-ориентированном программировании, которая позволяет объединять функциональность нескольких классов в один класс. В TypeScript mixins реализуются с помощью комбинации классов и миксиновых функций.

Миксин – это функция, которая принимает класс в качестве аргумента и возвращает новый класс с добавленной функциональностью. Для создания миксина в TypeScript можно использовать ключевое слово mixin. Например:


type Constructor = new (...args: any[]) => T;

function Timestamped(Base: T) {
  return class extends Base {
    timestamp = Date.now();
  };
}

class MyClass extends Timestamped(Object) {}

const myInstance = new MyClass();
console.log(myInstance.timestamp); // Выведет текущее время

В этом примере Timestamped – это миксин, который добавляет свойство timestamp с текущим временем к любому классу, который передается ему в качестве аргумента. Класс MyClass наследует функциональность Timestamped, и при создании экземпляра myInstance он получает свойство timestamp.

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

Добавить комментарий