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));