Ответ
В JavaScript я использую замыкания для создания приватного состояния и методов, что является основой модульного паттерна. Вот как я это реализую на практике:
Базовый пример с приватными переменными:
function createBankAccount(initialBalance) {
// Приватные переменные, доступные только через замыкание
let balance = initialBalance;
let transactionHistory = [];
// Публичные методы, возвращаемые наружу
return {
deposit: function(amount) {
if (amount > 0) {
balance += amount;
transactionHistory.push({
type: 'deposit',
amount,
timestamp: new Date()
});
return balance;
}
throw new Error('Deposit amount must be positive');
},
withdraw: function(amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
transactionHistory.push({
type: 'withdraw',
amount,
timestamp: new Date()
});
return balance;
}
throw new Error('Invalid withdrawal amount');
},
getBalance: function() {
return balance;
},
getRecentTransactions: function(count = 5) {
return transactionHistory.slice(-count);
}
};
}
// Использование
const myAccount = createBankAccount(1000);
console.log(myAccount.getBalance()); // 1000
myAccount.deposit(500);
console.log(myAccount.getBalance()); // 1500
// Попытка доступа к приватным полям напрямую
console.log(myAccount.balance); // undefined
console.log(myAccount.transactionHistory); // undefined
Более сложный пример с фабрикой объектов:
function createUserManager() {
// Приватный кэш пользователей
const usersCache = new Map();
// Приватный метод валидации
function validateUserData(userData) {
const requiredFields = ['id', 'name', 'email'];
return requiredFields.every(field => userData[field]);
}
// Публичный API
return {
addUser: function(userData) {
if (!validateUserData(userData)) {
throw new Error('Invalid user data');
}
usersCache.set(userData.id, {
...userData,
createdAt: new Date(),
updatedAt: new Date()
});
return userData.id;
},
getUser: function(userId) {
const user = usersCache.get(userId);
if (!user) {
throw new Error(`User ${userId} not found`);
}
return { ...user }; // Возвращаем копию для защиты от изменений
},
updateUser: function(userId, updates) {
const user = usersCache.get(userId);
if (!user) {
throw new Error(`User ${userId} not found`);
}
usersCache.set(userId, {
...user,
...updates,
updatedAt: new Date()
});
},
getStats: function() {
return {
totalUsers: usersCache.size,
cacheSize: Buffer.byteLength(JSON.stringify([...usersCache.values()]))
};
}
};
}
// Использование
const userManager = createUserManager();
const userId = userManager.addUser({
id: 'user-123',
name: 'John Doe',
email: 'john@example.com'
});
console.log(userManager.getUser(userId));
console.log(userManager.getStats());
Преимущества такого подхода:
- Полная инкапсуляция: Приватные переменные недоступны извне
- Контроль доступа: Можно реализовать сложную логику валидации
- Состояние сохраняется: Объекты "помнят" своё состояние между вызовами
- Гибкость: Можно создавать множество независимых экземпляров
В современном Node.js я также использую классы с приватными полями (синтаксис #), но замыкания остаются полезными для более сложных сценариев инкапсуляции.