Javascript Flashcards
- Что такое замыкание.
Замыкание (или closure) — это концепция в программировании, при которой функция сохраняет доступ к своей лексической области видимости, даже после того, как эта функция завершила выполнение. Проще говоря, замыкание позволяет функции “помнить” переменные, которые были доступны ей на момент создания, даже если она выполняется в другом контексте.
Основные характеристики замыканий
1. Лексическая область видимости:
o Замыкание сохраняет доступ к переменным, определённым в окружающем контексте функции, где оно было создано.
2. Сохранение состояния:
o Функция-замыкание сохраняет состояние окружающей области видимости, что позволяет ей работать с данными, которые могли бы быть удалены при обычном завершении функции.
3. Изоляция:
o Замыкание предоставляет механизм для создания приватных переменных и функций, которые не видны за пределами замыкания.
Пример замыкания
Рассмотрим простой пример замыкания в JavaScript:
function createCounter() {
let count = 0; // Переменная, доступная только в контексте createCounter
return function() {
count += 1; // Вложенная функция имеет доступ к переменной count
return count;
};
}
const counter = createCounter(); // counter теперь содержит замыкание
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
В этом примере:
* createCounter возвращает анонимную функцию, которая имеет доступ к переменной count.
* Переменная count остаётся доступной внутри возвращённой функции, несмотря на то, что выполнение функции createCounter завершено.
Как это работает
1. Создание функции-замыкания:
o Когда вызывается createCounter, создаётся функция с доступом к переменной count. Эта функция возвращается как результат выполнения createCounter.
2. Сохранение состояния:
o Когда функция-замыкание вызывается (через counter()), она получает доступ к count и может изменять его. Переменная count остаётся в памяти до тех пор, пока функция-замыкание доступна, даже если выполнение createCounter завершено.
3. Изоляция данных:
o Переменная count не доступна за пределами функции-замыкания, что обеспечивает инкапсуляцию и защиту данных от внешнего вмешательства.
Использование замыканий
1. Инкапсуляция:
o Замыкания позволяют создавать функции с приватными данными. Например, можно создать объект с приватным состоянием и методами, доступными только для этого объекта.
2. Функции обратного вызова (callback functions):
o Замыкания широко используются в асинхронном программировании и обработке событий, где функция должна сохранять контекст при её вызове в будущем.
3. Модули и пространства имён:
o Замыкания позволяют создавать модули и пространства имён, которые изолируют данные и функции от глобального контекста.
Пример использования замыкания для создания приватных данных
function createPerson(name) {
let age = 30; // Приватное состояние
return {
getName: function() {
return name;
},
getAge: function() {
return age;
},
setAge: function(newAge) {
if (newAge > 0) {
age = newAge;
}
}
};
}
const person = createPerson(‘John’);
console.log(person.getName()); // John
console.log(person.getAge()); // 30
person.setAge(35);
console.log(person.getAge()); // 35
В этом примере:
* Переменная age является приватной и доступна только через методы объекта, возвращённого функцией createPerson.
Заключение
Замыкания — мощный механизм в JavaScript, позволяющий сохранять состояние и создавать функции с приватными данными. Они используются для создания инкапсулированных данных, функций обратного вызова и модулей. Понимание замыканий важно для эффективного написания кода, особенно в асинхронном программировании и при работе с функциями, которые сохраняют контекст.
- Какие существуют типы наследования.
В JavaScript существуют несколько способов организации наследования, каждый из которых имеет свои особенности и применяется в зависимости от задач, которые необходимо решить. Основные типы наследования в JavaScript включают:
- Прототипное наследование (Prototype-based Inheritance)
JavaScript является языком с прототипным наследованием. Это означает, что объекты могут наследовать свойства и методы от других объектов через цепочку прототипов.
Пример:
const parent = {
greet() {
console.log(‘Hello!’);
}
};
const child = Object.create(parent);
child.greet(); // Hello!
В этом примере объект child наследует метод greet от объекта parent через прототип.
- Конструкторное наследование (Constructor-based Inheritance)
Конструкторные функции используются для создания и инициализации объектов, и они могут использоваться для организации наследования.
Пример:
function Parent(name) {
this.name = name;
}
Parent.prototype.greet = function() {
console.log(Hello, my name is ${this.name}
);
};
function Child(name, age) {
Parent.call(this, name); // Вызов конструктора родителя
this.age = age;
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
const child = new Child(‘Alice’, 10);
child.greet(); // Hello, my name is Alice
В этом примере функция-конструктор Child наследует от функции-конструктора Parent.
- Наследование с использованием class (Class-based Inheritance)
Появившийся в ES6 синтаксис классов предоставляет более удобный и привычный для разработчиков с других языков способ организации наследования.
Пример:
class Parent {
constructor(name) {
this.name = name;
}greet() {
console.log(Hello, my name is ${this.name}
);
}
}
class Child extends Parent {
constructor(name, age) {
super(name); // Вызов конструктора родителя
this.age = age;
}
}
const child = new Child(‘Alice’, 10);
child.greet(); // Hello, my name is Alice
В этом примере Child наследует от Parent с использованием синтаксиса классов и ключевого слова extends.
- Миксины (Mixins)
Миксины позволяют добавлять функциональность в классы без использования цепочки прототипов. Это своего рода “смешивание” различных объектов или классов для достижения многоразового использования кода.
Пример:
const sayHiMixin = {
sayHi() {
console.log(‘Hi!’);
}
};
const sayByeMixin = {
sayBye() {
console.log(‘Bye!’);
}
};
class User {
constructor(name) {
this.name = name;
}
}
Object.assign(User.prototype, sayHiMixin);
Object.assign(User.prototype, sayByeMixin);
const user = new User(‘John’);
user.sayHi(); // Hi!
user.sayBye(); // Bye!
В этом примере функциональность из sayHiMixin и sayByeMixin добавляется в User.
- Композиция (Composition)
Композиция предполагает создание новых объектов путем объединения функциональности из множества объектов, а не через цепочку наследования.
Пример:
javascript
Копировать код
function withSayHi(obj) {
obj.sayHi = function() {
console.log(‘Hi!’);
};
return obj;
}
function withSayBye(obj) {
obj.sayBye = function() {
console.log(‘Bye!’);
};
return obj;
}
const user = withSayHi(withSayBye({ name: ‘John’ }));
user.sayHi(); // Hi!
user.sayBye(); // Bye!
В этом примере мы создаем новый объект с функциональностью sayHi и sayBye через композицию.
Заключение
JavaScript предоставляет различные способы для организации наследования, каждый из которых подходит для разных задач:
* Прототипное наследование используется для прямого наследования объектов.
* Конструкторное наследование применяется для создания новых объектов через функции-конструкторы.
* Наследование с использованием class предоставляет более современный и удобный синтаксис для работы с наследованием.
* Миксины позволяют добавлять функциональность к классам или объектам.
* Композиция обеспечивает гибкий подход к объединению функциональности без использования наследования.
Каждый из этих подходов имеет свои преимущества и используется в зависимости от конкретных требований проекта.
- Что такое прототип.
В JavaScript прототип — это механизм, который позволяет объектам наследовать свойства и методы от других объектов. Это ключевой аспект прототипного наследования, которое является основным способом реализации наследования в JavaScript.
Основные концепции прототипа
1. Прототип объекта:
o Каждый объект в JavaScript имеет скрытое свойство, которое указывает на другой объект, называемый прототипом. Это свойство позволяет объекту наследовать свойства и методы от своего прототипа.
2. Цепочка прототипов (Prototype Chain):
o Когда свойство или метод ищется в объекте, JavaScript сначала проверяет этот объект. Если свойство или метод не найдены, поиск продолжается по цепочке прототипов, которая ведет к Object.prototype в конце. Если свойство не найдено даже там, результатом будет undefined.
3. Конструкторы и прототипы:
o Функции-конструкторы создают новые объекты и автоматически устанавливают их прототип на prototype свойства функции-конструктора. Это позволяет всем объектам, созданным через конструктор, наследовать методы и свойства от прототипа конструктора.
Примеры использования прототипов
Прототип объекта
const person = {
greet() {
console.log(“Hello!”);
}
};
const john = Object.create(person); // john наследует свойства и методы от person
john.greet(); // Hello!
В этом примере:
* john создается с прототипом person.
* Метод greet доступен для john, так как он наследуется от прототипа.
Прототип функции-конструктора
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function() {
console.log(Hello, my name is ${this.name}
);
};
const alice = new Person(‘Alice’);
alice.sayHello(); // Hello, my name is Alice
В этом примере:
* Функция-конструктор Person создает объекты с name свойством.
* Метод sayHello добавляется в Person.prototype, поэтому все объекты, созданные с помощью конструктора Person, могут использовать этот метод.
Основные особенности и особенности прототипов
1. Наследование:
o Прототипы позволяют объектам наследовать свойства и методы от других объектов. Это дает возможность создавать иерархии объектов и повторно использовать код.
- Цепочка прототипов:
o Каждый объект имеет ссылку на свой прототип, что позволяет создать цепочку прототипов. Поиск свойств и методов происходит по цепочке до тех пор, пока не будет найдено нужное свойство или не достигнет конца цепочки (Object.prototype). - Динамическое изменение:
o Вы можете добавлять или изменять свойства и методы в прототипе даже после создания объектов. Изменения в прототипе будут доступны всем объектам, которые наследуют от него. - Свойство __proto__:
o Это устаревшее свойство, которое можно использовать для получения или установки прототипа объекта. Рекомендуется использовать Object.getPrototypeOf() и Object.setPrototypeOf() для работы с прототипами.
Пример динамического изменения прототипа
const animal = {
makeSound() {
console.log(“Generic animal sound”);
}
};
const dog = Object.create(animal);
dog.makeSound(); // Generic animal sound
// Добавляем метод в прототип dog
dog.makeSound = function() {
console.log(“Woof!”);
};
dog.makeSound(); // Woof!
// Изменяем прототип объекта dog
Object.setPrototypeOf(dog, animal);
dog.makeSound(); // Generic animal sound
В этом примере:
* Метод makeSound изменяется у объекта dog, но затем восстанавливается его поведение через изменение прототипа.
Заключение
Прототипы в JavaScript — это мощный механизм для реализации наследования и повторного использования кода. Понимание работы прототипов позволяет эффективно создавать гибкие и масштабируемые приложения, управлять наследованием и динамически изменять поведение объектов.
- Разница между =, ==, ===.
В JavaScript существуют три оператора сравнения, которые используются для проверки равенства значений: =, ==, и ===. Эти операторы имеют разные цели и поведение, что важно понимать при написании кода.
- Оператор присваивания =
Описание:
* Оператор = используется для присваивания значения переменной.
Пример:
let x = 10; // Присваивание значения 10 переменной x
Основные моменты:
* Этот оператор не используется для сравнения. Он просто присваивает значение переменной. - Оператор сравнения ==
Описание:
* Оператор == используется для проверки равенства двух значений. При этом происходит неявное преобразование типов (type coercion), если типы сравниваемых значений различаются.
Пример:
console.log(5 == ‘5’); // true, так как строка ‘5’ приводится к числу 5
console.log(true == 1); // true, так как true приводится к числу 1
console.log(null == undefined); // true, особый случай в JavaScript
Основные моменты:
* Происходит автоматическое преобразование типов, чтобы сделать сравнение возможным.
* Может привести к неожиданным результатам из-за приведения типов. - Оператор строгого равенства ===
Описание:
* Оператор === используется для проверки равенства двух значений, при этом не происходит приведение типов. Значения должны быть равны и по типу, и по значению.
Пример:
console.log(5 === ‘5’); // false, так как типы различаются (число и строка)
console.log(true === 1); // false, так как типы различаются (boolean и число)
console.log(null === undefined); // false, так как это разные значения
Основные моменты:
* Сравниваются как значения, так и типы.
* Предпочтительнее использовать === для избежания неожиданных результатов из-за преобразования типов.
Сравнение примеров
Примеры с ==:
console.log(0 == ‘0’); // true, ‘0’ приводится к числу 0
console.log(false == 0); // true, false приводится к числу 0
console.log([] == false); // true, [] приводится к пустой строке, которая затем приводится к false
Примеры со ===:
console.log(0 === ‘0’); // false, типы различаются (число и строка)
console.log(false === 0); // false, типы различаются (boolean и число)
console.log([] === false); // false, типы различаются (массив и boolean)
Заключение
* =: Оператор присваивания, используется для установки значения переменной.
* ==: Оператор сравнения с неявным преобразованием типов, может приводить к неожиданным результатам.
* ===: Оператор строгого равенства, проверяет и значение, и тип, предпочтителен для большинства случаев, чтобы избежать проблем с неявным преобразованием типов.
Использование === помогает избежать многих проблем, связанных с типами данных и их приведением, и делает код более предсказуемым и безопасным.
- Отличия TypeScript от JavaScript.
TypeScript и JavaScript — это два языка программирования, которые тесно связаны, но имеют несколько ключевых отличий. TypeScript является надмножеством JavaScript, что означает, что любой корректный код JavaScript является корректным кодом TypeScript, но TypeScript добавляет некоторые дополнительные возможности и функции.
Основные отличия TypeScript от JavaScript
1. Статическая типизация
TypeScript:
* TypeScript добавляет статическую типизацию в JavaScript. Это позволяет разработчикам указывать типы переменных, параметров функций и возвращаемых значений. Типы проверяются на этапе компиляции, что помогает обнаружить ошибки до выполнения кода.
Пример:
let message: string = “Hello, TypeScript!”;
let count: number = 10;
function greet(name: string): string {
return Hello, ${name}
;
}
JavaScript:
* JavaScript является динамически типизированным языком, что означает, что типы проверяются только во время выполнения. Нет необходимости указывать типы переменных или функций.
Пример:
let message = “Hello, JavaScript!”;
let count = 10;
function greet(name) {
return Hello, ${name}
;
}
- Интерфейсы и типы
TypeScript:
* TypeScript позволяет определять интерфейсы и типы для структур данных, что упрощает управление сложными типами и обеспечивает лучшее автодополнение и проверку типов.
Пример:
interface Person {
name: string;
age: number;
}
let person: Person = {
name: “Alice”,
age: 30
};
JavaScript:
* В JavaScript нет встроенной поддержки интерфейсов или типов. Однако, можно использовать объекты для создания структур данных.
Пример:
let person = {
name: “Alice”,
age: 30
};
- Классы и модули
TypeScript:
* TypeScript поддерживает современные возможности объектно-ориентированного программирования, включая классы, модификаторы доступа (public, private, protected) и декораторы. TypeScript также поддерживает модули и пространственные имена (namespaces).
Пример:
class Animal {
private name: string;constructor(name: string) {
this.name = name;
}public getName(): string {
return this.name;
}
}
JavaScript:
* JavaScript поддерживает классы (начиная с ES6) и модули (начиная с ES6), но не имеет встроенной поддержки модификаторов доступа или декораторов.
Пример:
class Animal {
constructor(name) {
this.name = name;
}getName() {
return this.name;
}
} - Компиляция
TypeScript:
* TypeScript код компилируется в JavaScript. Это означает, что для выполнения TypeScript кода требуется процесс компиляции, который преобразует TypeScript в стандартный JavaScript, совместимый с любым браузером или средой выполнения.
JavaScript:
* JavaScript выполняется напрямую в среде выполнения (браузере, Node.js и т.д.) без необходимости компиляции. - Управление версиями
TypeScript:
* TypeScript поддерживает возможность использования типов из различных версий JavaScript и также может использовать более новые возможности ECMAScript, которые могут быть транспилированы в старые версии JavaScript.
JavaScript:
* JavaScript поддерживает различные версии ECMAScript, но функции и возможности зависят от версии языка, поддерживаемой средой выполнения. - Декораторы
TypeScript:
* TypeScript поддерживает декораторы, которые позволяют добавлять метаданные или изменять классы и методы во время выполнения. Это мощный инструмент для создания аннотаций и расширений.
Пример:
function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
console.log(${propertyKey} was called
);
}
class MyClass {
@log
myMethod() {
console.log(‘Hello!’);
}
}
JavaScript:
* Декораторы не поддерживаются в стандартном JavaScript, но существуют экспериментальные возможности и библиотеки, которые реализуют подобный функционал.
- Поддержка и инструменты
TypeScript:
* TypeScript предоставляет улучшенные инструменты разработки, такие как автодополнение, проверка типов и интеграция с IDE (например, Visual Studio Code). Это помогает улучшить качество кода и ускорить разработку.
JavaScript:
* JavaScript имеет широкий спектр инструментов и фреймворков, но возможности для статической проверки типов ограничены по сравнению с TypeScript.
- Что такое Node.js.
Node.js — это среда выполнения JavaScript на стороне сервера, построенная на движке V8 от Google, который используется в браузере Chrome. Node.js позволяет запускать JavaScript вне браузера, что делает его популярным выбором для серверного программирования и разработки веб-приложений. Вот подробное объяснение того, что такое Node.js и какие его ключевые особенности:
Основные характеристики Node.js
1. Асинхронная и событийно-ориентированная архитектура:
o Node.js использует неблокирующую (асинхронную) модель ввода-вывода (I/O) и событийный цикл для обработки запросов и операций. Это позволяет Node.js эффективно справляться с большим количеством одновременных соединений и операций ввода-вывода без блокировки основной программы.
- Один поток и однопоточность:
o Node.js работает в одном потоке, но может обрабатывать множество соединений одновременно благодаря асинхронной обработке. Это отличается от традиционных серверных технологий, которые часто используют многопоточность для обработки параллельных запросов. - Использование движка V8:
o Node.js построен на базе движка V8 от Google, который компилирует JavaScript в машинный код для высокой производительности. Это позволяет Node.js эффективно выполнять JavaScript-код на сервере. - Модульная система:
o Node.js имеет встроенную систему модулей, которая позволяет легко разделять код на небольшие, переиспользуемые модули. Модули могут быть загружены и использованы с помощью функции require. - Управление пакетами через npm:
o Node.js поставляется с пакетным менеджером npm (Node Package Manager), который позволяет легко устанавливать, управлять и обновлять библиотеки и зависимости. npm содержит тысячи пакетов для различных задач, что упрощает разработку. - Однопоточный Event Loop:
o Node.js использует событие loop (event loop) для обработки асинхронных операций. Это позволяет обрабатывать события, такие как запросы HTTP, операции файловой системы и другие асинхронные задачи, без блокировки основной петли обработки событий. - Серверная разработка:
o Node.js позволяет создавать серверные приложения и веб-серверы. Это может быть полезно для создания RESTful API, веб-приложений и микросервисов.
Примеры использования Node.js
Простой HTTP сервер:
const http = require(‘http’);
const hostname = ‘127.0.0.1’;
const port = 3000;
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader(‘Content-Type’, ‘text/plain’);
res.end(‘Hello World\n’);
});
server.listen(port, hostname, () => {
console.log(Server running at http://${hostname}:${port}/
);
});
В этом примере создается простой HTTP сервер, который отвечает текстом “Hello World” на все запросы.
Работа с файловой системой:
const fs = require(‘fs’);
// Асинхронное чтение файла
fs.readFile(‘example.txt’, ‘utf8’, (err, data) => {
if (err) throw err;
console.log(data);
});
Этот код асинхронно читает содержимое файла example.txt и выводит его в консоль.
Преимущества Node.js
1. Высокая производительность:
o Благодаря движку V8 и асинхронной обработке, Node.js обеспечивает высокую производительность и масштабируемость для приложений, работающих с большим количеством одновременных запросов.
2. Упрощение разработки:
o Использование одного языка (JavaScript) для фронтенда и бэкенда упрощает разработку и позволяет использовать общие библиотеки и модули на обеих сторонах.
3. Богатая экосистема:
o npm предоставляет доступ к огромному количеству библиотек и инструментов, что ускоряет разработку и упрощает решение различных задач.
4. Масштабируемость:
o Node.js хорошо подходит для создания масштабируемых приложений, особенно тех, которые требуют одновременной обработки большого числа соединений.
Ограничения Node.js
1. Однопоточная модель:
o Несмотря на асинхронность, Node.js работает в одном потоке. Это может стать узким местом для вычислительно интенсивных задач, таких как сложные вычисления, которые блокируют основной поток.
2. Отладка и ошибки:
o Асинхронное программирование может быть сложным для отладки, особенно когда ошибки возникают в асинхронных операциях или при обработке событий.