JavaScript · Синтаксис · Промежуточный
Замыкания
Функции, которые помнят свою внешнюю область видимости. Охватывает механику замыканий и практические паттерны — счётчики и фабрики.
Краткое введение в тему и пояснения перед упражнениями (упражнения ниже):
Функции-фабрики, ошибка var в цикле и паттерн модуля
#Паттерны замыканий: мемоизация, once, partial и ловушки памяти
#Упражнения:
Фабрика счётчиков
#Напишите функцию makeCounter(), которая возвращает новый объект-счётчик. Каждый вызов makeCounter() должен создавать независимый счётчик с методами increment() и getValue().
function makeCounter() {
}
const counter = makeCounter();
counter.increment();
counter.increment();
counter.increment();
console.log(counter.getValue());
Решение
function makeCounter() {
let count = 0;
return {
increment() { count++; },
getValue() { return count; },
};
}
const counter = makeCounter();
counter.increment();
counter.increment();
counter.increment();
console.log(counter.getValue());
Фабрика множителей
#Напишите функцию makeMultiplier(factor), которая возвращает новую функцию. Эта возвращённая функция принимает число и умножает его на исходный factor.
function makeMultiplier(factor) {
}
const triple = makeMultiplier(3);
const double = makeMultiplier(2);
console.log(triple(5));
console.log(double(7));
Решение
function makeMultiplier(factor) {
return function(n) {
return n * factor;
};
}
const triple = makeMultiplier(3);
const double = makeMultiplier(2);
console.log(triple(5));
console.log(double(7));
Одноразовая функция
#Напишите функцию once(fn), которая принимает функцию и возвращает новую функцию, которую можно вызвать только один раз. При первом вызове она выполняет fn и возвращает результат; последующие вызовы возвращают undefined.
function once(fn) {
}
const init = once(() => {
console.log("initialized!");
return 42;
});
console.log(init());
console.log(init());
console.log(init());
Решение
function once(fn) {
let called = false;
return function(...args) {
if (!called) {
called = true;
return fn(...args);
}
};
}
const init = once(() => {
console.log("initialized!");
return 42;
});
console.log(init());
console.log(init());
console.log(init());
Фабрика сумматоров
#Напишите функцию makeAdder(x), которая возвращает функцию. Возвращённая функция принимает число y и возвращает x + y.
function makeAdder(x) {
}
const add5 = makeAdder(5);
const add10 = makeAdder(10);
console.log(add5(3));
console.log(add10(3));
Решение
function makeAdder(x) {
return y => x + y;
}
const add5 = makeAdder(5);
const add10 = makeAdder(10);
console.log(add5(3));
console.log(add10(3));
Мемоизация
#Напишите функцию memoize(fn), которая оборачивает функцию и кэширует её результаты. Если функцию снова вызывают с тем же аргументом, верните кэшированный результат вместо повторного вызова fn.
function memoize(fn) {
}
const slowSquare = memoize(n => {
console.log(`computing ${n}...`);
return n * n;
});
console.log(slowSquare(4));
console.log(slowSquare(4));
console.log(slowSquare(5));
Решение
function memoize(fn) {
const cache = {};
return function(arg) {
if (arg in cache) return cache[arg];
cache[arg] = fn(arg);
return cache[arg];
};
}
const slowSquare = memoize(n => {
console.log(`computing ${n}...`);
return n * n;
});
console.log(slowSquare(4));
console.log(slowSquare(4));
console.log(slowSquare(5));
Приватная переменная
#Напишите функцию makeBankAccount(initialBalance), которая возвращает объект с методами deposit(amount), withdraw(amount) и getBalance(). Баланс не должен быть доступен снаружи напрямую.
function makeBankAccount(initialBalance) {
}
const account = makeBankAccount(100);
account.deposit(50);
account.withdraw(30);
console.log(account.getBalance());
Решение
function makeBankAccount(initialBalance) {
let balance = initialBalance;
return {
deposit(amount) { balance += amount; },
withdraw(amount) { balance -= amount; },
getBalance() { return balance; },
};
}
const account = makeBankAccount(100);
account.deposit(50);
account.withdraw(30);
console.log(account.getBalance());
Отложенный лог
#Напишите функцию delayedLog(message, seconds), которая выводит сообщение в консоль через заданное количество секунд. Используйте setTimeout и замыкание.
function delayedLog(message, seconds) {
}
delayedLog("hello", 1);
delayedLog("world", 2);
Решение
function delayedLog(message, seconds) {
setTimeout(() => {
console.log(message);
}, seconds * 1000);
}
delayedLog("hello", 1);
delayedLog("world", 2);
Фабрика ограничений
#Напишите функцию makeClamp(min, max), которая возвращает функцию. Возвращённая функция принимает число и ограничивает его между min и max включительно.
function makeClamp(min, max) {
}
const clamp0to100 = makeClamp(0, 100);
console.log(clamp0to100(-5));
console.log(clamp0to100(50));
console.log(clamp0to100(150));
Решение
function makeClamp(min, max) {
return n => Math.min(Math.max(n, min), max);
}
const clamp0to100 = makeClamp(0, 100);
console.log(clamp0to100(-5));
console.log(clamp0to100(50));
console.log(clamp0to100(150));
Обёртка со счётчиком вызовов
#Напишите функцию withCallCount(fn), которая оборачивает функцию и возвращает объект с методом call и методом getCount(). call() вызывает исходную функцию, а getCount() возвращает количество вызовов.
function withCallCount(fn) {
}
const wrapped = withCallCount(x => x * 2);
wrapped.call(5);
wrapped.call(10);
wrapped.call(3);
console.log(wrapped.getCount());
Решение
function withCallCount(fn) {
let count = 0;
return {
call(...args) {
count++;
return fn(...args);
},
getCount() { return count; },
};
}
const wrapped = withCallCount(x => x * 2);
wrapped.call(5);
wrapped.call(10);
wrapped.call(3);
console.log(wrapped.getCount());
Частичное применение
#Напишите функцию partial(fn, ...preArgs), которая принимает функцию и некоторые аргументы и возвращает новую функцию, которая при вызове подставляет эти аргументы первыми в fn.
function partial(fn, ...preArgs) {
}
function add(a, b, c) {
return a + b + c;
}
const add10 = partial(add, 10);
console.log(add10(5, 3));
console.log(add10(1, 2));
Решение
function partial(fn, ...preArgs) {
return function(...laterArgs) {
return fn(...preArgs, ...laterArgs);
};
}
function add(a, b, c) {
return a + b + c;
}
const add10 = partial(add, 10);
console.log(add10(5, 3));
console.log(add10(1, 2));