Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Как ООП используется во Frontend?
Объектно-ориентированное программирование (ООП) — это не просто парадигма, это способ организовать код так, чтобы он был переиспользуемым, тестируемым и поддерживаемым. На фронтенде ООП используется повсеместно, хотя это может быть не всегда очевидно.
ООП в JavaScript / TypeScript
Классы — основа ООП:
class User {
constructor(name, email) {
this.name = name;
this.email = email;
}
greet() {
return `Hello, I'm ${this.name}`;
}
changeEmail(newEmail) {
this.email = newEmail;
}
}
const user = new User('John', 'john@example.com');
console.log(user.greet()); // Hello, I'm John
Инкапсуляция — скрытие деталей реализации:
class BankAccount {
#balance = 0; // Private field
constructor(initialBalance) {
this.#balance = initialBalance;
}
deposit(amount) {
if (amount > 0) {
this.#balance += amount;
return this.#balance;
}
throw new Error('Amount must be positive');
}
getBalance() {
return this.#balance;
}
}
const account = new BankAccount(1000);
account.deposit(500); // OK
console.log(account.#balance); // Error: private field
Наследование — переиспользование кода:
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a sound`);
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name);
this.breed = breed;
}
speak() {
console.log(`${this.name} barks`);
}
getInfo() {
return `${this.name} is a ${this.breed}`;
}
}
const dog = new Dog('Rex', 'Labrador');
dog.speak(); // Rex barks
console.log(dog.getInfo()); // Rex is a Labrador
Полиморфизм — один интерфейс, разные реализации:
class Shape {
getArea() {
throw new Error('Must implement getArea()');
}
}
class Circle extends Shape {
constructor(radius) {
super();
this.radius = radius;
}
getArea() {
return Math.PI * this.radius ** 2;
}
}
class Rectangle extends Shape {
constructor(width, height) {
super();
this.width = width;
this.height = height;
}
getArea() {
return this.width * this.height;
}
}
const shapes = [
new Circle(5),
new Rectangle(4, 6)
];
shapes.forEach(shape => {
console.log(shape.getArea());
});
ООП в React компонентах
Классовые компоненты (Legacy, но все ещё используются):
class UserProfile extends React.Component {
constructor(props) {
super(props);
this.state = {
user: null,
loading: true
};
}
componentDidMount() {
this.fetchUser();
}
fetchUser = async () => {
try {
const response = await fetch(`/api/users/${this.props.userId}`);
const user = await response.json();
this.setState({ user, loading: false });
} catch (error) {
console.error(error);
this.setState({ loading: false });
}
}
render() {
const { user, loading } = this.state;
if (loading) return <div>Loading...</div>;
if (!user) return <div>User not found</div>;
return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
);
}
}
Функциональные компоненты с хуками (современный подход):
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchUser = async () => {
try {
const response = await fetch(`/api/users/${userId}`);
const data = await response.json();
setUser(data);
} finally {
setLoading(false);
}
};
fetchUser();
}, [userId]);
if (loading) return <div>Loading...</div>;
if (!user) return <div>User not found</div>;
return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
);
}
ООП паттерны на фронтенде
Паттерн Singleton — один экземпляр:
class Logger {
static instance = null;
static getInstance() {
if (!Logger.instance) {
Logger.instance = new Logger();
}
return Logger.instance;
}
log(message) {
console.log(`[${new Date().toISOString()}] ${message}`);
}
}
const logger1 = Logger.getInstance();
const logger2 = Logger.getInstance();
console.log(logger1 === logger2); // true — один и тот же объект
Паттерн Factory — создание объектов:
class Button {
constructor(text, onClick) {
this.text = text;
this.onClick = onClick;
}
render() {
return `<button>${this.text}</button>`;
}
}
class ButtonFactory {
static createPrimaryButton(text, onClick) {
return new Button(text, onClick);
}
static createSecondaryButton(text, onClick) {
return new Button(text, onClick);
}
}
const submitBtn = ButtonFactory.createPrimaryButton('Submit', () => {});
Паттерн Observer — реактивность:
class EventEmitter {
constructor() {
this.events = {};
}
on(event, callback) {
if (!this.events[event]) {
this.events[event] = [];
}
this.events[event].push(callback);
}
emit(event, data) {
if (this.events[event]) {
this.events[event].forEach(callback => callback(data));
}
}
}
const emitter = new EventEmitter();
emitter.on('userLoggedIn', (user) => {
console.log('User logged in:', user);
});
emitter.emit('userLoggedIn', { name: 'John' });
Service Classes для бизнес-логики
class UserService {
constructor(apiClient) {
this.apiClient = apiClient;
}
async getUserById(id) {
const user = await this.apiClient.get(`/users/${id}`);
return this.mapToDomain(user);
}
async createUser(userData) {
const validated = this.validateUserData(userData);
return await this.apiClient.post('/users', validated);
}
async updateUser(id, userData) {
const validated = this.validateUserData(userData);
return await this.apiClient.put(`/users/${id}`, validated);
}
validateUserData(data) {
if (!data.email || !data.email.includes('@')) {
throw new Error('Invalid email');
}
if (!data.name || data.name.length < 2) {
throw new Error('Invalid name');
}
return data;
}
mapToDomain(apiResponse) {
return {
id: apiResponse.id,
name: apiResponse.full_name,
email: apiResponse.email_address
};
}
}
// Использование
const userService = new UserService(apiClient);
const user = await userService.getUserById(123);
TypeScript для ООП
interface IUser {
id: number;
name: string;
email: string;
}
class UserRepository {
async findById(id: number): Promise<IUser | null> {
// Реализация
return null;
}
async save(user: IUser): Promise<void> {
// Реализация
}
}
class UserController {
constructor(private userRepository: UserRepository) {}
async getUser(id: number): Promise<IUser | null> {
return this.userRepository.findById(id);
}
async createUser(userData: Partial<IUser>): Promise<IUser> {
const user: IUser = {
id: Date.now(),
name: userData.name || '',
email: userData.email || ''
};
await this.userRepository.save(user);
return user;
}
}
Принципы SOLID во Frontend
S — Single Responsibility
// Плохо: класс делает слишком много
class UserManager {
fetchUser() { /* ... */ }
validateEmail() { /* ... */ }
sendEmail() { /* ... */ }
renderUI() { /* ... */ }
}
// Хорошо: разделение ответственности
class UserService { fetchUser() { /* ... */ } }
class EmailValidator { validateEmail() { /* ... */ } }
class EmailSender { sendEmail() { /* ... */ } }
class UserComponent { renderUI() { /* ... */ } }
D — Dependency Injection
class UserComponent {
constructor(
private userService: UserService,
private logger: Logger
) {}
async loadUser(id: number) {
this.logger.log('Loading user');
const user = await this.userService.getUser(id);
return user;
}
}
// Внедрение зависимостей
const userService = new UserService();
const logger = new Logger();
const component = new UserComponent(userService, logger);
Абстрактные классы
abstract class APIClient {
abstract request(endpoint: string, options?: any): Promise<any>;
async get(endpoint: string) {
return this.request(endpoint, { method: 'GET' });
}
async post(endpoint: string, data: any) {
return this.request(endpoint, {
method: 'POST',
body: JSON.stringify(data)
});
}
}
class FetchAPIClient extends APIClient {
async request(endpoint: string, options: any = {}) {
const response = await fetch(endpoint, options);
return response.json();
}
}
Заключение
ООП на фронтенде используется для:
- Организации кода — классы и модули
- Переиспользования — наследование и композиция
- Тестируемости — внедрение зависимостей
- Масштабируемости — SOLID принципы
- Типизации — TypeScript interfaces и классы
Модерный фронтенд сочетает ООП с функциональным программированием (React хуки, FP функции), создавая мощный и гибкий код.