Создаём клоны, фабрика
Фундаментальные концепции объектов
Что такое объект?
Объект в JavaScript — это контейнер для хранения данных и функциональности. Представьте его как
коробку, где вы храните связанные вещи вместе.
Простая аналогия: если переменная — это один ящик для хранения, то объект — это целый комод с
несколькими ящиками, каждый со своей меткой.
// Создаем простой объект, представляющий книгу
const book = {
title: "JavaScript для начинающих",
author: "Иван Иванов",
year: 2023,
// Метод (функция внутри объекта)
getInfo: function () {
return `${this.title}, автор: ${this.author}, ${this.year} год`
},
}
console.log(book.getInfo()) // "JavaScript для начинающих, автор: Иван Иванов, 2023 год"
Создание объектов с помощью литерала
Самый простой способ создать объект — использовать литерал объекта {}:
// Создание пустого объекта
const emptyObject = {}
// Создание объекта со свойствами
const person = {
name: "Анна",
age: 25,
city: "Москва",
}
// Каждый новый литерал {} создает уникальный объект в памяти
const person2 = {
name: "Анна",
age: 25,
city: "Москва",
}
console.log(person === person2) // false - это разные объекты!
Контекст вызова this
Ключевое слово this ссылается на объект, в контексте которого выполняется функция.
const user = {
name: "Мария",
greet: function () {
console.log("Привет, меня зовут " + this.name)
},
}
user.greet() // "Привет, меня зовут Мария"
// Проблема: потеря контекста
const greetFunction = user.greet
greetFunction() // "Привет, меня зовут undefined" - this больше не ссылается на user
// Решение: привязка контекста
const boundGreet = user.greet.bind(user)
boundGreet() // "Привет, меня зовут Мария"
Клонирование объектов и проблема дублирования кода
Необходимость клонирования
Часто нужно создавать несколько однотипных объектов:
// Плохой подход - дублирование кода
const slider1 = {
currentImage: 0,
images: ["img1.jpg", "img2.jpg", "img3.jpg"],
next: function () {
this.currentImage = (this.currentImage + 1) % this.images.length
console.log("Показываем: " + this.images[this.currentImage])
},
}
const slider2 = {
currentImage: 0,
images: ["photo1.jpg", "photo2.jpg", "photo3.jpg"],
next: function () {
this.currentImage = (this.currentImage + 1) % this.images.length
console.log("Показываем: " + this.images[this.currentImage])
},
}
Много одинакового кода — сложно поддерживать!
Решение через функцию-конструктор
// Лучший подход - функция для создания однотипных объектов
function createSlider(images) {
return {
currentImage: 0,
images: images,
next: function () {
this.currentImage = (this.currentImage + 1) % this.images.length
console.log("Показываем: " + this.images[this.currentImage])
},
}
}
// Создаем несколько слайдеров без дублирования кода
const slider1 = createSlider(["img1.jpg", "img2.jpg", "img3.jpg"])
const slider2 = createSlider(["photo1.jpg", "photo2.jpg", "photo3.jpg"])
slider1.next() // "Показываем: img2.jpg"
slider2.next() // "Показываем: photo2.jpg"
Инкапсуляция и уникальные идентификаторы
Проблемы с глобальным поиском элементов
Когда на странице несколько компонентов, прямой поиск по ID или классу может вызывать конфликты:
<!-- HTML -->
<div id="slider1">
<img src="img1.jpg" />
<button class="next-btn">Следующий</button>
</div>
<div id="slider2">
<img src="photo1.jpg" />
<button class="next-btn">Следующий</button>
</div>
// Проблемный код
function problemSlider() {
// Этот код найдет первую же кнопку на странице!
const nextButton = document.querySelector(".next-btn")
nextButton.addEventListener("click", function () {
// Обработчик для переключения слайдов
})
}
Решение через ограничение области поиска
// Правильный подход - поиск внутри конкретного контейнера
function createSlider(containerId) {
const container = document.getElementById(containerId)
// Ищем элементы только внутри контейнера
const nextButton = container.querySelector(".next-btn")
const image = container.querySelector("img")
let currentImage = 0
const images = ["img1.jpg", "img2.jpg", "img3.jpg"]
nextButton.addEventListener("click", function () {
currentImage = (currentImage + 1) % images.length
image.src = images[currentImage]
})
return {
// Можем вернуть методы для управления слайдером извне
goNext: function () {
currentImage = (currentImage + 1) % images.length
image.src = images[currentImage]
},
}
}
// Создаем слайдеры для разных контейнеров
const slider1 = createSlider("slider1")
const slider2 = createSlider("slider2")
Рефакторинг и фабричные методы
Вынесение общего кода в функцию
// Улучшенная фабрика слайдеров
function createSlider(containerId, imageList) {
const container = document.getElementById(containerId)
// Создаем элементы слайдера
const image = document.createElement("img")
image.src = imageList[0]
const nextButton = document.createElement("button")
nextButton.textContent = "Следующий"
container.appendChild(image)
container.appendChild(nextButton)
let currentImage = 0
// Добавляем обработчик события
nextButton.addEventListener("click", function () {
currentImage = (currentImage + 1) % imageList.length
image.src = imageList[currentImage]
})
// Возвращаем объект с методами для управления слайдером
return {
goNext: function () {
currentImage = (currentImage + 1) % imageList.length
image.src = imageList[currentImage]
},
goPrev: function () {
currentImage = (currentImage - 1 + imageList.length) % imageList.length
image.src = imageList[currentImage]
},
getCurrentImage: function () {
return currentImage
},
}
}
Создание фабричного объекта
// sliderFactory - фабрика по производству слайдеров
const sliderFactory = {
create: function (containerId, imageList) {
const container = document.getElementById(containerId)
// Создаем элементы интерфейса
const image = document.createElement("img")
image.src = imageList[0]
const nextBtn = document.createElement("button")
nextBtn.textContent = "Вперед"
const prevBtn = document.createElement("button")
prevBtn.textContent = "Назад"
// Добавляем элементы в контейнер
container.appendChild(prevBtn)
container.appendChild(image)
container.appendChild(nextBtn)
let currentImage = 0
// Обработчики событий
nextBtn.addEventListener("click", () => {
currentImage = (currentImage + 1) % imageList.length
image.src = imageList[currentImage]
})
prevBtn.addEventListener("click", () => {
currentImage = (currentImage - 1 + imageList.length) % imageList.length
image.src = imageList[currentImage]
})
// Возвращаем объект слайдера с публичными методами
return {
next: function () {
currentImage = (currentImage + 1) % imageList.length
image.src = imageList[currentImage]
},
prev: function () {
currentImage = (currentImage - 1 + imageList.length) % imageList.length
image.src = imageList[currentImage]
},
getCurrent: function () {
return currentImage
},
setCurrent: function (index) {
if (index >= 0 && index < imageList.length) {
currentImage = index
image.src = imageList[currentImage]
}
},
}
},
}
Сравнение подходов к созданию объектов
| Подход | Преимущества | Недостатки | Когда использовать |
|---|
Литерал объекта {} | Простота, наглядность | Невозможно повторное использование | Для простых одноразовых объектов |
| Функция-конструктор | Устранение дублирования кода | Нет инкапсуляции, все свойства публичные | Для создания множества однотипных объектов |
| Фабричный метод | Полная инкапсуляция, гибкость | Больше кода, сложнее реализация | Для сложных компонентов с внутренним состоянием |
Порядок подключения скриптов и отладка
Правильный порядок подключения
<!DOCTYPE html>
<html>
<head>
<title>Слайдеры</title>
</head>
<body>
<div id="slider1"></div>
<div id="slider2"></div>
<!-- Сначала подключаем файл с фабрикой -->
<script src="slider-factory.js"></script>
<!-- Затем файл, который использует фабрику -->
<script src="main.js"></script>
</body>
</html>
Основы отладки кода
Для отладки используйте инструменты разработчика в браузере (F12):
- Console — просмотр ошибок и вывод
console.log()
- Sources — установка точек останова (breakpoints)
- Debugger — использование ключевого слова
debugger; в коде
function createSlider(containerId, imageList) {
debugger // Выполнение остановится здесь
const container = document.getElementById(containerId)
// остальной код...
}
🏠 Домашнее задание
Задание 1
Создайте фабрику для объекта "Пользователь" со следующими свойствами и методами
const userFactory = {
create: function (name, email) {
// Ваша реализация здесь
},
}
// Пример использования:
const user1 = userFactory.create("Алексей", "alex@example.com")
const user2 = userFactory.create("Мария", "maria@example.com")
console.log(user1.getInfo()) // "Алексей (alex@example.com)"
console.log(user2.getInfo()) // "Мария (maria@example.com)"
Подсказка: объект пользователя должен иметь методы:
getInfo() — возвращает строку с именем и email
setEmail(newEmail) — изменяет email
getName() — возвращает имя
Задание 2
Создайте фабрику для создания объектов "Студент"
const studentFactory = {
create: function (name, grade) {
// Ваш код здесь
return {
// методы...
}
},
}
// Пример использования:
const student1 = studentFactory.create("Аня", 5)
const student2 = studentFactory.create("Петя", 4)
console.log(student1.getInfo()) // "Аня, 5 класс"
console.log(student2.getInfo()) // "Петя, 4 класс"
Подсказка: объект студента должен иметь методы:
getInfo() — возвращает строку с именем и классом
upgrade() — увеличивает класс на 1
changeName(newName) — изменяет имя
Заключение
Объекты — фундаментальная часть JavaScript. Понимание принципов их создания, клонирования и
организации через фабричные методы поможет вам писать более чистый, поддерживаемый и масштабируемый
код.
Помните главные принципы:
- Избегайте дублирования кода
- Инкапсулируйте логику внутри объектов
- Используйте фабрики для создания однотипных объектов
- Всегда проверяйте порядок подключения скриптов
Проверь себя
❓
Вопрос 1: Почему в JavaScript так важен контекст ключевого слова this?
Ответ: this ссылается на объект, в контексте которого выполняется функция. Если this
неправильно привязан, метод может пытаться получить доступ к свойствам несуществующего или
неправильного объекта, что приведет к ошибкам.
❓
Вопрос 2: Объясните, почему дублирование кода является "грехом номер один" в программировании.
Ответ: Дублирование кода создает проблемы с поддержкой и масштабированием.
При необходимости внести изменения, их придется дублировать во всех местах, где есть
одинаковый код, что увеличивает вероятность ошибок и затраты времени.
❓
Вопрос 3: Что такое "фабричный метод" в контексте JavaScript-объектов?
Ответ: Фабричный метод — это функция или метод объекта, который отвечает за
создание и возврат новых объектов. Он инкапсулирует логику создания, позволяя генерировать
однотипные, но независимые экземпляры.