Problem
You want to create a generic function that converts a function to a function that can only be called once.
Ingredients
- a closure
- the
applymethod
Directions
-
Given: a function that executes only once (based on yesterday’s recipe).
function printSomething() { let called = false; if(called) { return undefined; } else { called = true; console.log('something'); printSomething = function() {}; } } printSomething(); // (1) Ok printSomething(); // (2) Does nothing -
Create a function
once()that accepts a function as parameter and returns another function.function once(fn) { return function() { ... } } -
Add a variable to memorize if the function has been called.
function once(fn) { let called = false; return function() { ... } } -
Inside the returned function check that variable. If it is
true, then returnundefined…function once(fn) { let called = false; return function() { if(called) { return undefined; } ... } } -
… otherwise set the variable to
trueand call the passed function.function once(fn) { let called = false; return function() { if(called) { return undefined; } else { called = true; fn.apply(this, arguments); } } } -
Optimization: use
void 0instead ofreturn undefined, which are less characters.function once(fn) { let called = false; return function() { if(called) { void 0; } else { called = true; fn.apply(this, arguments); } } } -
Optimization: replace
ifandelsewith the ternary operator.function once(fn) { let called = false; return function() { return called ? void 0 : ((called = true), fn.apply(this, arguments)); } } -
Optimization: use a fat arrow function for
once().const once = (fn) => { let called = false; return function() { return called ? void 0 : ((called = true), fn.apply(this, arguments)); } } -
Now remove all the boilerplate logic from
printSomething()and useonce()to create another functionprintSomethingOnce().function printSomething() { console.log('something'); } let printSomethingOnce = once(printSomething); - Voilá, now if
printSomethingOnce()is called a second time, it simply does nothing.
```javascript
function printSomething() {
console.log('something');
}
let printSomethingOnce = once(printSomething);
printSomethingOnce(); // (1) Ok
printSomethingOnce(); // (2) Does nothing
```Alternative recipes
- Use a self-defining function.