5.01. Работа с объектами
Работа с объектами
Пример класса в JS
class Unit {
constructor() {
this.name = "Имя";
this.intel = 10;
this.agility = 10;
this.strength = 10;
this.health = 100;
this.mana = 50;
this.level = 1;
}
get damage() {
return (this.intel + this.agility + this.strength) + (this.level * 2);
}
attack(target) {
console.log(`${this.name} атакует ${target.name} и наносит ${this.damage} единиц урона.`);
target.health -= this.damage;
console.log(`${target.name} теперь имеет ${target.health} здоровья.`);
}
}
const warrior = new Unit();
warrior.name = "Воин";
warrior.intel = 5;
warrior.agility = 15;
warrior.strength = 30;
const mage = new Unit();
mage.name = "Маг";
mage.intel = 35;
mage.agility = 10;
mage.strength = 5;
warrior.attack(mage);
mage.attack(warrior);
Ключевое слово class определяет новый класс. Внутри фигурных скобок размещаются свойства и методы класса.
Метод constructor вызывается автоматически при создании нового объекта с помощью оператора new. В конструкторе инициализируются начальные значения свойств объекта.
Каждое свойство объекта хранит конкретное значение. Свойства определяются через ключевое слово this, которое ссылается на текущий экземпляр класса.
Метод get damage() представляет собой геттер — специальный метод, который вычисляет значение свойства каждый раз при обращении к нему. Геттер позволяет динамически рассчитывать урон на основе текущих характеристик персонажа.
Метод attack принимает целевой объект в качестве параметра. Внутри метода происходит вывод сообщения о нанесённом уроне и изменение здоровья цели.
Оператор new создаёт новый экземпляр класса. После создания объекта можно изменять значения его свойств, присваивая новые значения напрямую.
Для вызова метода объекта используется точечная нотация: имя объекта, точка, имя метода и круглые скобки с аргументами.
Объекты и классы
В JavaScript, с появлением ES6 (2015) используется «синтаксический сахар» над прототипным наследованием – классы. Это позволяет писать код в более привычном ООП-стиле. Благодаря классам, может быть создан объект (некоторые объекты уже создаются – элементы DOM), и можно получить доступ к свойствам или методам класса. К примеру, можно вызвать класс.метод() и сразу вызывать уже существующие стандартные возможности, а также создавать свои.
Объект JavaScript может быть пользовательским (со своими свойствами и методами) или объектом DOM.
Их можно расширять при помощи миксинов (путем объединения свойств и методов из одного или нескольких исходных объектов). Микс - смешивать, смешивают поведение нескольких в один объект.

К примеру, так выглядит базовый объект:
// Базовый объект
const Объект = {
имя: "Основной объект",
метод() {
console.log("Это метод основного объекта.");
}
};
Если обратиться через название объекта и точку, можно получать свойства и вызывать методы - Объект.имя или Объект.метод().
Вышеприведенный пример называется простым созданием объекта (литерал):
let obj = {
key: "value",
anotherKey: "AnotherValue"
};
Это более сложный тип данных, который можно записать в переменную и придать ему свойства в виде «ключ: “значение”». Это может быть, к примеру, пользователь с присущими ему именем, Email и возрастом:
let user = {
name: "Тимур",
email: "timur@mail.com",
age: 30
};
Почему это называется простым созданием? Потому что есть более сложное - с использованим классов или через функцию конструктор.
Можно создать функцию для того, чтобы создавать объекты по шаблону лишь путем вызова метода-конструктора, которому нужно лишь передать аргументы (соответствующие значениям) и функция сама создаст объект:
function Объект(значение) {
this.ключ = значение;
this.другойКлюч = "значение по умолчанию";
}
let obj1 = new Объект("привет");
let obj2 = new Объект("мир");
console.log(obj1); // { ключ: "привет", другойКлюч: "значение по умолчанию" }
Конструктор будет выступать в роли метода, создающего объект, а команда new означает создание нового объекта. Важно не путать с простым литералом, ведь new Объект это именно функция, а не сам объект.
Объекты нужны для создания комплексного набора данных с разными типами, и при работе с JS в крупных системах, как правило, работа с объектами неизбежна. Важное отличие от примитивов в том, что они работают ссылочно, благодаря чему переменная лишь ссылается на объект. Ссылаться на объект user могут несколько переменных, что позволяет не копировать все значение целиком - память будет использоваться как для одного набора данных. Но давайте пока не будем в это погружаться. Это станет понятно позднее.
У объекта могут быть методы - особые функции, которые являются свойством объекта и хранятся в нём. К примеру, мы сделали объект «собака»:
let собака = {
имя: "Тарзан",
лает: function() {
console.log(this.имя + " говорит: Гав-гав!");
}
};
Здесь «лает» является методом объекта «собака». И «this» — это ключевое слово, которое ссылается на сам объект «собака». В дальнейшем мы просто можем вызывать метод через точку:
собака.лает(); // "Тарзан говорит: Гав-гав!"
В ES6 появился и более удобный способ:
let собака = {
имя: "Тарзан",
лает() {
console.log(this.имя + " говорит: Гав-гав!");
},
ест(еда) {
console.log(this.имя + " ест " + еда);
}
};
собака.лает(); // Гав-гав!
собака.ест("корм"); // Тарзан ест корм
Причем метод можно добавить и позднее, не обязательно при создании объекта. К примеру, сначала создать объект, потом добавить метод и вызывать его:
let кот = {
имя: "Барсик"
};
// Добавляем метод позже
кот.мяукает = function() {
console.log(this.имя + " говорит: Мяу!");
};
кот.мяукает(); // "Барсик говорит: Мяу!"
this — это ссылка на объект, который вызывает функцию. Это называется контекст выполнения - this определяется в момент вызова функции, а не при её создании и зависит от способа вызова функции.
Если вызывать this в методе объекта, то this = сам объект. В больших проектах работа с объектами позволит использовать автодополнение в браузере или IDE, когда можно будет указать «кот.» и IDE выведет доступные свойства и методы.
Ранее мы упоминали, что существует деструктивное присваивание — это может быть применено и к объектам. Пример:
let user = { name: "Боб", age: 30 };
let { name, age } = user;
console.log(name); // "Боб"
console.log(age); // 30
prototype — это механизм наследования в JavaScript, основанный на цепочке прототипов (prototype chain). Каждая функция в JS имеет свойство prototype, которое используется, когда функция используется как конструктор (с new).
prototype — это свойство функции-конструктора.
__proto__ — это свойство объекта, указывающее на его прототип (устаревшее, но работает; современный аналог — Object.getPrototypeOf()).
superclass — это родительский класс, от которого наследуется другой класс (дочерний, или подкласс).
Этот термин чаще используется в контексте классов (ES6+) и наследования через extends.
Получение свойств и методов объекта
В JavaScript очень часто приходится работать с объектами, чья структура неочевидна — это может быть объект из API, библиотеки, фреймворка (React, Vue, Express), или даже this в сложной цепочке вызовов. И на практике сталкиваться можно с непониманием того, что есть у объекта. Узнать, какие свойства или методы есть у объекта, можно несколькими способами.
- Консоль. Можно открыть консоль в инструментах разработчика (DevTools, F12), поставить точку останова на нужной строке и запустить выполнение кода. После этого, в консоли можно просто написать
thisи поставить точку - после точки будет отображено всё возможное - так работает автодополнение:

- Дерево объекта. Также можно в браузере написать
console.log(myObject); - после чего в консоли браузера увидим интерактивное дерево объекта - по нему можно кликать, раскрывать свойства, смотреть методы.

