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
apply
method
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
true
and 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 0
instead 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
if
andelse
with 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.