🔥 Как работает this в JavaScript? – примеры с call, apply, bind
В JavaScript
🔍 this в разных контекстах
📌 В методе объекта –
📌 В обычной функции (
Без строгого режима (
В браузере
В Node.js
• В строгом режиме (
📌 В
🚀 Важно:
2️⃣ Как изменить
🔥
Синтаксис:
✅
✅ Можно передавать аргументы через запятую.
🔥
Синтаксис:
✅ Удобно, если аргументы уже находятся в массиве.
❌
🔥
Синтаксис:
📌 Пример использования
Если вызвать
🚀 Итог
✅ Используй
✅ Используй
✅ Используй
В JavaScript
this
– это ключевое слово, которое зависит от контекста вызова функции. Давай разберёмся, как оно работает, и как его можно контролировать с помощью call
, apply
и bind
.🔍 this в разных контекстах
📌 В методе объекта –
this
указывает на сам объект:const user = {
name: "Alice",
sayHi() {
console.log(this.name);
}
};
user.sayHi(); // "Alice"
📌 В обычной функции (
function
) значение this
зависит от режима:Без строгого режима (
"use strict"
):В браузере
this
указывает на window
.В Node.js
this
будет {}
в модуле, а в функции – undefined
.function showThis() {
console.log(this);
}
showThis();
// В браузере без "use strict" → window
// В Node.js → undefined
• В строгом режиме (
"use strict"
) this
всегда undefined
в обычных функциях:"use strict";
function showThis() {
console.log(this);
}
showThis();
// undefined
📌 В
arrow function
this
не создаётся, а берётся из внешнего контекста:const obj = {
name: "Alice",
arrowFunc: () => console.log(this)
};
obj.arrowFunc();
// this возьмётся из глобального контекста (в браузере – window, в Node.js – {})
🚀 Важно:
this
в arrow function
определяется в момент создания и не меняется!2️⃣ Как изменить
this
? Используем call
, apply
, bind
🔥
call
– вызов функции с заданным this
Синтаксис:
func.call(context, arg1, arg2, ...)
function sayHello() {
console.log(`Привет, ${this.name}`);
}
const user = { name: "Alice" };
sayHello.call(user); // "Привет, Alice"
✅
call
сразу вызывает функцию.✅ Можно передавать аргументы через запятую.
🔥
apply
– как call
, но аргументы передаются массивомСинтаксис:
func.apply(context, [arg1, arg2, ...])
function sum(a, b) {
console.log(this.value + a + b);
}
const obj = { value: 10 };
sum.apply(obj, [2, 3]); // 10 + 2 + 3 = 15
✅ Удобно, если аргументы уже находятся в массиве.
❌
apply
редко используется, так как сейчас есть оператор spread
(...
):sum.call(obj, ...[2, 3]); // Тоже самое, что apply
🔥
bind
– создаёт новую функцию с привязанным this
Синтаксис:
const newFunc = func.bind(context, arg1, arg2, ...)
📌 Пример использования
bind
в обработчике событий:const button = {
text: "Нажми меня",
handleClick() {
console.log(this.text);
}
};
document.querySelector("button").addEventListener("click", button.handleClick.bind(button));
Если вызвать
handleClick
без bind
, this
станет undefined
, так как обработчик событий меняет контекст вызова.🚀 Итог
✅ Используй
call
, если нужно вызвать функцию сразу и передать this
.✅ Используй
apply
, если аргументы уже находятся в массиве.✅ Используй
bind
, если нужно привязать this
и вызвать функцию позже.🔥 Зачем нужны
В JavaScript есть 7 примитивных типов данных:
Но зачем нам
1️⃣ Что такое
🔹 Symbol("id") – это новый уникальный символ.
🔹 Даже если создать два одинаковых
Это ключевая особенность
2️⃣
В отличие от строк,
💡 Используется, когда нужно хранить "скрытые" свойства, которые не мешают основному API объекта.
3️⃣ Глобальные
Если нам нужно глобально переиспользовать один и тот же Symbol, используем
💡 В отличие от обычных
🔎 Чтобы получить имя
4️⃣
JavaScript использует
🔹
🔹
📌 Когда использовать
✅ Для уникальных ключей в объектах, которые не конфликтуют с другими свойствами.
✅ Для скрытых свойств, которые не должны быть доступны через
✅ Для работы с системными
Symbol
в JavaScript?В JavaScript есть 7 примитивных типов данных:
string
, number
, boolean
, null
, undefined
, bigint
, и… Symbol
.Но зачем нам
Symbol
, если есть строки? 🤔 Давай разбираться!1️⃣ Что такое
Symbol
?Symbol
– это уникальное и неизменяемое значение, которое можно использовать как ключ для свойств объекта.const id = Symbol("id");
console.log(id); // Symbol(id)
🔹 Symbol("id") – это новый уникальный символ.
🔹 Даже если создать два одинаковых
Symbol
, они не равны:const sym1 = Symbol("test");
const sym2 = Symbol("test");
console.log(sym1 === sym2); // ❌ false
Это ключевая особенность
Symbol
– каждый новый Symbol()
уникален! 🚀2️⃣
Symbol
как ключ в объектеВ отличие от строк,
Symbol
не виден при обычном переборе for...in
и Object.keys()
, что делает его полезным для скрытых свойств:const user = {
name: "Alice",
[Symbol("id")]: 123
};
console.log(user);
// { name: 'Alice', [Symbol(id)]: 123 }
console.log(Object.keys(user));
// ['name'] – символ не виден!
💡 Используется, когда нужно хранить "скрытые" свойства, которые не мешают основному API объекта.
3️⃣ Глобальные
Symbol
Если нам нужно глобально переиспользовать один и тот же Symbol, используем
Symbol.for()
:const sym1 = Symbol.for("shared");
const sym2 = Symbol.for("shared");
console.log(sym1 === sym2); // ✅ true
💡 В отличие от обычных
Symbol
, Symbol.for("key")
всегда создаёт или переиспользует существующий символ.🔎 Чтобы получить имя
Symbol
, используем .keyFor()
:const sym = Symbol.for("shared");
console.log(Symbol.keyFor(sym)); // "shared"
4️⃣
Symbol
в системных свойствахJavaScript использует
Symbol
для скрытых механизмов языка, например:🔹
Symbol.iterator
– делает объект итерируемым:const iterableObj = {
data: [1, 2, 3],
[Symbol.iterator]() {
let index = 0;
return {
next: () => ({
value: this.data[index++],
done: index > this.data.length
})
};
}
};
for (const value of iterableObj) {
console.log(value); // 1, 2, 3
}
🔹
Symbol.toPrimitive
– настраивает преобразование объекта в примитив:const obj = {
value: 100,
[Symbol.toPrimitive](hint) {
return hint === "string" ? "Объект" : this.value;
}
};
console.log(obj + 1); // 101
console.log(String(obj)); // "Объект"
📌 Когда использовать
Symbol
?✅ Для уникальных ключей в объектах, которые не конфликтуют с другими свойствами.
✅ Для скрытых свойств, которые не должны быть доступны через
Object.keys()
.✅ Для работы с системными
Symbol
, такими как Symbol.iterator
.🔄 Как работают генераторы и
В JavaScript есть обычные функции, которые выполняются сразу и до конца. А есть генераторы – это особый вид функций, которые могут приостанавливать выполнение и продолжать его позже. Давай разбираться!
1️⃣ Что такое генератор?
Генератор – это функция, которая может возвращать несколько значений по очереди.
📌 Для создания генератора используется
📌 Вместо
🔹 Каждый вызов
•
•
2️⃣ Как
Генератор не выполняется полностью при вызове – он останавливается на
🔹
3️⃣ Передача данных в
Можно передавать значения внутрь генератора с помощью
🔹 Первый
🔹 Второй
4️⃣
Если нужно вызвать другой генератор внутри текущего, можно использовать
🔹
5️⃣ Бесконечные генераторы
Генераторы можно использовать для создания бесконечных последовательностей:
🔹 В отличие от обычных циклов, бесконечный генератор не блокирует выполнение кода, потому что
📌 Итоги
✅ Генераторы (
✅
✅ Можно передавать данные в
✅
✅ Генераторы подходят для работы с потоками данных, итераторов и бесконечных последовательностей.
yield
в JavaScript?В JavaScript есть обычные функции, которые выполняются сразу и до конца. А есть генераторы – это особый вид функций, которые могут приостанавливать выполнение и продолжать его позже. Давай разбираться!
1️⃣ Что такое генератор?
Генератор – это функция, которая может возвращать несколько значений по очереди.
📌 Для создания генератора используется
function*
(звёздочка *
после function
).📌 Вместо
return
используется yield
, который приостанавливает выполнение функции.function* myGenerator() {
yield "Первое значение";
yield "Второе значение";
yield "Третье значение";
}
const gen = myGenerator(); // Создаём объект генератора
console.log(gen.next()); // { value: 'Первое значение', done: false }
console.log(gen.next()); // { value: 'Второе значение', done: false }
console.log(gen.next()); // { value: 'Третье значение', done: false }
console.log(gen.next()); // { value: undefined, done: true }
🔹 Каждый вызов
gen.next()
возвращает объект { value, done }
, где:•
value
– текущее значение из yield
,•
done
– true
, если генератор завершён.2️⃣ Как
yield
приостанавливает выполнение?Генератор не выполняется полностью при вызове – он останавливается на
yield
и ждёт следующего вызова .next()
.function* numbers() {
console.log("Запуск генератора");
yield 1;
console.log("Продолжение генератора");
yield 2;
}
const gen = numbers();
gen.next(); // Запуск генератора, { value: 1, done: false }
gen.next(); // Продолжение генератора, { value: 2, done: false }
gen.next(); // { value: undefined, done: true }
🔹
console.log()
срабатывает не сразу, а только при вызове .next()
!3️⃣ Передача данных в
yield
Можно передавать значения внутрь генератора с помощью
.next(value)
.function* sayHello() {
const name = yield "Как тебя зовут?";
yield `Привет, ${name}!`;
}
const gen = sayHello();
console.log(gen.next().value); // "Как тебя зовут?"
console.log(gen.next("Alice").value); // "Привет, Alice!"
🔹 Первый
.next()
просто начинает выполнение и доходит до первого yield
.🔹 Второй
.next("Alice")
передаёт "Alice"
внутрь генератора вместо yield
.4️⃣
yield*
– делегирование другому генераторуЕсли нужно вызвать другой генератор внутри текущего, можно использовать
yield*
:function* firstGen() {
yield "A";
yield "B";
}
function* secondGen() {
yield "1";
yield* firstGen();
yield "2";
}
const gen = secondGen();
console.log([...gen]); // ["1", "A", "B", "2"]
🔹
yield*
делегирует выполнение другому генератору.5️⃣ Бесконечные генераторы
Генераторы можно использовать для создания бесконечных последовательностей:
function* infiniteCounter() {
let i = 1;
while (true) {
yield i++;
}
}
const counter = infiniteCounter();
console.log(counter.next().value); // 1
console.log(counter.next().value); // 2
console.log(counter.next().value); // 3
🔹 В отличие от обычных циклов, бесконечный генератор не блокирует выполнение кода, потому что
yield
останавливает его до следующего вызова .next()
.📌 Итоги
✅ Генераторы (
function*
) позволяют останавливать и продолжать выполнение функций.✅
yield
возвращает значения и приостанавливает выполнение до следующего .next()
.✅ Можно передавать данные в
yield
через .next(value)
.✅
yield*
делегирует выполнение другому генератору.✅ Генераторы подходят для работы с потоками данных, итераторов и бесконечных последовательностей.
🔥 Прототипное наследование в JavaScript – что такое
JavaScript поддерживает объектно-ориентированное программирование (ООП), но его модель основана не на классах в привычном виде, а на прототипном наследовании.
Давай разберёмся, как это работает и какую роль в этом играют
1️⃣ Как работает прототипное наследование?
📌 В JavaScript каждый объект может наследовать свойства и методы от другого объекта через прототип.
Принцип работы:
• Если свойство ищется в объекте и не найдено, JavaScript ищет его в прототипе (в
• Если оно не найдено и там, поиск идёт дальше по цепочке прототипов.
📌 Здесь
2️⃣ Что такое __proto__?
🔹
⚠️ Не путай
•
•
❌
3️⃣ Object.create() – лучший способ наследования
Вместо того чтобы менять
📌
4️⃣ Цепочка прототипов (prototype chain)
Прототипное наследование образует цепочку (
📌 В конце цепочки всегда стоит
5️⃣ class – удобный синтаксис для прототипного наследования
В ES6 появился
📌 Под капотом
6️⃣ Наследование через extends
Классы позволяют удобно наследовать поведение с помощью
📌 Под капотом
🚀 Вывод
• Прототипное наследование – основа JavaScript.
•
• Используй
__proto__
и Object.create()
?JavaScript поддерживает объектно-ориентированное программирование (ООП), но его модель основана не на классах в привычном виде, а на прототипном наследовании.
Давай разберёмся, как это работает и какую роль в этом играют
__proto__
и Object.create()
! 🚀1️⃣ Как работает прототипное наследование?
📌 В JavaScript каждый объект может наследовать свойства и методы от другого объекта через прототип.
Принцип работы:
• Если свойство ищется в объекте и не найдено, JavaScript ищет его в прототипе (в
__proto__
).• Если оно не найдено и там, поиск идёт дальше по цепочке прототипов.
const parent = {
greet() {
console.log("Привет от родителя!");
}
};
const child = Object.create(parent); // Указываем, что parent – прототип
child.greet(); // "Привет от родителя!" (взято из прототипа)
📌 Здесь
child
не имеет метода greet()
, но он нашёл его в parent через прототип!2️⃣ Что такое __proto__?
🔹
__proto__
– это ссылка на прототип объекта.const obj = { a: 10 };
const newObj = Object.create(obj);
console.log(newObj.__proto__ === obj); // ✅ true
console.log(newObj.a); // 10 (взято из прототипа)
⚠️ Не путай
__proto__
и prototype
!•
__proto__
– это свойство объекта, которое указывает на его прототип.•
prototype
– это свойство функции-конструктора, которое будет прототипом всех её экземпляров.function User(name) {
this.name = name;
}
User.prototype.sayHi = function() {
console.log(`Привет, ${this.name}!`);
};
const user = new User("Alice");
console.log(user.__proto__ === User.prototype); // ✅ true
user.sayHi(); // "Привет, Alice!"
❌
__proto__
лучше не использовать напрямую, так как он медленный. Вместо него – Object.create()
или Object.setPrototypeOf()
.3️⃣ Object.create() – лучший способ наследования
Вместо того чтобы менять
__proto__
, лучше использовать Object.create(proto)
, который создаёт новый объект с указанным прототипом:const animal = { sound: "Meow" };
const cat = Object.create(animal);
console.log(cat.sound); // "Meow" (взято из прототипа)
📌
Object.create()
создаёт объект без лишних свойств и позволяет точно контролировать прототип.4️⃣ Цепочка прототипов (prototype chain)
Прототипное наследование образует цепочку (
prototype chain
):const grandparent = { grandparentProp: "👴" };
const parent = Object.create(grandparent);
parent.parentProp = "👨";
const child = Object.create(parent);
child.childProp = "👶";
console.log(child.childProp); // "👶" (из самого объекта)
console.log(child.parentProp); // "👨" (из прототипа)
console.log(child.grandparentProp); // "👴" (из прототипа прототипа)
console.log(child.toString()); // `[object Object]` (из Object.prototype)
📌 В конце цепочки всегда стоит
Object.prototype
, который имеет методы toString()
, hasOwnProperty()
и т.д.5️⃣ class – удобный синтаксис для прототипного наследования
В ES6 появился
class
, который не заменяет прототипное наследование, а делает его удобнее:class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} издаёт звук.`);
}
}
const cat = new Animal("Мурзик");
cat.speak(); // "Мурзик издаёт звук."
📌 Под капотом
class
всё равно работает через прототипы:console.log(Animal.prototype.speak); // Функция в прототипе класса
console.log(cat.__proto__ === Animal.prototype); // ✅ true
6️⃣ Наследование через extends
Классы позволяют удобно наследовать поведение с помощью
extends
:class Dog extends Animal {
speak() {
console.log(`${this.name} лает!`);
}
}
const rex = new Dog("Рекс");
rex.speak(); // "Рекс лает!"
📌 Под капотом
extends
работает так же, как Object.create()
.🚀 Вывод
• Прототипное наследование – основа JavaScript.
•
class
– это просто удобная оболочка над прототипами.• Используй
Object.create()
для лёгкого наследования.Разница между
🔹
• Преобразует переданное значение в число, а затем проверяет, является ли оно
• Из-за приведения типов
🔹
• Проверяет только те значения, которые уже являются
•
🔥 Разница в работе:
📌 Вывод:
•
•
isNaN
и Number.isNaN
заключается в их поведении при проверке значений, которые нельзя преобразовать в число.🔹
isNaN(value)
• Преобразует переданное значение в число, а затем проверяет, является ли оно
NaN
.• Из-за приведения типов
isNaN('abc')
сначала пытается преобразовать строку 'abc'
в число, получает NaN
, а затем проверяет isNaN(NaN)
, что дает true
.🔹
Number.isNaN(value)
• Проверяет только те значения, которые уже являются
NaN
без преобразования типов.•
'abc'
не является NaN
, а просто строка, поэтому Number.isNaN('abc')
возвращает false
.🔥 Разница в работе:
console.log(isNaN('abc')); // true (так как 'abc' → NaN)
console.log(Number.isNaN('abc')); // false (так как 'abc' не является NaN)
📌 Вывод:
•
isNaN
менее строгий и преобразует входные данные.•
Number.isNaN
более строгий и проверяет только NaN
без преобразования.9 законов, которые избавят тебя от боли в коде и хаоса в команде
Почему стоит их знать?
Потому что эти правила проверены десятками лет, сотнями проектов и тысячами разработчиков. Они помогают писать код проще, управлять проектами спокойнее и не наступать на чужие грабли.
1️⃣ Закон Парето
20% усилий дают 80% результата. Сосредоточься на главном.
2️⃣ Закон Гудхарта
Когда метрики становятся целью, они теряют смысл. KPI ≠ продуктивность.
3️⃣ Закон Конвея
Архитектура повторяет структуру команды. Бардак в команде = бардак в коде.
4️⃣ Закон Питера
Топовый дев ≠ топовый тимлид. Хард- и софт-скиллы — разные вселенные.
5️⃣ Закон Брукса
Больше разработчиков ≠ быстрее. Координация съедает время.
6️⃣ Закон Хайрама
Если у API много пользователей — кто-то точно начнёт использовать баг как фичу. Потом не отвяжешься.
7️⃣ Закон Линуса
Чем больше глаз смотрит на код — тем быстрее найдутся баги. Спасибо, опенсорс.
8️⃣ Закон Кернигана
Сложный код — отложенная боль. Пиши просто, не геройствуй.
9️⃣ Закон Хофтшадтера
Даже если думаешь, что учёл задержки — не учёл. Всегда добавляй буфер.
Почему стоит их знать?
Потому что эти правила проверены десятками лет, сотнями проектов и тысячами разработчиков. Они помогают писать код проще, управлять проектами спокойнее и не наступать на чужие грабли.
1️⃣ Закон Парето
20% усилий дают 80% результата. Сосредоточься на главном.
2️⃣ Закон Гудхарта
Когда метрики становятся целью, они теряют смысл. KPI ≠ продуктивность.
3️⃣ Закон Конвея
Архитектура повторяет структуру команды. Бардак в команде = бардак в коде.
4️⃣ Закон Питера
Топовый дев ≠ топовый тимлид. Хард- и софт-скиллы — разные вселенные.
5️⃣ Закон Брукса
Больше разработчиков ≠ быстрее. Координация съедает время.
6️⃣ Закон Хайрама
Если у API много пользователей — кто-то точно начнёт использовать баг как фичу. Потом не отвяжешься.
7️⃣ Закон Линуса
Чем больше глаз смотрит на код — тем быстрее найдутся баги. Спасибо, опенсорс.
8️⃣ Закон Кернигана
Сложный код — отложенная боль. Пиши просто, не геройствуй.
9️⃣ Закон Хофтшадтера
Даже если думаешь, что учёл задержки — не учёл. Всегда добавляй буфер.
Использование
❌ 1. Игнорирование реального типа
➡️ TypeScript "поверит" вам, что это
❌ 2. Скрытие ошибок
➡️ Даже если
❌ 3. Сложности с рефакторингом
Если вы используете
❌ 4. "Двойное" приведение типа — потенциально опасно
➡️ Часто используется для обхода ограничений компилятора, но абсолютно небезопасно. Это способ сказать TypeScript: "я знаю, что делаю", но чаще всего — вы не знаете.
❌ 5. Труднее читать и поддерживать
При большом количестве
• Сложнее понять, откуда и почему у значения появился тот или иной тип.
• Новому разработчику трудно доверять таким местам в коде.
✅ Когда as допустим:
• Приведение DOM-элементов:
• Ситуации, когда TypeScript не может вывести тип, а вы уверены в структуре данных.
• Работа с unknown:
🧠 Альтернатива: Type Guards
Вместо
Использование
as
в TypeScript — это type assertion (уточнение типа). Хотя это мощный инструмент, у него есть несколько минусов и подводных камней, особенно при неправильном использовании:❌ 1. Игнорирование реального типа
const value = "hello" as number;
➡️ TypeScript "поверит" вам, что это
number
, но на самом деле это строка. Это может привести к ошибкам во время выполнения, особенно если вы используете тип не по назначению.❌ 2. Скрытие ошибок
const user = getUser() as AdminUser;
➡️ Даже если
getUser()
возвращает GuestUser
, компилятор не проверит это, и вы потеряете типовую безопасность.❌ 3. Сложности с рефакторингом
Если вы используете
as
повсеместно, IDE и инструменты анализа кода теряют возможность правильно находить ошибки типов при изменениях в коде.❌ 4. "Двойное" приведение типа — потенциально опасно
const input = "123" as unknown as number;
➡️ Часто используется для обхода ограничений компилятора, но абсолютно небезопасно. Это способ сказать TypeScript: "я знаю, что делаю", но чаще всего — вы не знаете.
❌ 5. Труднее читать и поддерживать
При большом количестве
as
в коде:• Сложнее понять, откуда и почему у значения появился тот или иной тип.
• Новому разработчику трудно доверять таким местам в коде.
✅ Когда as допустим:
• Приведение DOM-элементов:
const input = document.querySelector('input') as HTMLInputElement;
• Ситуации, когда TypeScript не может вывести тип, а вы уверены в структуре данных.
• Работа с unknown:
const data = JSON.parse(json) as MyType;
🧠 Альтернатива: Type Guards
Вместо
as
, по возможности, используйте type guards:function isAdmin(user: User): user is Admin {
return (user as Admin).role === 'admin';
}
Использование
as
должно быть осознанным исключением, а не повседневной практикой.🧠 Web Worker в JavaScript — как запустить многопоточность в браузере
JavaScript — однопоточный, но бывают задачи, которые нагружают главный поток: парсинг больших данных, сложные расчёты и т.п. Чтобы не вешать интерфейс — можно использовать Web Worker!
👷♂️ Кто такой Web Worker?
Это отдельный поток, в котором можно запускать JS-код параллельно с основным. Он не имеет доступа к DOM, но может общаться с основным потоком через сообщения.
📦 Пример:
Файл:
Файл:
🔐 Ограничения:
• Нет доступа к
• Нельзя напрямую взаимодействовать с DOM
• Работает только с отдельными файлами (или blob'ами)
⚙️ Когда использовать?
✅ Долгие вычисления (например, графика, криптография)
✅ Парсинг больших JSON или CSV
✅ Работа с оффлайн данными или IndexedDB
JavaScript — однопоточный, но бывают задачи, которые нагружают главный поток: парсинг больших данных, сложные расчёты и т.п. Чтобы не вешать интерфейс — можно использовать Web Worker!
👷♂️ Кто такой Web Worker?
Это отдельный поток, в котором можно запускать JS-код параллельно с основным. Он не имеет доступа к DOM, но может общаться с основным потоком через сообщения.
📦 Пример:
Файл:
worker.js
// worker.js
self.onmessage = function (e) {
const result = e.data * 2;
self.postMessage(result);
};
Файл:
main.js
const worker = new Worker('worker.js');
worker.postMessage(10);
worker.onmessage = function (e) {
console.log('Результат от воркера:', e.data); // 20
};
🔐 Ограничения:
• Нет доступа к
window
, document
, localStorage
и т.п.• Нельзя напрямую взаимодействовать с DOM
• Работает только с отдельными файлами (или blob'ами)
⚙️ Когда использовать?
✅ Долгие вычисления (например, графика, криптография)
✅ Парсинг больших JSON или CSV
✅ Работа с оффлайн данными или IndexedDB
🕸 DOM — что это такое и какие его бывают типы?
Ты слышал про DOM, Shadow DOM, Virtual DOM, Light DOM... но что это всё значит? Давай разложим по полочкам.
📖 Что такое DOM?
DOM (Document Object Model) — это объектная модель документа.
Когда ты открываешь HTML-страницу в браузере, она превращается в структуру объектов, с которыми можно взаимодействовать через JavaScript.
Каждый HTML-элемент становится узлом дерева DOM, и ты можешь:
• Добавлять/удалять элементы (appendChild, removeChild)
• Менять текст и атрибуты (textContent, setAttribute)
• Реагировать на события (addEventListener)
👉 Это фундамент всей клиентской части фронтенда.
🧩 Основные типы DOM и где они применяются:
🔹 1. HTML DOM — базовый, реальный DOM
Это та модель, которую создаёт браузер из HTML-документа.
С ним ты работаешь ежедневно:
🔸 Доступ к элементам, атрибутам, тексту
🔸 Изменение структуры страницы
🔸 Добавление и удаление узлов
HTML DOM — это именно то, что ты видишь в инструментах разработчика в браузере (
🔹 2. XML DOM — работа с XML-структурами
DOM применим не только к HTML. Например, если ты получаешь XML-файл (например, SVG или RSS), ты тоже можешь "обходить" его как дерево.
📌 XML DOM работает так же, как HTML DOM, но с XML-документами.
🔹 3. Virtual DOM — концепт из фреймворков
Это не настоящий DOM, а его копия в памяти.
📌 Фреймворки вроде React, Vue, Preact создают Virtual DOM, чтобы не трогать реальный DOM напрямую.
Как это работает:
1. Рендер компонента создаёт виртуальное дерево
2. Когда данные меняются, создаётся новое дерево
3.. Сравниваются старое и новое → браузеру отправляется минимум изменений
🔧 Это ускоряет перерисовку, особенно при большом количестве элементов.
❗️ Важно: Virtual DOM — это внутренний механизм фреймворка, в браузере его нет.
🔹 4. Shadow DOM — скрытый DOM
Это способ инкапсуляции, введённый вместе с Web Components.
Преимущества:
• 🔒 Полная изоляция стилей и структуры
• ✅ Никакие внешние CSS не затрагивают Shadow DOM
• 👀 Используется в <video>, <input type="range">, Web Components
📌 mode: 'open' позволяет получить доступ к shadowRoot, а 'closed' — нет.
🔹 5. Light DOM — то, что вставляется через <slot>
Когда ты используешь Web Component и в него передаёшь контент — этот контент называется Light DOM.
А внутри my-box:
Light DOM существует вне Shadow DOM, но может быть вставлен внутрь через <slot>.
🔹 6. DOM Tree (дерево узлов)
DOM состоит из узлов (nodes):
•
•
•
•
•
Это дерево, и ты можешь перемещаться по нему:
Ты слышал про DOM, Shadow DOM, Virtual DOM, Light DOM... но что это всё значит? Давай разложим по полочкам.
📖 Что такое DOM?
DOM (Document Object Model) — это объектная модель документа.
Когда ты открываешь HTML-страницу в браузере, она превращается в структуру объектов, с которыми можно взаимодействовать через JavaScript.
Каждый HTML-элемент становится узлом дерева DOM, и ты можешь:
• Добавлять/удалять элементы (appendChild, removeChild)
• Менять текст и атрибуты (textContent, setAttribute)
• Реагировать на события (addEventListener)
👉 Это фундамент всей клиентской части фронтенда.
🧩 Основные типы DOM и где они применяются:
🔹 1. HTML DOM — базовый, реальный DOM
Это та модель, которую создаёт браузер из HTML-документа.
С ним ты работаешь ежедневно:
document.querySelector('button').addEventListener('click', () => {
alert('Привет, DOM!');
});
🔸 Доступ к элементам, атрибутам, тексту
🔸 Изменение структуры страницы
🔸 Добавление и удаление узлов
HTML DOM — это именно то, что ты видишь в инструментах разработчика в браузере (
Elements
).🔹 2. XML DOM — работа с XML-структурами
DOM применим не только к HTML. Например, если ты получаешь XML-файл (например, SVG или RSS), ты тоже можешь "обходить" его как дерево.
const parser = new DOMParser();
const xml = parser.parseFromString(xmlString, "application/xml");
console.log(xml.getElementsByTagName('title')[0].textContent);
📌 XML DOM работает так же, как HTML DOM, но с XML-документами.
🔹 3. Virtual DOM — концепт из фреймворков
Это не настоящий DOM, а его копия в памяти.
📌 Фреймворки вроде React, Vue, Preact создают Virtual DOM, чтобы не трогать реальный DOM напрямую.
Как это работает:
1. Рендер компонента создаёт виртуальное дерево
2. Когда данные меняются, создаётся новое дерево
3.. Сравниваются старое и новое → браузеру отправляется минимум изменений
🔧 Это ускоряет перерисовку, особенно при большом количестве элементов.
❗️ Важно: Virtual DOM — это внутренний механизм фреймворка, в браузере его нет.
🔹 4. Shadow DOM — скрытый DOM
Это способ инкапсуляции, введённый вместе с Web Components.
const el = document.querySelector('my-widget');
const shadow = el.attachShadow({ mode: 'open' });
shadow.innerHTML = `<style>p { color: red; }</style><p>Привет</p>`;
Преимущества:
• 🔒 Полная изоляция стилей и структуры
• ✅ Никакие внешние CSS не затрагивают Shadow DOM
• 👀 Используется в <video>, <input type="range">, Web Components
📌 mode: 'open' позволяет получить доступ к shadowRoot, а 'closed' — нет.
🔹 5. Light DOM — то, что вставляется через <slot>
Когда ты используешь Web Component и в него передаёшь контент — этот контент называется Light DOM.
<my-box>
<p>Я — в Light DOM</p>
</my-box>
А внутри my-box:
<slot></slot>
Light DOM существует вне Shadow DOM, но может быть вставлен внутрь через <slot>.
🔹 6. DOM Tree (дерево узлов)
DOM состоит из узлов (nodes):
•
document
– корень•
element
– HTML-теги•
text
– текст между тегами•
comment
– <!-- комментарии -->•
attribute
– id, class и др. (доступны через .getAttribute())Это дерево, и ты можешь перемещаться по нему:
parentNode
, childNodes
, nextSibling
, firstElementChild
и т.д.В прошлом посте упоминались Web Components, я хочу рассказать о них подробнее!
Web Components — это нативный способ создавать переиспользуемые, инкапсулированные элементы прямо на чистом JavaScript, без привязки к фреймворкам.
⚙️ Что такое Web Components?
Состоит из трёх ключевых технологий:
1. Custom Elements — создание своих HTML-тегов
2. Shadow DOM — инкапсуляция структуры и стилей
3. HTML Templates — шаблоны с отложенным рендерингом
Пример:
Теперь ты можешь использовать <my-tag></my-tag> как обычный HTML-элемент.
✅ Плюсы Web Components
🔹 Фреймворк-независимость — работают в любом проекте: React, Vue, Angular или просто HTML
🔹 Изоляция — стили и структура компонента защищены через Shadow DOM
🔹 Гибкость в использовании — можно зашарить компонент через CDN или npm
🔹 Поддержка браузерами — почти все современные браузеры (включая Safari и Edge)
🔹 Идеально для UI-библиотек и виджетов — калькуляторы, формы, кнопки, карты, чаты
🚨 На что обратить внимание
🔍 Не пытайся встроить Web Components в SPA без нужды — в React/Vue уже есть свои системы компонентов, они эффективнее внутри экосистемы
🧪 Тестируй кроссбраузерность — особенно если используешь slot, form-associated custom elements или adoptedStyleSheets
📦 Для сложных компонентов лучше использовать библиотеки-обёртки, например:
• Lit — удобный синтаксис
• Stencil — компилятор, создающий Web Components с возможностью SSR
• Haunted — Web Components + Hooks
Web Components — это не замена фреймворкам, а инструмент. И если использовать его по назначению — это мощная технология:
✅ Пишите компонент один раз → используйте везде
✅ Минимум зависимостей
✅ Идеальны для SDK и внешних виджетов
Web Components — это нативный способ создавать переиспользуемые, инкапсулированные элементы прямо на чистом JavaScript, без привязки к фреймворкам.
⚙️ Что такое Web Components?
Состоит из трёх ключевых технологий:
1. Custom Elements — создание своих HTML-тегов
2. Shadow DOM — инкапсуляция структуры и стилей
3. HTML Templates — шаблоны с отложенным рендерингом
Пример:
class MyTag extends HTMLElement {
connectedCallback() {
this.attachShadow({ mode: 'open' });
this.shadowRoot.innerHTML = `<p>Привет из компонента!</p>`;
}
}
customElements.define('my-tag', MyTag);
Теперь ты можешь использовать <my-tag></my-tag> как обычный HTML-элемент.
✅ Плюсы Web Components
🔹 Фреймворк-независимость — работают в любом проекте: React, Vue, Angular или просто HTML
🔹 Изоляция — стили и структура компонента защищены через Shadow DOM
🔹 Гибкость в использовании — можно зашарить компонент через CDN или npm
🔹 Поддержка браузерами — почти все современные браузеры (включая Safari и Edge)
🔹 Идеально для UI-библиотек и виджетов — калькуляторы, формы, кнопки, карты, чаты
🚨 На что обратить внимание
🔍 Не пытайся встроить Web Components в SPA без нужды — в React/Vue уже есть свои системы компонентов, они эффективнее внутри экосистемы
🧪 Тестируй кроссбраузерность — особенно если используешь slot, form-associated custom elements или adoptedStyleSheets
📦 Для сложных компонентов лучше использовать библиотеки-обёртки, например:
• Lit — удобный синтаксис
• Stencil — компилятор, создающий Web Components с возможностью SSR
• Haunted — Web Components + Hooks
Web Components — это не замена фреймворкам, а инструмент. И если использовать его по назначению — это мощная технология:
✅ Пишите компонент один раз → используйте везде
✅ Минимум зависимостей
✅ Идеальны для SDK и внешних виджетов
🚧 Рабочие будни: микрофронты, Astro и новая идея
В последнее время я плотно работаю с Astro — фреймворком, который позволяет миксовать всё, что угодно. У меня в проекте сейчас одновременно используются и React, и Vue, и всё это работает как микрофронтенды внутри одного приложения.
⚙️ Это гибко, но на практике возникает куча проблем:
— нужен UI-kit, который будет одинаково работать во всех фреймворках
— хочется реактивность
— хочется инкапсуляцию и лёгкость переиспользования
— и при этом — минимум зависимости от среды
🧠 Поэтому я решил собрать свой UI-kit, основанный на следующих технологиях:
✅ 💡 Lit — для генерации Web Components с реактивностью и Shadow DOM
✅ 🎨 PostCSS — для трансформации и оптимизации стилей (плагины, кастомизация, минификация)
✅ 🧠 Partytown — чтобы переносить тяжёлые вычисления в Web Workers, разгружая главный поток
📦 В результате я хочу получить:
— Нативные веб-компоненты, работающие в React/Vue/HTML
— Стили, изолированные через Shadow DOM и оптимизированные через PostCSS
— Возможность легко встраивать эти компоненты в любую среду без боевой зависимости от фреймворка
— И, конечно, быструю загрузку и отзывчивость за счёт Partytown
Если тебе интересна тема UI-kit без фреймворк-зависимости, реактивных веб-компонентов, или хочешь следить за тем, как я это буду собирать — напиши в комменты, задавай вопросы и я в будущем оформлю по этой теме новые посты🔥
В последнее время я плотно работаю с Astro — фреймворком, который позволяет миксовать всё, что угодно. У меня в проекте сейчас одновременно используются и React, и Vue, и всё это работает как микрофронтенды внутри одного приложения.
⚙️ Это гибко, но на практике возникает куча проблем:
— нужен UI-kit, который будет одинаково работать во всех фреймворках
— хочется реактивность
— хочется инкапсуляцию и лёгкость переиспользования
— и при этом — минимум зависимости от среды
🧠 Поэтому я решил собрать свой UI-kit, основанный на следующих технологиях:
✅ 💡 Lit — для генерации Web Components с реактивностью и Shadow DOM
✅ 🎨 PostCSS — для трансформации и оптимизации стилей (плагины, кастомизация, минификация)
✅ 🧠 Partytown — чтобы переносить тяжёлые вычисления в Web Workers, разгружая главный поток
📦 В результате я хочу получить:
— Нативные веб-компоненты, работающие в React/Vue/HTML
— Стили, изолированные через Shadow DOM и оптимизированные через PostCSS
— Возможность легко встраивать эти компоненты в любую среду без боевой зависимости от фреймворка
— И, конечно, быструю загрузку и отзывчивость за счёт Partytown
Если тебе интересна тема UI-kit без фреймворк-зависимости, реактивных веб-компонентов, или хочешь следить за тем, как я это буду собирать — напиши в комменты, задавай вопросы и я в будущем оформлю по этой теме новые посты🔥
🛠 Почему я выбрал Astro для SSR
Когда я начал думать о серверном рендеринге (SSR) в своём проекте, выбор стоял между классическими решениями вроде Next.js, Nuxt, а также более лёгкими альтернативами.
🔍 После долгих тестов и сравнений я остановился на Astro. Вот почему:
✅ 1. Истинный Island Architecture
Astro по-настоящему рендерит только то, что нужно.
Компоненты по умолчанию — это просто HTML без JS на клиенте. Хочешь интерактивность? Подключай
Это даёт:
— Меньше JavaScript на клиенте
— Мгновенную отрисовку (zero-js для части контента)
— Отличную производительность из коробки
⚙️ 2. SSR без боли
Astro позволяет включить SSR буквально одной настройкой. Всё работает на Vite, поддерживает адаптеры под любую платформу: Node, Deno, Edge, Vercel, Cloudflare Workers.
Ты пишешь как будто SPA, но получаешь всё, что нужно для SEO и скорости:
— Серверный рендеринг
— Стриминг
— Пререндеринг
— Кэширование
🧩 3. Полная свобода компонентов
Astro позволяет использовать любой фреймворк внутри проекта:
🔹 React
🔹 Vue
🔹 Svelte
🔹 Solid
🔹 Web Components
Можно собирать микрофронты, не переписывая ничего с нуля. У меня в проекте React и Vue живут рядом — и это не вызывает боли.
🧼 4. Простота + мощь
Astro не нагружен абстракциями. Он очень чистый и понятный:
— минимальная настройка
— файлы .astro читаются легко
— всё работает быстро и стабильно
📌 Итог:
Я выбрал Astro, потому что он:
✅ делает SSR проще
✅ уменьшает клиентский JS
✅ даёт контроль и гибкость
✅ не заставляет выбирать один фреймворк
Когда я начал думать о серверном рендеринге (SSR) в своём проекте, выбор стоял между классическими решениями вроде Next.js, Nuxt, а также более лёгкими альтернативами.
🔍 После долгих тестов и сравнений я остановился на Astro. Вот почему:
✅ 1. Истинный Island Architecture
Astro по-настоящему рендерит только то, что нужно.
Компоненты по умолчанию — это просто HTML без JS на клиенте. Хочешь интерактивность? Подключай
client:load
, client:idle
, client:visible
— всё под контролем.Это даёт:
— Меньше JavaScript на клиенте
— Мгновенную отрисовку (zero-js для части контента)
— Отличную производительность из коробки
⚙️ 2. SSR без боли
Astro позволяет включить SSR буквально одной настройкой. Всё работает на Vite, поддерживает адаптеры под любую платформу: Node, Deno, Edge, Vercel, Cloudflare Workers.
Ты пишешь как будто SPA, но получаешь всё, что нужно для SEO и скорости:
— Серверный рендеринг
— Стриминг
— Пререндеринг
— Кэширование
🧩 3. Полная свобода компонентов
Astro позволяет использовать любой фреймворк внутри проекта:
🔹 React
🔹 Vue
🔹 Svelte
🔹 Solid
🔹 Web Components
Можно собирать микрофронты, не переписывая ничего с нуля. У меня в проекте React и Vue живут рядом — и это не вызывает боли.
🧼 4. Простота + мощь
Astro не нагружен абстракциями. Он очень чистый и понятный:
— минимальная настройка
— файлы .astro читаются легко
— всё работает быстро и стабильно
📌 Итог:
Я выбрал Astro, потому что он:
✅ делает SSR проще
✅ уменьшает клиентский JS
✅ даёт контроль и гибкость
✅ не заставляет выбирать один фреймворк
🌐 Проблема современной фронтенд-индустрии
Мы пришли к странной точке: чтобы отрендерить пару кнопок и табов — мы тащим React, Router, Redux, Tailwind, MUI, Query, Zustand, i18n, Formik и ещё десяток обвязок поверх этого всего.
📦 Каждый новый проект начинается не с разработки, а с выбора стека из 20 npm-пакетов.
🧩 Компоненты завязаны на фреймворки.
🐘 Проекты быстро разрастаются, сложны в поддержке и часто зависят от устаревших библиотек.
🔁 Я решил двигаться в обратную сторону.
Моя цель — вернуться к корням:
✅ меньше зависимостей
✅ больше контроля
✅ меньше магии — больше прозрачности
🛠 Как я это делаю:
Я использую Astro как базу, потому что он:
— отлично справляется с SSR
— поддерживает HTML-first подход
— позволяет использовать любой фреймворк, но не заставляет
— работает с чистой ванилой и Web Components без боли
🏗 План:
1. Писать компоненты как Web Components на Lit
2. Использовать PostCSS для стилизации вместо Tailwind и UI-фреймворков
3. Постепенно отказываться от React и Vue
4. Использовать Partytown, чтобы тяжёлые операции не трогали главный поток
5. Держать код максимально лёгким, понятным и кросс-фреймворк-френдли
🧠 Это не "анти-фреймворковая" позиция.
Я просто считаю, что фреймворк должен быть инструментом, а не фундаментом всего.
А сейчас у многих проектов — полная зависимость от того, на чём они начали.
Мы пришли к странной точке: чтобы отрендерить пару кнопок и табов — мы тащим React, Router, Redux, Tailwind, MUI, Query, Zustand, i18n, Formik и ещё десяток обвязок поверх этого всего.
📦 Каждый новый проект начинается не с разработки, а с выбора стека из 20 npm-пакетов.
🧩 Компоненты завязаны на фреймворки.
🐘 Проекты быстро разрастаются, сложны в поддержке и часто зависят от устаревших библиотек.
🔁 Я решил двигаться в обратную сторону.
Моя цель — вернуться к корням:
✅ меньше зависимостей
✅ больше контроля
✅ меньше магии — больше прозрачности
🛠 Как я это делаю:
Я использую Astro как базу, потому что он:
— отлично справляется с SSR
— поддерживает HTML-first подход
— позволяет использовать любой фреймворк, но не заставляет
— работает с чистой ванилой и Web Components без боли
🏗 План:
1. Писать компоненты как Web Components на Lit
2. Использовать PostCSS для стилизации вместо Tailwind и UI-фреймворков
3. Постепенно отказываться от React и Vue
4. Использовать Partytown, чтобы тяжёлые операции не трогали главный поток
5. Держать код максимально лёгким, понятным и кросс-фреймворк-френдли
🧠 Это не "анти-фреймворковая" позиция.
Я просто считаю, что фреймворк должен быть инструментом, а не фундаментом всего.
А сейчас у многих проектов — полная зависимость от того, на чём они начали.