А если написать console.dir(myObject) — это покажет только свойства и методы, без лишней информации. Можете открыть любой сайт и попробовать протестировать, написав console.log(this) или console.dir(this). Если навести мышкой на объект - подсказка покажет тип и структуру.
Если при поиске объекта, к примеру myObject.test() получаем ошибку «Uncaught TypeError: myObject.test is not a function», значит такой функции нет. А если напишем myObject.test, но такого свойства нет, то получим undefined - «неопределенно».
- Можно использовать методы для перечисления свойств, что позволит программно узнать, что внутри объекта:
Object.keys(obj)вернёт массив собственных перечисляемых свойств ([‘name’, ‘age’]);Object.values(obj)вернёт массив значений свойств ([‘Тимур’, ‘30’]);Object.entries(obj)вернет свойства целиком ([‘name’, ‘Тимур’],[‘age’, ‘30’]);for…inпозволит перебирать свойства, к примеру:
for (let key in user) {
console.log(key, user[key]);
}
Object.getOwnPropertyNames(obj)покажет все собственные свойства, включая неперечисляемые;Reflect.ownKeys(obj)покажет все собственные ключи, включая символы (Symbol) и неперечисляемые.
- VS Code и другие IDE. Если объект имеет типизацию (например, JSDoc, TypeScript или встроен в JS (базовый объект), то VS Code покажет подсказки при наведении и автодополнении. К примеру:
const arr = [1, 2, 3];
arr. // → сразу выпадает список методов: push, pop, map, filter и т.д.
Если же объект из библиотеки или фреймворка, то разумеется, сначала следует прочитать документацию, которая всегда описывает структуру объектов. И опять же, можно смотреть в консоли, как описано выше.
Миксин
Мы можем создать миксины:
// Миксин 1
const Миксин1 = {
миксин1Метод() {
console.log("Это метод из Миксина 1.");
}
};
// Миксин 2
const Миксин2 = {
миксин2Метод() {
console.log("Это метод из Миксина 2.");
}
};
Теперь это два «дополнительных объекта», которые имеют свои методы миксин1Метод() и миксин2Метод().
После этого мы можем либо использовать Object.assign, чтобы скопировать свойства и методы из Миксин1 и Миксин2 в Объект:
// Применяем миксины напрямую через Object.assign
Object.assign(Объект, Миксин1, Миксин2);
// Используем расширенный объект
Объект.метод(); // Это метод основного объекта.
Объект.миксин1Метод(); // Это метод из Миксина 1.
Объект.миксин2Метод(); // Это метод из Миксина 2.
…либо использовать функцию смешивания:
// Функция для смешивания (применения миксинов)
function применитьМиксины(целевойОбъект, ...миксины) {
миксины.forEach(миксин => {
Object.assign(целевойОбъект, миксин);
});
}
// Применяем миксины к объекту
применитьМиксины(Объект, Миксин1, Миксин2);
Функция смешивания актуальна как посредник для случаев, когда объектов может быть несколько и нужно часто применять их к разным объектам, или когда работа ведётся с большим количеством миксинов. Она используется для упрощения процесса объединения свойств и методов из нескольких миксинов в один объект.
// Применяем миксины к объектам
применитьМиксины(Объект1, Миксин1, Миксин2);
применитьМиксины(Объект2, Миксин1);
// Используем расширенные объекты
Объект1.метод(); // Это метод первого объекта.
Объект1.миксин1Метод(); // Это метод из Миксина 1.
Объект1.миксин2Метод(); // Это метод из Миксина 2.
Объект2.метод(); // Это метод второго объекта.
Объект2.миксин1Метод(); // Это метод из Миксина 1.
// Объект2.миксин2Метод(); // Ошибка: метод миксин2Метод не определён, так как Миксин2 не был применён.
Базовые операции с объектами и классами
Объявление класса:
class Person {
// Конструктор (вызывается при создании объекта)
constructor(name, age) {
this.name = name; // Свойство
this.age = age;
}
// Метод
greet() {
return `Привет, я ${this.name}!`;
}
}
Создание объекта (экземпляра класса):
const person = new Person('Том', 25);
console.log(person.greet()); // "Привет, я Том!"
Свойства – это переменные, принадлежащие объекту/классу.
Публичные свойства доступны извне класса:
class Car {
constructor(brand) {
this.brand = brand; // Публичное свойство
}
}
const myCar = new Car('Toyota');
console.log(myCar.brand); // "Toyota"
Приватные свойства (с префиксом #) доступны только внутри класса:
class User {
#password; // Приватное свойство
constructor(login, password) {
this.login = login;
this.#password = password;
}
}
const user = new User('admin', '12345');
console.log(user.#password); // Ошибка!
Статические свойства (static) принадлежат классу, а не экземплярам:
class MathUtils {
static PI = 3.14; // Статическое свойство
}
console.log(MathUtils.PI); // 3.14
★ Методы – это функции, принадлежащие классу/объекту.
Публичные методы, как и свойства, доступны извне. Приватные методы, как и свойства, доступны внутри класса и имеют префикс #.
★ Пример – класс для работы с API:
class ApiClient {
constructor(baseUrl) {
this.baseUrl = baseUrl;
}
async get(endpoint) {
const response = await fetch(`${this.baseUrl}/${endpoint}`);
return response.json();
}
async post(endpoint, data) {
const response = await fetch(`${this.baseUrl}/${endpoint}`, {
method: 'POST',
body: JSON.stringify(data)
});
return response.json();
}
}
// Использование
const api = new ApiClient('https://api.example.com');
api.get('users').then(users => console.log(users));
Создание объектов
Литералы объектов
Литерал объекта — это выражение, создающее объект с указанными свойствами и методами:
const user = {
name: "Алексей",
age: 28,
greet() {
return `Привет, меня зовут ${this.name}`;
}
};
Конструкторы через функции
Функция-конструктор создаёт объекты с заданной структурой:
function User(name, age) {
this.name = name;
this.age = age;
this.greet = function() {
return `Привет, меня зовут ${this.name}`;
};
}
const user1 = new User("Мария", 25);
const user2 = new User("Иван", 30);
Object.create()
Метод Object.create() создаёт новый объект с указанным прототипом:
const parent = {
greet() {
return `Привет от ${this.name}`;
}
};
const child = Object.create(parent);
child.name = "Дочерний объект";
console.log(child.greet()); // "Привет от Дочерний объект"
Свойства объектов
Геттеры и сеттеры
Геттеры и сеттеры позволяют контролировать доступ к свойствам:
const user = {
_name: "Анна",
_age: 25,
get name() {
return this._name.toUpperCase();
},
set name(value) {
if (value.length > 2) {
this._name = value;
}
},
get age() {
return this._age;
},
set age(value) {
if (value >= 0 && value <= 120) {
this._age = value;
}
}
};
console.log(user.name); // "АННА"
user.name = "Петр";
console.log(user.name); // "ПЕТР"
Дескрипторы свойств
Дескрипторы описывают поведение свойств объекта:
const obj = {};
Object.defineProperty(obj, 'name', {
value: 'Тест',
writable: false, // нельзя изменить
enumerable: true, // видно в циклах
configurable: false // нельзя удалить или изменить дескриптор
});
obj.name = 'Новое'; // Не сработает
console.log(obj.name); // "Тест"
Полный пример с геттером и сеттером через дескриптор:
const person = {};
let _age = 0;
Object.defineProperty(person, 'age', {
get() {
return _age;
},
set(value) {
if (value >= 0 && value <= 120) {
_age = value;
}
},
enumerable: true,
configurable: true
});
person.age = 25;
console.log(person.age); // 25
person.age = 200; // Не сработает
console.log(person.age); // 25
Прототипное наследование
Цепочка прототипов
Каждый объект имеет внутреннее свойство [[Prototype]], ссылающееся на другой объект — прототип. При обращении к свойству объекта, если его нет в самом объекте, поиск продолжается в прототипе:
const animal = {
eats: true,
walk() {
console.log("Животное ходит");
}
};
const rabbit = Object.create(animal);
rabbit.jumps = true;
rabbit.walk(); // "Животное ходит" — метод из прототипа
console.log(rabbit.eats); // true — свойство из прототипа
console.log(rabbit.jumps); // true — собственное свойство
Изменение прототипа
Современный способ изменения прототипа — Object.setPrototypeOf():
const parent = { parentProp: "родитель" };
const child = { childProp: "ребёнок" };
Object.setPrototypeOf(child, parent);
console.log(child.parentProp); // "родитель"
Проверка прототипа
Метод isPrototypeOf() проверяет, является ли объект прототипом другого:
const parent = {};
const child = Object.create(parent);
console.log(parent.isPrototypeOf(child)); // true
console.log(Object.prototype.isPrototypeOf(child)); // true
Методы объекта Object
Object.keys(), values(), entries()
Эти методы возвращают массивы ключей, значений и пар ключ-значение:
const user = {
name: "Олег",
age: 30,
city: "Москва"
};
console.log(Object.keys(user)); // ["name", "age", "city"]
console.log(Object.values(user)); // ["Олег", 30, "Москва"]
console.log(Object.entries(user)); // [["name","Олег"],["age",30],["city","Москва"]]
Object.assign()
Копирует свойства из одного или нескольких источников в целевой объект:
const target = { a: 1 };
const source1 = { b: 2 };
const source2 = { c: 3 };
Object.assign(target, source1, source2);
console.log(target); // { a: 1, b: 2, c: 3 }
Глубокое копирование требует рекурсивного подхода:
function deepClone(obj) {
if (obj === null || typeof obj !== 'object') {
return obj;
}
if (obj instanceof Date) {
return new Date(obj);
}
if (obj instanceof Array) {
return obj.map(item => deepClone(item));
}
if (obj instanceof Object) {
const clonedObj = {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
clonedObj[key] = deepClone(obj[key]);
}
}
return clonedObj;
}
return obj;
}
Object.defineProperty() и Object.defineProperties()
Определяют или изменяют свойства объекта с точным контролем:
const obj = {};
Object.defineProperties(obj, {
name: {
value: "Тест",
writable: true,
enumerable: true,
configurable: true
},
id: {
value: 1,
writable: false,
enumerable: false,
configurable: false
}
});
Object.freeze(), seal(), preventExtensions()
Эти методы ограничивают изменение объекта:
const obj = { name: "Тест" };
// Запрещает добавление новых свойств
Object.preventExtensions(obj);
obj.newProp = "value"; // Не сработает в строгом режиме
// Запрещает добавление и удаление свойств
const sealed = { name: "Тест" };
Object.seal(sealed);
sealed.newProp = "value"; // Не сработает
delete sealed.name; // Не сработает
// Полностью блокирует изменения
const frozen = { name: "Тест" };
Object.freeze(frozen);
frozen.name = "Новое"; // Не сработает
frozen.newProp = "value"; // Не сработает
delete frozen.name; // Не сработает
Object.hasOwnProperty()
Проверяет, является ли свойство собственным (не унаследованным):
const parent = { parentProp: "родитель" };
const child = Object.create(parent);
child.childProp = "ребёнок";
console.log(child.hasOwnProperty("childProp")); // true
console.log(child.hasOwnProperty("parentProp")); // false
console.log("parentProp" in child); // true — проверяет включая прототипы
Function как конструктор
Функция в JavaScript может выступать в роли конструктора при вызове с оператором new:
function Person(name, age) {
this.name = name;
this.age = age;
this.greet = function() {
return `Привет, меня зовут ${this.name}`;
};
}
const person = new Person("Сергей", 35);
console.log(person.name); // "Сергей"
console.log(person.greet()); // "Привет, меня зовут Сергей"
Свойство prototype функции-конструктора становится прототипом созданных объектов:
function Car(brand) {
this.brand = brand;
}
Car.prototype.start = function() {
console.log(`${this.brand} заводится`);
};
const myCar = new Car("Toyota");
myCar.start(); // "Toyota заводится"
Синтаксис классов
Базовый класс
Класс — это шаблон для создания объектов:
class Rectangle {
constructor(width, height) {
this.width = width;
this.height = height;
}
getArea() {
return this.width * this.height;
}
getPerimeter() {
return 2 * (this.width + this.height);
}
}
const rect = new Rectangle(10, 5);
console.log(rect.getArea()); // 50
console.log(rect.getPerimeter()); // 30
Конструктор
Конструктор инициализирует новый объект:
class User {
constructor(name, email) {
this.name = name;
this.email = email;
this.createdAt = new Date();
}
getInfo() {
return `${this.name} (${this.email})`;
}
}
const user = new User("Анна", "anna@example.com");
console.log(user.getInfo()); // "Анна (anna@example.com)"
Наследование через extends
Ключевое слово extends создаёт дочерний класс:
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} издаёт звук`);
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name); // Вызов конструктора родителя
this.breed = breed;
}
bark() {
console.log(`${this.name} лает`);
}
}
const dog = new Dog("Рекс", "Овчарка");
dog.speak(); // "Рекс издаёт звук"
dog.bark(); // "Рекс лает"
Методы super
Ключевое слово super обращается к методам родительского класса:
class Parent {
greet() {
return "Привет от родителя";
}
}
class Child extends Parent {
greet() {
return super.greet() + " и от ребёнка";
}
}
const child = new Child();
console.log(child.greet()); // "Привет от родителя и от ребёнка"
В конструкторе дочернего класса super() должен вызываться до использования this:
class Parent {
constructor(name) {
this.name = name;
}
}
class Child extends Parent {
constructor(name, age) {
super(name); // Обязательно первым
this.age = age;
}
}
Статические методы и поля
Статические члены принадлежат классу, а не экземплярам:
class MathUtils {
static PI = 3.14159;
static add(a, b) {
return a + b;
}
static multiply(a, b) {
return a * b;
}
}
console.log(MathUtils.PI); // 3.14159
console.log(MathUtils.add(5, 3)); // 8
console.log(MathUtils.multiply(4, 2)); // 8
// Нельзя вызывать статические методы через экземпляр
const utils = new MathUtils();
// utils.add(1, 2); // Ошибка
Приватные поля и методы
Приватные члены доступны только внутри класса:
class BankAccount {
#balance = 0;
#pinCode;
constructor(owner, pinCode) {
this.owner = owner;
this.#pinCode = pinCode;
}
#validatePin(pin) {
return pin === this.#pinCode;
}
deposit(amount) {
if (amount > 0) {
this.#balance += amount;
return true;
}
return false;
}
withdraw(amount, pin) {
if (this.#validatePin(pin) && amount <= this.#balance) {
this.#balance -= amount;
return true;
}
return false;
}
getBalance(pin) {
if (this.#validatePin(pin)) {
return this.#balance;
}
return "Неверный пин-код";
}
}
const account = new BankAccount("Иван", "1234");
account.deposit(1000);
console.log(account.getBalance("1234")); // 1000
console.log(account.getBalance("0000")); // "Неверный пин-код"
// console.log(account.#balance); // Ошибка
Статические блоки инициализации
Статические блоки выполняют сложную инициализацию статических полей:
class Config {
static DEFAULT_TIMEOUT;
static MAX_RETRIES;
static API_URL;
static {
try {
const env = process.env.NODE_ENV || 'development';
if (env === 'production') {
this.DEFAULT_TIMEOUT = 30000;
this.MAX_RETRIES = 3;
this.API_URL = 'https://api.example.com';
} else {
this.DEFAULT_TIMEOUT = 5000;
this.MAX_RETRIES = 1;
this.API_URL = 'https://dev-api.example.com';
}
console.log('Конфигурация загружена');
} catch (error) {
console.error('Ошибка инициализации конфигурации:', error);
this.DEFAULT_TIMEOUT = 10000;
this.MAX_RETRIES = 2;
this.API_URL = 'https://fallback-api.example.com';
}
}
}
console.log(Config.API_URL);
Практические примеры
Построение иерархии классов
class Shape {
constructor(color = 'black') {
this.color = color;
}
draw() {
console.log(`Рисую фигуру цветом ${this.color}`);
}
}
class Circle extends Shape {
constructor(radius, color) {
super(color);
this.radius = radius;
}
getArea() {
return Math.PI * this.radius * this.radius;
}
draw() {
console.log(`Рисую круг радиусом ${this.radius}`);
super.draw();
}
}
class Rectangle extends Shape {
constructor(width, height, color) {
super(color);
this.width = width;
this.height = height;
}
getArea() {
return this.width * this.height;
}
draw() {
console.log(`Рисую прямоугольник ${this.width}x${this.height}`);
super.draw();
}
}
const circle = new Circle(5, 'red');
const rect = new Rectangle(10, 20, 'blue');
circle.draw();
console.log(`Площадь круга: ${circle.getArea()}`);
rect.draw();
console.log(`Площадь прямоугольника: ${rect.getArea()}`);
Фабрика объектов
class UserFactory {
static #userCount = 0;
static createUser(type, name, email) {
this.#userCount++;
switch(type) {
case 'admin':
return new AdminUser(name, email, this.#userCount);
case 'guest':
return new GuestUser(name, email, this.#userCount);
default:
return new RegularUser(name, email, this.#userCount);
}
}
static getUserCount() {
return this.#userCount;
}
}
class BaseUser {
constructor(name, email, id) {
this.name = name;
this.email = email;
this.id = id;
this.createdAt = new Date();
}
getInfo() {
return `${this.name} (${this.email})`;
}
}
class AdminUser extends BaseUser {
#permissions = ['read', 'write', 'delete', 'admin'];
hasPermission(permission) {
return this.#permissions.includes(permission);
}
getRole() {
return 'Administrator';
}
}
class GuestUser extends BaseUser {
#permissions = ['read'];
hasPermission(permission) {
return this.#permissions.includes(permission);
}
getRole() {
return 'Guest';
}
}
class RegularUser extends BaseUser {
#permissions = ['read', 'write'];
hasPermission(permission) {
return this.#permissions.includes(permission);
}
getRole() {
return 'User';
}
}
const admin = UserFactory.createUser('admin', 'Админ', 'admin@example.com');
const guest = UserFactory.createUser('guest', 'Гость', 'guest@example.com');
const user = UserFactory.createUser('user', 'Пользователь', 'user@example.com');
console.log(`Создано пользователей: ${UserFactory.getUserCount()}`);
console.log(`${admin.getInfo()} - ${admin.getRole()}`);
console.log(`${guest.getInfo()} - ${guest.getRole()}`);
console.log(`${user.getInfo()} - ${user.getRole()}`);
Базовые объекты
Базовые встроенные объекты
★ Базовые встроенные объекты
JavaScript предоставляет стандартные объекты-конструкторы для работы с разными типами данных:
| Объект | Описание | Пример использования |
|---|---|---|
Array | Работа с массивами | [1, 2, 3].map(x => x * 2) |
Boolean | Логические значения | new Boolean(true) |
Math | Математические операции | Math.PI, Math.random() |
Number | Числа и методы | Number.parseInt("42") |
String | Строки и методы | "Hello".toUpperCase() |
Global | Глобальные функции | isNaN(), eval() |
Глобальные свойства
globalThis
Глобальный объект globalThis обеспечивает единый способ доступа к глобальному объекту в разных средах выполнения JavaScript. В браузере глобальным объектом является window, в Node.js — global, а globalThis работает везде одинаково:
// В браузере
console.log(globalThis === window); // true
// В Node.js
console.log(globalThis === global); // true
// Универсальный доступ
globalThis.myGlobalVar = "Значение";
console.log(globalThis.myGlobalVar); // "Значение"
Infinity
Свойство Infinity представляет бесконечность — числовое значение, большее любого другого числа:
console.log(Infinity); // Infinity
console.log(10 / 0); // Infinity
console.log(Infinity + 1); // Infinity
console.log(Infinity * 2); // Infinity
console.log(Infinity - Infinity); // NaN
// Проверка на бесконечность
console.log(isFinite(Infinity)); // false
console.log(isFinite(100)); // true
console.log(Number.isFinite(Infinity)); // false
Отрицательная бесконечность:
console.log(-Infinity); // -Infinity
console.log(-10 / 0); // -Infinity
NaN
NaN (Not-a-Number) представляет результат неопределённой или некорректной математической операции:
console.log(NaN); // NaN
console.log(0 / 0); // NaN
console.log(Math.sqrt(-1)); // NaN
console.log(parseInt("текст")); // NaN
console.log("строка" * 5); // NaN
Особенность NaN — он не равен ничему, включая самого себя:
console.log(NaN === NaN); // false
console.log(NaN == NaN); // false
Проверка на NaN:
console.log(isNaN(NaN)); // true
console.log(isNaN("текст")); // true — преобразует в число сначала
console.log(isNaN(123)); // false
// Более точная проверка
console.log(Number.isNaN(NaN)); // true
console.log(Number.isNaN("текст")); // false — не преобразует
console.log(Number.isNaN(123)); // false
undefined
undefined означает, что переменная объявлена, но не инициализирована, или свойство объекта отсутствует:
let x;
console.log(x); // undefined
console.log(typeof x); // "undefined"
const obj = {};
console.log(obj.nonExistentProperty); // undefined
function noReturn() {}
console.log(noReturn()); // undefined
Проверка на undefined:
let value;
if (value === undefined) {
console.log("Значение не определено");
}
// Безопасная проверка
if (typeof value === "undefined") {
console.log("Переменная не определена");
}
Глобальные функции
eval()
Функция eval() выполняет строку как код JavaScript:
const code = "console.log('Привет, мир!')";
eval(code); // "Привет, мир!"
const result = eval("2 + 2 * 3");
console.log(result); // 8
const x = 10;
const y = 20;
console.log(eval("x + y")); // 30
Опасности использования eval():
- Выполнение непроверенного кода создаёт уязвимости
- Замедляет выполнение программы
- Нарушает безопасность приложения
- Затрудняет отладку кода
Безопасные альтернативы:
// Вместо eval для вычисления математических выражений
const expression = "2 + 2 * 3";
const result = Function('"use strict";return (' + expression + ')')();
console.log(result); // 8
// Для парсинга JSON
const jsonString = '{"name":"Иван","age":30}';
const obj = JSON.parse(jsonString);
console.log(obj.name); // "Иван"
isFinite()
Функция isFinite() проверяет, является ли значение конечным числом:
console.log(isFinite(123)); // true
console.log(isFinite(-123)); // true
console.log(isFinite(0)); // true
console.log(isFinite(Infinity)); // false
console.log(isFinite(-Infinity)); // false
console.log(isFinite(NaN)); // false
// Строки преобразуются в числа
console.log(isFinite("123")); // true
console.log(isFinite("12.5")); // true
console.log(isFinite("текст")); // false
console.log(isFinite("")); // true (пустая строка → 0)
Более строгая проверка через Number.isFinite():
console.log(Number.isFinite(123)); // true
console.log(Number.isFinite("123")); // false — не преобразует строку
console.log(Number.isFinite(NaN)); // false
isNaN()
Функция isNaN() проверяет, является ли значение NaN:
console.log(isNaN(NaN)); // true
console.log(isNaN("текст")); // true — "текст" → NaN
console.log(isNaN(123)); // false
console.log(isNaN("123")); // false — "123" → 123
console.log(isNaN("")); // false — пустая строка → 0
console.log(isNaN(" ")); // false — пробел → 0
Разница между isNaN() и Number.isNaN():
console.log(isNaN("привет")); // true
console.log(Number.isNaN("привет")); // false — не преобразует
console.log(isNaN(NaN)); // true
console.log(Number.isNaN(NaN)); // true
console.log(isNaN(undefined)); // true
console.log(Number.isNaN(undefined)); // false
parseInt()
Функция parseInt() преобразует строку в целое число:
console.log(parseInt("42")); // 42
console.log(parseInt("42px")); // 42
console.log(parseInt("10.5")); // 10 — дробная часть отбрасывается
console.log(parseInt("0xFF")); // 255 — шестнадцатеричное
console.log(parseInt("10", 2)); // 2 — двоичная система
console.log(parseInt("10", 8)); // 8 — восьмеричная система
console.log(parseInt("10", 16)); // 16 — шестнадцатеричная система
// Обработка некорректных значений
console.log(parseInt("текст")); // NaN
console.log(parseInt("")); // NaN
console.log(parseInt(" 42 ")); // 42 — пробелы игнорируются
Явное указание системы счисления:
console.log(parseInt("10", 10)); // 10
console.log(parseInt("FF", 16)); // 255
console.log(parseInt("77", 8)); // 63
console.log(parseInt("1010", 2)); // 10
parseFloat()
Функция parseFloat() преобразует строку в число с плавающей точкой:
console.log(parseFloat("3.14")); // 3.14
console.log(parseFloat("3.14px")); // 3.14
console.log(parseFloat("10")); // 10
console.log(parseFloat("10.00")); // 10
console.log(parseFloat("2.5e3")); // 2500 — экспоненциальная запись
console.log(parseFloat("Infinity")); // Infinity
// Обработка некорректных значений
console.log(parseFloat("текст")); // NaN
console.log(parseFloat("")); // NaN
console.log(parseFloat(" 3.14 ")); // 3.14
Разница между parseInt() и parseFloat():
console.log(parseInt("10.99")); // 10 — только целая часть
console.log(parseFloat("10.99")); // 10.99 — сохраняет дробную часть
console.log(parseInt("3.5e2")); // 3
console.log(parseFloat("3.5e2")); // 350
Кодирование и декодирование URI
Функции для работы с URL и URI:
encodeURI()
Кодирует полный URI, сохраняя разделители:
const uri = "https://сайт.рф/путь?параметр=значение с пробелами";
console.log(encodeURI(uri));
// "https://сайт.рф/путь?параметр=значение%20с%20пробелами"
// Сохраняет разделители URI
console.log(encodeURI("https://example.com/путь/страница"));
// "https://example.com/%D0%BF%D1%83%D1%82%D1%8C/%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86%D0%B0"
encodeURIComponent()
Кодирует компонент URI, включая разделители:
const component = "значение с пробелами и / символами";
console.log(encodeURIComponent(component));
// "значение%20с%20пробелами%20и%20%2F%20символами"
// Используется для кодирования параметров запроса
const params = {
name: "Иван Петров",
city: "Москва/Санкт-Петербург"
};
const queryString = Object.entries(params)
.map(([key, value]) =>
`${encodeURIComponent(key)}=${encodeURIComponent(value)}`
)
.join("&");
console.log(queryString);
// "name=%D0%98%D0%B2%D0%B0%D0%BD%20%D0%9F%D0%B5%D1%82%D1%80%D0%BE%D0%B2&city=%D0%9C%D0%BE%D1%81%D0%BA%D0%B2%D0%B0%2F%D0%A1%D0%B0%D0%BD%D0%BA%D1%82-%D0%9F%D0%B5%D1%82%D0%B5%D1%80%D0%B1%D1%83%D1%80%D0%B3"
decodeURI()
Декодирует полный URI:
const encoded = "https://сайт.рф/путь?параметр=значение%20с%20пробелами";
console.log(decodeURI(encoded));
// "https://сайт.рф/путь?параметр=значение с пробелами"
decodeURIComponent()
Декодирует компонент URI:
const encodedComponent = "значение%20с%20пробелами%20и%20%2F%20символами";
console.log(decodeURIComponent(encodedComponent));
// "значение с пробелами и / символами"
// Пример декодирования параметров
const url = "https://example.com/?name=%D0%98%D0%B2%D0%B0%D0%BD&city=%D0%9C%D0%BE%D1%81%D0%BA%D0%B2%D0%B0";
const paramsString = url.split("?")[1];
const paramsArray = paramsString.split("&");
const params = {};
paramsArray.forEach(param => {
const [key, value] = param.split("=");
params[decodeURIComponent(key)] = decodeURIComponent(value);
});
console.log(params);
// { name: "Иван", city: "Москва" }
Разница между encodeURI() и encodeURIComponent():
const url = "https://example.com/путь?параметр=значение с пробелами";
console.log(encodeURI(url));
// "https://example.com/%D0%BF%D1%83%D1%82%D1%8C?параметр=значение%20с%20пробелами"
// Символы ? и = сохраняются
console.log(encodeURIComponent(url));
// "https%3A%2F%2Fexample.com%2F%D0%BF%D1%83%D1%82%D1%8C%3F%D0%BF%D0%B0%D1%80%D0%B0%D0%BC%D0%B5%D1%82%D1%80%3D%D0%B7%D0%BD%D0%B0%D1%87%D0%B5%D0%BD%D0%B8%D0%B5%20%D1%81%20%D0%BF%D1%80%D0%BE%D0%B1%D0%B5%D0%BB%D0%B0%D0%BC%D0%B8"
// Все специальные символы кодируются
Устаревшие функции
escape() и unescape()
Эти функции считаются устаревшими и не должны использоваться в современном коде:
// Устаревший способ
const oldEncoded = escape("Привет мир!");
console.log(oldEncoded); // "%u041F%u0440%u0438%u0432%u0435%u0442%20%u043C%u0438%u0440%21"
const oldDecoded = unescape(oldEncoded);
console.log(oldDecoded); // "Привет мир!"
// Современная замена
const newEncoded = encodeURIComponent("Привет мир!");
console.log(newEncoded); // "%D0%9F%D1%80%D0%B8%D0%B2%D0%B5%D1%82%20%D0%BC%D0%B8%D1%80%21"
const newDecoded = decodeURIComponent(newEncoded);
console.log(newDecoded); // "Привет мир!"
Фундаментальные объекты
Boolean
Объект Boolean представляет логическое значение:
// Создание объектов Boolean
const bool1 = new Boolean(true);
const bool2 = new Boolean(false);
const bool3 = new Boolean("текст"); // любое непустое значение → true
console.log(bool1); // Boolean { true }
console.log(bool2); // Boolean { false }
console.log(bool3); // Boolean { true }
// Приведение к примитиву
console.log(bool1.valueOf()); // true
console.log(bool2.valueOf()); // false
// Автоматическое приведение в условиях
if (bool1) {
console.log("Истина"); // Выполнится
}
if (bool2) {
console.log("Истина"); // Не выполнится
}
Разница между примитивом и объектом:
const primitive = true;
const object = new Boolean(true);
console.log(typeof primitive); // "boolean"
console.log(typeof object); // "object"
console.log(primitive === true); // true
console.log(object === true); // false — объект не равен примитиву
console.log(object == true); // true — приведение типов
// Объект Boolean всегда истинен в логическом контексте
if (new Boolean(false)) {
console.log("Это выполнится"); // Выполнится
}
Symbol
Символы — это уникальные идентификаторы:
// Создание символов
const sym1 = Symbol();
const sym2 = Symbol("описание");
const sym3 = Symbol("описание");
console.log(sym1); // Symbol()
console.log(sym2); // Symbol(описание)
console.log(sym3); // Symbol(описание)
// Символы всегда уникальны
console.log(sym2 === sym3); // false
console.log(Symbol() === Symbol()); // false
Глобальный реестр символов:
const sym1 = Symbol.for("ключ");
const sym2 = Symbol.for("ключ");
console.log(sym1 === sym2); // true — один и тот же символ из реестра
console.log(Symbol.keyFor(sym1)); // "ключ"
Использование символов как ключей свойств:
const id = Symbol("id");
const name = Symbol("name");
const user = {
[id]: 12345,
[name]: "Алексей",
age: 30
};
console.log(user[id]); // 12345
console.log(user[name]); // "Алексей"
console.log(user.age); // 30
// Символьные свойства не видны в обычных циклах
for (let key in user) {
console.log(key); // только "age"
}
console.log(Object.keys(user)); // ["age"]
console.log(Object.getOwnPropertyNames(user)); // ["age"]
// Получение символьных ключей
console.log(Object.getOwnPropertySymbols(user)); // [Symbol(id), Symbol(name)]
// Получение всех ключей
const allKeys = [...Object.getOwnPropertyNames(user), ...Object.getOwnPropertySymbols(user)];
console.log(allKeys); // ["age", Symbol(id), Symbol(name)]
Символы как константы:
const EventType = {
CLICK: Symbol("click"),
HOVER: Symbol("hover"),
DRAG: Symbol("drag")
};
function handleEvent(eventType) {
switch (eventType) {
case EventType.CLICK:
console.log("Обработка клика");
break;
case EventType.HOVER:
console.log("Обработка наведения");
break;
case EventType.DRAG:
console.log("Обработка перетаскивания");
break;
}
}
handleEvent(EventType.CLICK); // "Обработка клика"
Error и его подтипы
Объект Error представляет ошибку во время выполнения:
// Создание ошибки
const error = new Error("Произошла ошибка");
console.log(error.message); // "Произошла ошибка"
console.log(error.name); // "Error"
console.log(error.stack); // Стек вызовов
// Генерация ошибки
throw new Error("Что-то пошло не так");
// Обработка ошибки
try {
throw new Error("Ошибка в блоке try");
} catch (error) {
console.log(error.message); // "Ошибка в блоке try"
console.log(error.name); // "Error"
}
EvalError
Ошибка при работе с функцией eval():
try {
throw new EvalError("Ошибка при вычислении");
} catch (error) {
console.log(error.name); // "EvalError"
console.log(error.message); // "Ошибка при вычислении"
}
RangeError
Ошибка при выходе за пределы допустимого диапазона:
// Примеры возникновения RangeError
try {
const arr = new Array(-1); // Отрицательная длина
} catch (error) {
console.log(error.name); // "RangeError"
}
try {
(123.456).toFixed(1000); // Слишком много знаков
} catch (error) {
console.log(error.name); // "RangeError"
}
// Создание своей ошибки диапазона
function setAge(age) {
if (age < 0 || age > 150) {
throw new RangeError("Возраст должен быть от 0 до 150");
}
return age;
}
try {
setAge(200);
} catch (error) {
console.log(error.message); // "Возраст должен быть от 0 до 150"
}
ReferenceError
Ошибка при обращении к несуществующей переменной:
try {
console.log(nonExistentVariable);
} catch (error) {
console.log(error.name); // "ReferenceError"
}
// Создание своей ошибки ссылки
function accessProperty(obj, prop) {
if (!(prop in obj)) {
throw new ReferenceError(`Свойство "${prop}" не существует`);
}
return obj[prop];
}
const user = { name: "Иван" };
try {
accessProperty(user, "age");
} catch (error) {
console.log(error.message); // "Свойство "age" не существует"
}
SyntaxError
Ошибка синтаксиса в коде:
try {
eval("function test( { return 1; }"); // Неправильный синтаксис
} catch (error) {
console.log(error.name); // "SyntaxError"
}
// Создание своей ошибки синтаксиса
function validateJSON(jsonString) {
try {
JSON.parse(jsonString);
} catch (error) {
if (error instanceof SyntaxError) {
throw new SyntaxError("Некорректный JSON: " + error.message);
}
throw error;
}
}
try {
validateJSON("{ name: 'Иван' }"); // Неправильные кавычки
} catch (error) {
console.log(error.message); // "Некорректный JSON: ..."
}
TypeError
Ошибка при применении операции к неподходящему типу:
try {
null.someMethod(); // null не имеет методов
} catch (error) {
console.log(error.name); // "TypeError"
}
try {
const num = 42;
num(); // Число не является функцией
} catch (error) {
console.log(error.name); // "TypeError"
}
// Создание своей ошибки типа
function divide(a, b) {
if (typeof a !== "number" || typeof b !== "number") {
throw new TypeError("Оба аргумента должны быть числами");
}
if (b === 0) {
throw new TypeError("Деление на ноль запрещено");
}
return a / b;
}
try {
divide("10", 2);
} catch (error) {
console.log(error.message); // "Оба аргумента должны быть числами"
}
URIError
Ошибка при работе с функциями кодирования URI:
try {
decodeURIComponent("%"); // Некорректная последовательность
} catch (error) {
console.log(error.name); // "URIError"
}
// Создание своей ошибки URI
function safeDecode(uriComponent) {
try {
return decodeURIComponent(uriComponent);
} catch (error) {
if (error instanceof URIError) {
throw new URIError(`Некорректный URI-компонент: ${uriComponent}`);
}
throw error;
}
}
try {
safeDecode("%E0%A4%A");
} catch (error) {
console.log(error.message); // "Некорректный URI-компонент: %E0%A4%A"
}
AggregateError
Представляет несколько ошибок одновременно:
// Используется с Promise.any()
const promises = [
Promise.reject(new Error("Ошибка 1")),
Promise.reject(new Error("Ошибка 2")),
Promise.reject(new Error("Ошибка 3"))
];
Promise.any(promises)
.then(result => console.log(result))
.catch(error => {
console.log(error.name); // "AggregateError"
console.log(error.errors.length); // 3
error.errors.forEach((err, index) => {
console.log(`Ошибка ${index + 1}: ${err.message}`);
});
});
// Создание своего AggregateError
const errors = [
new TypeError("Тип 1"),
new RangeError("Диапазон 1"),
new Error("Обычная ошибка")
];
const aggregate = new AggregateError(errors, "Несколько ошибок произошло");
console.log(aggregate.message); // "Несколько ошибок произошло"
console.log(aggregate.errors.length); // 3
SuppressedError
Представляет ошибку, подавленную другой ошибкой (новый функционал):
// Используется при обработке ошибок с подавлением
try {
try {
throw new Error("Первичная ошибка");
} catch (primaryError) {
try {
// Код очистки, который тоже может выбросить ошибку
throw new Error("Ошибка очистки");
} catch (cleanupError) {
// Подавляем ошибку очистки
throw new SuppressedError(cleanupError, primaryError, "Ошибка при очистке");
}
}
} catch (error) {
console.log(error.name); // "SuppressedError"
console.log(error.error); // Ошибка очистки
console.log(error.suppressed); // Первичная ошибка
}
InternalError (не стандартный)
Нестандартный объект ошибки, используемый в некоторых средах:
// Этот объект не является частью стандарта ECMAScript
// Может встречаться в старых версиях некоторых браузеров
try {
// Очень глубокая рекурсия
function recurse() {
recurse();
}
recurse();
} catch (error) {
console.log(error.name); // Может быть "InternalError" или "RangeError"
}
Практические примеры
Валидация данных с использованием глобальных функций
class DataValidator {
static isValidNumber(value) {
return !isNaN(parseFloat(value)) && isFinite(value);
}
static parseInteger(value, defaultValue = 0) {
const parsed = parseInt(value, 10);
return this.isValidNumber(parsed) ? parsed : defaultValue;
}
static parseFloat(value, defaultValue = 0.0) {
const parsed = parseFloat(value);
return this.isValidNumber(parsed) ? parsed : defaultValue;
}
static isValidURL(url) {
try {
new URL(url);
return true;
} catch {
return false;
}
}
static encodeURLParams(params) {
return Object.entries(params)
.map(([key, value]) =>
`${encodeURIComponent(key)}=${encodeURIComponent(value)}`
)
.join("&");
}
static decodeURLParams(queryString) {
const params = {};
queryString.split("&").forEach(param => {
const [key, value] = param.split("=");
params[decodeURIComponent(key)] = decodeURIComponent(value || "");
});
return params;
}
}
// Использование
console.log(DataValidator.isValidNumber("123")); // true
console.log(DataValidator.isValidNumber("текст")); // false
console.log(DataValidator.parseInteger("42")); // 42
console.log(DataValidator.parseInteger("текст")); // 0
const urlParams = { name: "Иван Петров", city: "Москва" };
const encoded = DataValidator.encodeURLParams(urlParams);
console.log(encoded); // "name=%D0%98%D0%B2%D0%B0%D0%BD%20%D0%9F%D0%B5%D1%82%D1%80%D0%BE%D0%B2&city=%D0%9C%D0%BE%D1%81%D0%BA%D0%B2%D0%B0"
const decoded = DataValidator.decodeURLParams(encoded);
console.log(decoded); // { name: "Иван Петров", city: "Москва" }
Создание кастомных ошибок
class ValidationError extends Error {
constructor(message, field) {
super(message);
this.name = "ValidationError";
this.field = field;
this.timestamp = new Date().toISOString();
}
}
class DatabaseError extends Error {
constructor(message, query, code) {
super(message);
this.name = "DatabaseError";
this.query = query;
this.code = code;
this.timestamp = new Date().toISOString();
}
}
class AuthenticationError extends Error {
constructor(message, userId = null) {
super(message);
this.name = "AuthenticationError";
this.userId = userId;
this.timestamp = new Date().toISOString();
}
}
// Использование
function validateUser(user) {
if (!user.name || user.name.length < 2) {
throw new ValidationError("Имя должно содержать минимум 2 символа", "name");
}
if (!user.email || !user.email.includes("@")) {
throw new ValidationError("Некорректный email", "email");
}
if (user.age < 0 || user.age > 150) {
throw new ValidationError("Некорректный возраст", "age");
}
return true;
}
try {
validateUser({ name: "А", email: "invalid", age: 200 });
} catch (error) {
if (error instanceof ValidationError) {
console.log(`${error.name}: ${error.message}`);
console.log(`Поле: ${error.field}`);
console.log(`Время: ${error.timestamp}`);
}
}
Особенности базовых объектов
★ Массив – основные методы:
| Метод | Описание | Пример |
|---|---|---|
push() / pop() | Добавить/удалить элемент в конец | arr.push(4) → [1,2,3,4] |
shift() / unshift() | Удалить/добавить в начало | arr.unshift(0) → [0,1,2,3] |
slice() | Копирует часть массива | arr.slice(1,3) → [2,3] |
splice() | Удаляет/заменяет элементы | arr.splice(1,1) → [1,3] |
map() | Преобразует массив | [1,2].map(x => x*2) → [2,4] |
filter() | Фильтрует элементы | [1,2,3].filter(x => x>1) → [2,3] |
reduce() | Сворачивает массив в одно значение | [1,2].reduce((a,b) => a+b) → 3 |
find() | Находит первый подходящий элемент | [1,2,3].find(x => x>1) → 2 |
sort() | Сортирует массив | [3,1].sort() → [1,3] |
forEach(fn) | Выполняет функцию для каждого элемента (ничего не возвращает) | arr.forEach(el => console.log(el)) |
includes(value) | Проверяет, есть ли элемент в массиве | [1,2].includes(2) → true |
indexOf(value) | Возвращает индекс элемента или -1, если не найден | [1,2,3].indexOf(2) → 1 |
some(fn) | Проверяет, существует ли хотя бы один подходящий элемент | [1,2].some(x => x > 1) → true |
every(fn) | Проверяет, все ли элементы соответствуют условию | [2,3].every(x => x > 1) → true |
join(separator) | Преобразует массив в строку с указанным разделителем | [1,2,3].join('-') → "1-2-3" |
reverse() | Разворачивает массив (мутирует оригинал) | [1,2,3].reverse() → [3,2,1] |
flat(n) | Расплющивает вложенные массивы на n уровней | [1, [2, [3]]].flat(2) → [1,2,3] |
isArray()(статический) | Проверяет, является ли значение массивом | Array.isArray([1]) → true |
★ Числа – основные методы и свойства:
| Метод/ Свойство | Описание | Пример |
|---|---|---|
Number.parseInt() | Преобразует строку в целое число | parseInt("42") → 42 |
Number.parseFloat() | Преобразует в дробное число | parseFloat("3.14") → 3.14 |
toFixed(n) | Округляет до n знаков после запятой | 3.1415.toFixed(2) → "3.14" |
toString() | Преобразует число в строку | 42.toString() → "42" |
Math.random() | Случайное число от 0 до 1 | Math.random() → 0.123 |
Math.round() | Округление до ближайшего целого | Math.round(3.6) → 4 |
Math.floor(x) | Округляет вниз до целого | Math.floor(3.9) → 3 |
Math.ceil(x) | Округляет вверх до целого | Math.ceil(3.1) → 4 |
Math.trunc(x) | Убирает дробную часть, не округляя | Math.trunc(3.9) → 3 |
Number.isNaN(value) | Проверяет, является ли значение NaN | Number.isNaN(NaN) → true |
Number.isFinite(value) | Проверяет, является ли значение конечным числом | Number.isFinite(Infinity) → false |
Number.isInteger(value) | Проверяет, является ли число целым | Number.isInteger(42) → true |
Math.pow(x, y) | Возводит x в степень y | Math.pow(2, 3) → 8 |
Math.sqrt(x) | Квадратный корень из числа | Math.sqrt(16) → 4 |
Math.min(...values) | Находит минимальное число | Math.min(1, 2, 3) → 1 |
Math.max(...values) | Находит максимальное число | Math.max(1, 2, 3) → 3 |
Number.MIN_VALUE | Минимально возможное положительное число | Number.MIN_VALUE → 5e-324 |
Number.MAX_VALUE | Максимально возможное число | Number.MAX_VALUE → 1.7976...e+308 |
★ Строки – основные методы:
| Метод | Описание | Пример |
|---|---|---|
length | Длина строки | "hello".length → 5 |
toUpperCase() | Преобразует в верхний регистр | "Hi".toUpperCase() → "HI" |
toLowerCase() | Преобразует в нижний регистр | "Hi".toLowerCase() → "hi" |
includes() | Проверяет наличие подстроки | "hello".includes("ell") → true |
split() | Разделяет строку в массив | "a,b,c".split(",") → ["a","b","c"] |
trim() | Удаляет пробелы с обоих концов | " hi ".trim() → "hi" |
replace() | Заменяет подстроку | "hi".replace("i", "ello") → "hello" |
indexOf(substring) | Возвращает индекс первого вхождения подстроки, или -1, если не найдено | "hello".indexOf("e") → 1 |
lastIndexOf(substring) | Возвращает индекс последнего вхождения подстроки | "abacaba".lastIndexOf("a") → 6 |
slice(start, end?) | Возвращает часть строки между начальным и конечным индексами (не включая конец) | "hello".slice(1,4) → "ell" |
substring(start, end?) | То же, что и slice, но не поддерживает отрицательные значения | "hello".substring(1,4) → "ell" |
charAt(index) | Возвращает символ по указанному индексу | "hello".charAt(0) → "h" |
startsWith(prefix) | Проверяет, начинается ли строка с указанной подстроки | "hello".startsWith("he") → true |
endsWith(suffix) | Проверяет, заканчивается ли строка указанной подстрокой | "hello".endsWith("lo") → true |
repeat(count) | Повторяет строку заданное число раз | "ha".repeat(3) → "hahaha" |
★ Глобальные функции – функции, которые доступны в глобальной области видимости:
| Функция | Описание | Пример |
|---|---|---|
isNaN() | Проверяет, является ли значение NaN | isNaN("text") → true |
parseInt() | Аналог Number.parseInt() | parseInt("42px") → 42 |
parseFloat() | Аналог Number.parseFloat() | parseFloat("3.14.15") → 3.14 |
eval() | Выполняет строку как код (опасно!) | eval("2+2") → 4 |
encodeURI() | Кодирует URL | encodeURI("https://сайт.рф") → "https://%D1%81%D0%B0%D0%B9%D1%82.%D1%80%D1%84" |
★ Работа с датами (Date)
Date – объект, который используется для работы с временем.
const now = new Date(); // Текущая дата
console.log(now.getFullYear()); // Год (2023)
console.log(now.getMonth()); // Месяц (0-11)
console.log(now.getDate()); // День месяца (1-31)
console.log(now.getHours()); // Часы (0-23)
console.log(now.toLocaleString()); // "12.01.2023, 14:30:00"
DOM
★ DOM (Document Object Model)
DOM – программное представление HTML-документа в виде дерева объектов. Объект Document представляет собой весь HTML-документ и является корневым узлом дерева DOM. Он предоставляет методы и свойства для взаимодействия с содержимым страницы: поиска элементов, создания новых узлов, изменения содержимого, управления стилями и многим другим.
Основные сущности DOM:
| Сущность | Описание |
|---|---|
Document | Корень DOM (весь документ) |
Element | HTML-элемент |
Attr | Атрибут элемента |
Text | Текстовый узел |
Comment | Комментарий |
DocumentFragment | Легковесный «контейнер» для DOM |
Node | Базовый класс для всех узлов |
NodeList | Коллекция узлов |
NamedNodeMap | Коллекция атрибутов элемента |
★ Свойства Document:
| Свойство | Описание | Пример |
|---|---|---|
document.documentElement Ссылка на корневой элемент <html> | document.documentElement.tagName → "HTML" | |
document.head | Ссылка на элемент <head> | document.head.appendChild(myScript) |
document.body | Ссылка на элемент <body> | document.body.innerHTML = "<h1>Привет</h1>" |
document.title | Получает или устанавливает заголовок страницы (<title>) | document.title = "Новая страница" |
document.URL | Возвращает полный URL текущего документа | console.log(document.URL) |
document.location | Объект Location, содержащий информацию о текущем адресе | document.location.href |
document.links | Коллекция всех гиперссылок (<a>) на странице | document.links[0].href |
document.images | Коллекция всех изображений (<img>) на странице | document.images.length |
document.forms | Коллекция всех форм на странице | document.forms.loginForm |
Методы поиска элементов:
| Метод | Пример | Возвращает |
|---|---|---|
getElementById() | document.getElementById('app') | Один Element |
getElementsByClassName() | document.getElementsByClassName('item') | HTMLCollection |
getElementsByTagName() | document.getElementsByTagName('div') | HTMLCollection |
querySelector() | document.querySelector('.btn') | Первый подходящий Element |
querySelectorAll() | document.querySelectorAll('p') | NodeList |
Методы создания элементов:
- createElement() – создаёт элемент;
- createTextNode() –создаёт текстовый узел;
- createComment() – создаёт комментарий.
★ Свойства Element:
| Свойство | Значение |
|---|---|
element.id | значение атрибута id |
element.className | строка классов (class="...") |
element.classList | объект для работы с классами (методы: add(), remove(), toggle()) |
element.innerHTML | HTML-содержимое |
element.textContent | текст (без HTML-тегов) |
element.style | доступ к CSS-стилям |
★ Методы:
| Метод | Пример | Действие |
|---|---|---|
| getAttribute() | div.getAttribute('data-id') | Получить атрибут |
| setAttribute() | div.setAttribute('data-test', '123') | Установить атрибут |
| removeAttribute() | div.removeAttribute('hidden') | Удалить атрибут |
| append() / prepend() | div.append(newElement) | Добавить элемент |
| remove() | div.remove() | Удалить элемент |
| closest() | div.closest('.parent') | Найти ближайший родительский элемент |
Прочие объекты
Браузерные объекты
★ Браузерные объекты
Браузерные объекты – объекты, предоставляемые для работы с окружением:
| Объект | Описание |
|---|---|
| Window | Глобальный объект (вкладка браузера) |
| Navigator | Информация о браузере |
| Screen | Данные об экране |
| History | Управление историей |
| Location | URL страницы |
Свойства Window:
| Свойства | Описание | Пример |
|---|---|---|
| window.innerWidth | Ширина области просмотра (px) | window.innerWidth → 1200 |
| window.innerHeight | Высота области просмотра (px) | window.innerHeight → 800 |
| window.outerWidth | Ширина всего окна браузера (px) | window.outerWidth → 1400 |
| window.outerHeight | Высота всего окна браузера (px) | window.outerHeight → 900 |
| window.location | Объект Location (URL страницы) | window.location.href |
| window.document | Объект Document (DOM) | window.document.title |
| window.localStorage | Локальное хранилище данных | window.localStorage.setItem('key', 'value') |
Методы:
| Метод | Описание | Пример |
|---|---|---|
| window.alert() | Показывает alert-окно | window.alert("Привет!") |
| window.open() | Открывает новое окно/вкладку | window.open("https://google.com") |
| window.scrollTo() | Прокручивает страницу | window.scrollTo(0, 100) |
| window.setTimeout() | Выполняет код с задержкой | setTimeout(() => {}, 1000) |
| window.fetch() | Отправляет HTTP-запрос | fetch("https://api.example.com") |
★ Графика: Canvas
Объект CanvasRenderingContext2D позволяет рисовать на <canvas>:
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
ctx.fillStyle = 'red';
ctx.fillRect(10, 10, 100, 100); // Рисуем красный квадрат
```JavaScript
Работа с файлами и системой
В браузере и Node.js есть объекты для работы с файлами, сетью и процессами.
В браузере:
File API: Чтение файлов через `<input type= "file">`:
```JavaScript
const fileInput = document.querySelector('input[type="file"]');
fileInput.addEventListener('change', (e) => {
const file = e.target.files[0];
console.log(file.name); // Имя файла
});
Основные методы/свойства File API:
- files – список выбранных файлов;
- FileReader – чтение содержимого файла;
- Blob – бинарные данные файла.
В Node.js:
- File System (fs): чтение/запись файлов – readFile, writeFile, promises;
- HTTP/NET: создание серверов – createServer, request;
- Buffer/Stream: работа с бинарными данными – Buffer.from(), stream.pipe();
- Process: процессы – argv, cwd, exit.
И да, метода JSON.statham() нет. Вроде бы.
Структурированные данные
ArrayBuffer и SharedArrayBuffer
ArrayBuffer представляет собой буфер данных фиксированной длины, используемый для хранения бинарных данных:
// Создание буфера размером 16 байт
const buffer = new ArrayBuffer(16);
console.log(buffer.byteLength); // 16
console.log(buffer); // ArrayBuffer { byteLength: 16 }
Для работы с данными внутри буфера используются типизированные массивы:
const buffer = new ArrayBuffer(16);
// Создание представлений на буфер
const uint8View = new Uint8Array(buffer);
const uint16View = new Uint16Array(buffer);
const float32View = new Float32Array(buffer);
// Запись данных через одно представление
uint8View[0] = 255;
uint8View[1] = 128;
uint8View[2] = 64;
uint8View[3] = 32;
// Чтение через другое представление
console.log(uint16View[0]); // 32895
console.log(float32View[0]); // 1.539989614439558e-36
SharedArrayBuffer позволяет разделять буфер памяти между несколькими потоками выполнения (например, с помощью веб-воркеров):
// Создание разделяемого буфера
const sharedBuffer = new SharedArrayBuffer(1024);
// Создание представления
const sharedArray = new Int32Array(sharedBuffer);
// Запись данных
sharedArray[0] = 42;
sharedArray[1] = 100;
DataView
DataView предоставляет низкоуровневый интерфейс для чтения и записи данных произвольного типа в ArrayBuffer:
const buffer = new ArrayBuffer(16);
const view = new DataView(buffer);
// Запись данных разных типов
view.setInt8(0, 127); // 1 байт
view.setInt16(1, 32767); // 2 байта
view.setInt32(3, 2147483647); // 4 байта
view.setFloat32(7, 3.14); // 4 байта
view.setFloat64(11, 2.71828); // 8 байт
// Чтение данных
console.log(view.getInt8(0)); // 127
console.log(view.getInt16(1)); // 32767
console.log(view.getInt32(3)); // 2147483647
console.log(view.getFloat32(7)); // 3.14
console.log(view.getFloat64(11)); // 2.71828
Указание порядка байтов (endianness):
const buffer = new ArrayBuffer(4);
const view = new DataView(buffer);
// Запись с указанием порядка байтов
view.setInt32(0, 0x12345678, true); // little-endian
console.log(view.getInt32(0, true)); // 305419896
view.setInt32(0, 0x12345678, false); // big-endian
console.log(view.getInt32(0, false)); // 305419896
Atomics
Atomics предоставляет атомарные операции для работы с разделяемой памятью, обеспечивая потокобезопасность:
const sharedBuffer = new SharedArrayBuffer(1024);
const sharedArray = new Int32Array(sharedBuffer);
// Атомарная запись
Atomics.store(sharedArray, 0, 100);
console.log(Atomics.load(sharedArray, 0)); // 100
// Атомарное сложение
Atomics.add(sharedArray, 0, 50);
console.log(Atomics.load(sharedArray, 0)); // 150
// Атомарное вычитание
Atomics.sub(sharedArray, 0, 30);
console.log(Atomics.load(sharedArray, 0)); // 120
// Атомарное сравнение и замена
Atomics.compareExchange(sharedArray, 0, 120, 200);
console.log(Atomics.load(sharedArray, 0)); // 200
// Атомарный инкремент
Atomics.add(sharedArray, 1, 1);
console.log(Atomics.load(sharedArray, 1)); // 1
Синхронизация потоков с помощью wait и notify:
// В основном потоке
Atomics.store(sharedArray, 0, 0);
// В воркере
Atomics.wait(sharedArray, 0, 0); // Ожидает, пока значение не изменится
console.log("Продолжение выполнения");
// В основном потоке после изменения
Atomics.store(sharedArray, 0, 1);
Atomics.notify(sharedArray, 0); // Пробуждает ожидающий воркер
JSON
Объект JSON предоставляет методы для преобразования данных в формат JSON и обратно:
// Преобразование объекта в JSON-строку
const user = {
name: "Алексей",
age: 30,
city: "Москва",
hobbies: ["чтение", "программирование"]
};
const jsonString = JSON.stringify(user);
console.log(jsonString);
// '{"name":"Алексей","age":30,"city":"Москва","hobbies":["чтение","программирование"]}'
// Преобразование строки в объект
const parsedUser = JSON.parse(jsonString);
console.log(parsedUser.name); // "Алексей"
console.log(parsedUser.hobbies[0]); // "чтение"
Форматирование при сериализации:
const data = {
title: "Продукт",
price: 1999,
inStock: true,
tags: ["новинка", "акция"]
};
// Без форматирования
console.log(JSON.stringify(data));
// {"title":"Продукт","price":1999,"inStock":true,"tags":["новинка","акция"]}
// С отступами (2 пробела)
console.log(JSON.stringify(data, null, 2));
/*
{
"title": "Продукт",
"price": 1999,
"inStock": true,
"tags": [
"новинка",
"акция"
]
}
*/
// С табуляцией
console.log(JSON.stringify(data, null, "\t"));
Использование функции-замены (replacer):
const product = {
name: "Ноутбук",
price: 50000,
costPrice: 30000, // Себестоимость (не показываем)
discount: 0.1,
inStock: true
};
// Исключение полей из сериализации
const jsonString = JSON.stringify(product, ["name", "price", "discount", "inStock"]);
console.log(jsonString);
// '{"name":"Ноутбук","price":50000,"discount":0.1,"inStock":true}'
// Преобразование значений
const formatted = JSON.stringify(product, (key, value) => {
if (key === "price") {
return value + " ₽";
}
if (key === "discount") {
return (value * 100) + "%";
}
return value;
});
console.log(formatted);
// '{"name":"Ноутбук","price":"50000 ₽","costPrice":30000,"discount":"10%","inStock":true}'
Управление памятью
WeakRef
WeakRef создаёт слабую ссылку на объект, позволяя сборщику мусора удалить объект, если на него нет других сильных ссылок:
class CacheItem {
constructor(value) {
this.value = value;
this.timestamp = Date.now();
}
}
const cache = new Map();
function addToCache(key, value) {
const item = new CacheItem(value);
cache.set(key, new WeakRef(item));
return item;
}
function getFromCache(key) {
const ref = cache.get(key);
if (ref) {
return ref.deref();
}
return undefined;
}
const obj = addToCache("user1", { name: "Иван" });
console.log(getFromCache("user1")); // CacheItem { value: { name: "Иван" }, timestamp: ... }
// После удаления сильной ссылки объект может быть собран
obj = null;
setTimeout(() => {
console.log(getFromCache("user1")); // undefined (возможно)
}, 100);
FinalizationRegistry
FinalizationRegistry регистрирует колбэк, который вызывается после удаления объекта сборщиком мусора:
const registry = new FinalizationRegistry(heldValue => {
console.log(`Объект освобождён: ${heldValue}`);
});
class Resource {
constructor(name) {
this.name = name;
this.buffer = new ArrayBuffer(1024 * 1024); // 1 МБ
registry.register(this, name, this);
}
cleanup() {
registry.unregister(this);
console.log(`Ресурс ${this.name} очищен вручную`);
}
}
// Создание ресурса
const resource1 = new Resource("файл1");
const resource2 = new Resource("файл2");
// Удаление ссылок
resource1 = null;
resource2.cleanup();
resource2 = null;
// Через некоторое время в консоли:
// "Объект освобождён: файл1"
Практическое применение для управления ресурсами:
class ImageLoader {
constructor() {
this.cache = new Map();
this.registry = new FinalizationRegistry(key => {
console.log(`Изображение ${key} удалено из кэша`);
this.cache.delete(key);
});
}
load(url) {
if (this.cache.has(url)) {
return this.cache.get(url);
}
const image = new Image();
image.src = url;
this.cache.set(url, image);
this.registry.register(image, url);
return image;
}
clear() {
this.cache.clear();
console.log("Кэш изображений очищен");
}
}
const loader = new ImageLoader();
const img1 = loader.load("image1.jpg");
const img2 = loader.load("image2.jpg");
// После удаления ссылок изображения будут удалены из кэша
Абстракции управления
Iterator и AsyncIterator
Итераторы предоставляют стандартный способ перебора элементов коллекции:
// Создание кастомного итератора
const range = {
from: 1,
to: 5,
[Symbol.iterator]() {
let current = this.from;
const last = this.to;
return {
next() {
if (current <= last) {
return { value: current++, done: false };
} else {
return { done: true };
}
}
};
}
};
// Использование итератора
for (let num of range) {
console.log(num); // 1, 2, 3, 4, 5
}
// Распространение в массив
const arr = [...range];
console.log(arr); // [1, 2, 3, 4, 5]
Асинхронные итераторы для работы с асинхронными данными:
const asyncRange = {
from: 1,
to: 3,
async *[Symbol.asyncIterator]() {
for (let i = this.from; i <= this.to; i++) {
await new Promise(resolve => setTimeout(resolve, 1000));
yield i;
}
}
};
// Использование асинхронного итератора
(async () => {
for await (let num of asyncRange) {
console.log(num); // 1 (через 1с), 2 (через 2с), 3 (через 3с)
}
})();
Promise
Promise представляет результат асинхронной операции:
// Создание промиса
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
const success = true;
if (success) {
resolve("Операция завершена успешно");
} else {
reject(new Error("Произошла ошибка"));
}
}, 1000);
});
// Обработка результата
promise
.then(result => console.log(result))
.catch(error => console.error(error.message));
// Цепочка промисов
fetch("https://api.example.com/data")
.then(response => response.json())
.then(data => processData(data))
.then(result => saveResult(result))
.catch(error => console.error("Ошибка:", error));
Статические методы Promise:
// Promise.all - ожидание всех промисов
Promise.all([
fetch("/api/users"),
fetch("/api/posts"),
fetch("/api/comments")
])
.then(responses => Promise.all(responses.map(r => r.json())))
.then(([users, posts, comments]) => {
console.log("Данные загружены:", { users, posts, comments });
});
// Promise.race - возвращает первый завершённый промис
Promise.race([
fetch("/api/data"),
new Promise((_, reject) =>
setTimeout(() => reject(new Error("Таймаут")), 5000)
)
])
.then(data => console.log("Данные получены"))
.catch(error => console.error("Ошибка:", error));
// Promise.allSettled - ожидает все промисы независимо от результата
Promise.allSettled([
Promise.resolve(1),
Promise.reject(new Error("Ошибка")),
Promise.resolve(3)
])
.then(results => {
results.forEach((result, index) => {
if (result.status === "fulfilled") {
console.log(`Промис ${index} выполнен:`, result.value);
} else {
console.log(`Промис ${index} отклонён:`, result.reason);
}
});
});
Generator и GeneratorFunction
Генераторы позволяют приостанавливать и возобновлять выполнение функции:
// Объявление генератора
function* numberGenerator() {
yield 1;
yield 2;
yield 3;
return 4;
}
const gen = numberGenerator();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: 3, done: false }
console.log(gen.next()); // { value: 4, done: true }
console.log(gen.next()); // { value: undefined, done: true }
Генератор с параметрами:
function* counter() {
let count = 0;
while (true) {
const increment = yield count;
count += increment || 1;
}
}
const gen = counter();
console.log(gen.next().value); // 0
console.log(gen.next().value); // 1
console.log(gen.next(5).value); // 6
console.log(gen.next(10).value); // 16
Делегирование генераторов:
function* gen1() {
yield 1;
yield 2;
}
function* gen2() {
yield 3;
yield* gen1(); // Делегирование
yield 4;
}
const gen = gen2();
console.log([...gen]); // [3, 1, 2, 4]
AsyncGenerator и AsyncGeneratorFunction
Асинхронные генераторы объединяют функциональность генераторов и промисов:
async function* asyncNumberGenerator() {
yield 1;
await new Promise(resolve => setTimeout(resolve, 1000));
yield 2;
await new Promise(resolve => setTimeout(resolve, 1000));
yield 3;
}
(async () => {
for await (let num of asyncNumberGenerator()) {
console.log(num); // 1, затем 2 (через 1с), затем 3 (через 2с)
}
})();
Практическое применение для потоковой загрузки данных:
async function* fetchPages(url, pages) {
for (let page = 1; page <= pages; page++) {
const response = await fetch(`${url}?page=${page}`);
const data = await response.json();
yield data;
}
}
(async () => {
for await (const pageData of fetchPages("/api/items", 3)) {
console.log("Загружена страница:", pageData);
}
})();
AsyncFunction
AsyncFunction — это конструктор для создания асинхронных функций динамически:
const AsyncFunction = Object.getPrototypeOf(async function(){}).constructor;
const asyncSum = new AsyncFunction('a', 'b', 'return a + b');
asyncSum(5, 3).then(result => console.log(result)); // 8
DisposableStack и AsyncDisposableStack
Эти объекты упрощают управление ресурсами, требующими очистки:
// DisposableStack для синхронных ресурсов
class FileHandle {
constructor(filename) {
this.filename = filename;
this.open = true;
console.log(`Файл ${filename} открыт`);
}
read() {
return `Содержимое файла ${this.filename}`;
}
[Symbol.dispose]() {
if (this.open) {
console.log(`Файл ${this.filename} закрыт`);
this.open = false;
}
}
}
function processFile() {
using file = new FileHandle("data.txt");
console.log(file.read());
// Файл автоматически закроется при выходе из блока
}
processFile();
Асинхронная версия:
class DatabaseConnection {
constructor(connectionString) {
this.connectionString = connectionString;
this.connected = true;
console.log(`Подключение к базе данных установлено`);
}
async query(sql) {
return `Результат запроса: ${sql}`;
}
async [Symbol.asyncDispose]() {
if (this.connected) {
console.log(`Подключение к базе данных закрыто`);
this.connected = false;
}
}
}
async function processData() {
await using db = new DatabaseConnection("connection-string");
const result = await db.query("SELECT * FROM users");
console.log(result);
// Подключение автоматически закроется при выходе из блока
}
await processData();
Рефлексия
Reflect
Reflect предоставляет методы для выполнения операций над объектами в функциональном стиле:
const obj = { name: "Тест", value: 42 };
// Получение свойства
console.log(Reflect.get(obj, "name")); // "Тест"
// Установка свойства
Reflect.set(obj, "value", 100);
console.log(obj.value); // 100
// Проверка наличия свойства
console.log(Reflect.has(obj, "name")); // true
console.log(Reflect.has(obj, "missing")); // false
// Удаление свойства
Reflect.deleteProperty(obj, "value");
console.log("value" in obj); // false
// Применение функции
function sum(a, b) {
return a + b;
}
console.log(Reflect.apply(sum, null, [5, 3])); // 8
// Создание экземпляра
class Person {
constructor(name) {
this.name = name;
}
}
const person = Reflect.construct(Person, ["Алексей"]);
console.log(person.name); // "Алексей"
Proxy
Proxy позволяет перехватывать и переопределять операции над объектами:
const target = {
message: "Привет",
count: 0
};
const handler = {
get(target, prop, receiver) {
console.log(`Чтение свойства: ${prop}`);
return Reflect.get(target, prop, receiver);
},
set(target, prop, value, receiver) {
console.log(`Запись свойства ${prop} = ${value}`);
return Reflect.set(target, prop, value, receiver);
},
has(target, prop) {
console.log(`Проверка наличия: ${prop}`);
return Reflect.has(target, prop);
},
deleteProperty(target, prop) {
console.log(`Удаление свойства: ${prop}`);
return Reflect.deleteProperty(target, prop);
}
};
const proxy = new Proxy(target, handler);
proxy.message; // "Чтение свойства: message"
proxy.count = 5; // "Запись свойства count = 5"
"name" in proxy; // "Проверка наличия: name"
delete proxy.message; // "Удаление свойства: message"
Валидация данных через прокси:
const validator = {
set(obj, prop, value) {
if (prop === "age") {
if (typeof value !== "number" || value < 0 || value > 150) {
throw new RangeError("Возраст должен быть от 0 до 150");
}
}
if (prop === "email") {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(value)) {
throw new TypeError("Некорректный формат email");
}
}
return Reflect.set(obj, prop, value);
}
};
const user = new Proxy({}, validator);
user.age = 25; // OK
user.email = "test@example.com"; // OK
// user.age = 200; // RangeError
// user.email = "invalid"; // TypeError
Создание неизменяемого объекта:
function makeImmutable(obj) {
return new Proxy(obj, {
set(target, prop, value) {
throw new Error(`Нельзя изменить свойство ${prop}`);
},
deleteProperty(target, prop) {
throw new Error(`Нельзя удалить свойство ${prop}`);
}
});
}
const config = makeImmutable({
apiUrl: "https://api.example.com",
timeout: 5000,
debug: false
});
// config.apiUrl = "new-url"; // Error
// delete config.timeout; // Error
Интернационализация
Intl
Объект Intl предоставляет интернационализированные функции форматирования:
// Текущая локаль
console.log(Intl.getCanonicalLocales("en-US")); // ["en-US"]
console.log(Intl.getCanonicalLocales("RU")); // ["ru"]
Intl.Collator
Collator обеспечивает сортировку строк с учётом локали:
const names = ["Иван", "Анна", "Петр", "Елена"];
// Сортировка без учёта локали
console.log(names.sort());
// ["Анна", "Елена", "Иван", "Петр"]
// Сортировка с учётом русской локали
const collator = new Intl.Collator("ru");
console.log(names.sort((a, b) => collator.compare(a, b)));
// ["Анна", "Елена", "Иван", "Петр"]
// Сортировка с игнорированием регистра
const caseInsensitive = new Intl.Collator("ru", { sensitivity: "base" });
console.log(caseInsensitive.compare("Тест", "тест")); // 0 (равны)
Intl.DateTimeFormat
Форматирование дат и времени:
const date = new Date(2024, 0, 15, 14, 30, 45);
// Различные форматы для русской локали
console.log(new Intl.DateTimeFormat("ru-RU").format(date));
// "15.01.2024"
console.log(new Intl.DateTimeFormat("ru-RU", { dateStyle: "full" }).format(date));
// "понедельник, 15 января 2024 г."
console.log(new Intl.DateTimeFormat("ru-RU", {
dateStyle: "long",
timeStyle: "short"
}).format(date));
// "15 января 2024 г., 14:30"
console.log(new Intl.DateTimeFormat("ru-RU", {
year: "numeric",
month: "long",
day: "numeric",
weekday: "long",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
timeZoneName: "short"
}).format(date));
// "понедельник, 15 января 2024 г., 14:30:45, MSK"
Форматирование для разных локалей:
const date = new Date(2024, 0, 15);
console.log(new Intl.DateTimeFormat("en-US").format(date)); // "1/15/2024"
console.log(new Intl.DateTimeFormat("de-DE").format(date)); // "15.1.2024"
console.log(new Intl.DateTimeFormat("ja-JP").format(date)); // "2024/1/15"
console.log(new Intl.DateTimeFormat("zh-CN").format(date)); // "2024/1/15"
Intl.NumberFormat
Форматирование чисел:
const number = 1234567.89;
// Форматирование для русской локали
console.log(new Intl.NumberFormat("ru-RU").format(number));
// "1 234 567,89"
// Валюта
console.log(new Intl.NumberFormat("ru-RU", {
style: "currency",
currency: "RUB"
}).format(number));
// "1 234 567,89 ₽"
console.log(new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD"
}).format(number));
// "$1,234,567.89"
// Проценты
console.log(new Intl.NumberFormat("ru-RU", {
style: "percent"
}).format(0.42));
// "42 %"
// Научная нотация
console.log(new Intl.NumberFormat("ru-RU", {
notation: "scientific"
}).format(number));
// "1,23456789E6"
// Компактный формат
console.log(new Intl.NumberFormat("ru-RU", {
notation: "compact"
}).format(1234567));
// "1,2 млн"
Intl.RelativeTimeFormat
Форматирование относительного времени:
const formatter = new Intl.RelativeTimeFormat("ru", { numeric: "auto" });
console.log(formatter.format(-1, "day")); // "вчера"
console.log(formatter.format(0, "day")); // "сегодня"
console.log(formatter.format(1, "day")); // "завтра"
console.log(formatter.format(-2, "day")); // "позавчера"
console.log(formatter.format(2, "day")); // "послезавтра"
console.log(formatter.format(-1, "hour")); // "час назад"
console.log(formatter.format(1, "hour")); // "через час"
console.log(formatter.format(3, "hour")); // "через 3 часа"
console.log(formatter.format(-1, "month")); // "в прошлом месяце"
console.log(formatter.format(1, "month")); // "в следующем месяце"
Intl.ListFormat
Форматирование списков:
const formatter = new Intl.ListFormat("ru", {
style: "long",
type: "conjunction"
});
console.log(formatter.format(["яблоки"])); // "яблоки"
console.log(formatter.format(["яблоки", "груши"])); // "яблоки и груши"
console.log(formatter.format(["яблоки", "груши", "апельсины"]));
// "яблоки, груши и апельсины"
// Другие типы
const disjunction = new Intl.ListFormat("ru", { type: "disjunction" });
console.log(disjunction.format(["да", "нет"])); // "да или нет"
const unit = new Intl.ListFormat("ru", { type: "unit" });
console.log(unit.format(["метр", "килограмм"])); // "метр, килограмм"
Intl.PluralRules
Определение формы множественного числа:
const rules = new Intl.PluralRules("ru");
console.log(rules.select(1)); // "one"
console.log(rules.select(2)); // "few"
console.log(rules.select(5)); // "many"
console.log(rules.select(11)); // "many"
console.log(rules.select(21)); // "one"
console.log(rules.select(22)); // "few"
// Практическое применение
function getMessage(count) {
const forms = {
one: "сообщение",
few: "сообщения",
many: "сообщений"
};
return `${count} ${forms[rules.select(count)]}`;
}
console.log(getMessage(1)); // "1 сообщение"
console.log(getMessage(2)); // "2 сообщения"
console.log(getMessage(5)); // "5 сообщений"
console.log(getMessage(21)); // "21 сообщение"
Intl.DisplayNames
Отображение названий локалей, регионов и языков:
const displayNames = new Intl.DisplayNames("ru", { type: "language" });
console.log(displayNames.of("en")); // "английский"
console.log(displayNames.of("de")); // "немецкий"
console.log(displayNames.of("fr")); // "французский"
console.log(displayNames.of("zh")); // "китайский"
// Названия регионов
const regionNames = new Intl.DisplayNames("ru", { type: "region" });
console.log(regionNames.of("US")); // "США"
console.log(regionNames.of("DE")); // "Германия"
console.log(regionNames.of("CN")); // "Китай"
console.log(regionNames.of("RU")); // "Россия"
// Названия скриптов
const scriptNames = new Intl.DisplayNames("ru", { type: "script" });
console.log(scriptNames.of("Latn")); // "латиница"
console.log(scriptNames.of("Cyrl")); // "кириллица"
Intl.Segmenter
Сегментация текста на графемы, слова или предложения:
// Сегментация на слова
const wordSegmenter = new Intl.Segmenter("ru", { granularity: "word" });
const text = "Привет, мир! Как дела?";
const segments = wordSegmenter.segment(text);
for (const segment of segments) {
console.log(segment.segment);
}
// "Привет"
// "мир"
// "Как"
// "дела"
// Сегментация на предложения
const sentenceSegmenter = new Intl.Segmenter("ru", { granularity: "sentence" });
const sentences = sentenceSegmenter.segment("Первое предложение. Второе предложение! Третье?");
for (const segment of sentences) {
console.log(segment.segment);
}
// "Первое предложение."
// "Второе предложение!"
// "Третье?"
Intl.Locale
Работа с локалями:
const locale = new Intl.Locale("ru-RU");
console.log(locale.language); // "ru"
console.log(locale.script); // undefined
console.log(locale.region); // "RU"
console.log(locale.baseName); // "ru-RU"
// Методы
console.log(locale.maximize()); // "ru-Cyrl-RU" (добавлен скрипт)
console.log(locale.minimize()); // "ru" (убран регион)
// Создание с опциями
const localeWithOptions = new Intl.Locale("ru", {
region: "RU",
calendar: "gregory",
hourCycle: "h23"
});
console.log(localeWithOptions.toString());
// "ru-RU-u-ca-gregory-hc-h23"
Практические примеры
Создание кэша с автоматической очисткой
class SmartCache {
constructor(maxSize = 100) {
this.cache = new Map();
this.maxSize = maxSize;
this.registry = new FinalizationRegistry(key => {
console.log(`Ключ ${key} удалён из кэша`);
});
}
set(key, value) {
if (this.cache.size >= this.maxSize) {
const firstKey = this.cache.keys().next().value;
this.cache.delete(firstKey);
}
this.cache.set(key, new WeakRef(value));
this.registry.register(value, key);
}
get(key) {
const ref = this.cache.get(key);
return ref ? ref.deref() : undefined;
}
has(key) {
return this.cache.has(key);
}
delete(key) {
return this.cache.delete(key);
}
clear() {
this.cache.clear();
console.log("Кэш очищен");
}
size() {
return this.cache.size;
}
}
const cache = new SmartCache(3);
cache.set("user1", { name: "Иван" });
cache.set("user2", { name: "Мария" });
cache.set("user3", { name: "Алексей" });
console.log(cache.get("user1")); // { name: "Иван" }
console.log(cache.size()); // 3
Интернационализированный форматтер дат
class DateFormatter {
constructor(locale = "ru-RU", options = {}) {
this.locale = locale;
this.options = options;
this.formatter = new Intl.DateTimeFormat(locale, options);
}
format(date) {
return this.formatter.format(date);
}
formatRange(startDate, endDate) {
return `${this.format(startDate)} - ${this.format(endDate)}`;
}
formatRelative(date) {
const diff = date - Date.now();
const seconds = Math.floor(diff / 1000);
const minutes = Math.floor(seconds / 60);
const hours = Math.floor(minutes / 60);
const days = Math.floor(hours / 24);
const formatter = new Intl.RelativeTimeFormat(this.locale, {
numeric: "auto"
});
if (Math.abs(days) >= 1) {
return formatter.format(days, "day");
} else if (Math.abs(hours) >= 1) {
return formatter.format(hours, "hour");
} else if (Math.abs(minutes) >= 1) {
return formatter.format(minutes, "minute");
} else {
return formatter.format(seconds, "second");
}
}
}
const formatter = new DateFormatter("ru-RU", {
dateStyle: "long",
timeStyle: "short"
});
const now = new Date();
const tomorrow = new Date(now);
tomorrow.setDate(tomorrow.getDate() + 1);
console.log(formatter.format(now)); // "15 января 2024 г., 14:30"
console.log(formatter.formatRange(now, tomorrow)); // "15 января 2024 г., 14:30 - 16 января 2024 г., 14:30"
console.log(formatter.formatRelative(tomorrow)); // "завтра"