Appearance
Functions
Since Underscore is designed to leverage JavaScript's functional programming capabilities, it provides many higher-order functions that JavaScript itself does not have.
bind
What is bind()
useful for? Let's first look at a common mistake:
javascript
let s = ' Hello ';
s.trim();
// Output: 'Hello'
let fn = s.trim;
fn();
// Uncaught TypeError: String.prototype.trim called on null or undefined
If you want to replace s.trim()
with fn()
, the approach above won't work because directly calling fn()
results in this
being undefined
. You must do this instead:
javascript
let s = ' Hello ';
let fn = s.trim;
// Call with `call`, passing `s` as `this`:
fn.call(s);
// Output: 'Hello'
This is cumbersome! It's easier to just use s.trim()
. However, bind()
can help us bind the s
object directly to the this
pointer of fn()
, so that subsequent calls to fn()
work correctly:
javascript
let s = ' Hello ';
let fn = _.bind(s.trim, s);
fn();
// Output: 'Hello'
Conclusion: When a variable fn
points to a method of an object, calling fn()
directly won't work due to the loss of the this
reference. Using bind
can fix this issue.
partial
partial()
creates a partial function for a given function. What is a partial function? Let’s see an example:
Suppose we want to calculate ( x^y ) using Math.pow(x, y)
. If we frequently calculate ( 2^y ), writing Math.pow(2, y)
each time can be cumbersome. Instead, we can create a new function called pow2N(y)
that is a partial function with the first parameter fixed to 2:
javascript
let pow2N = _.partial(Math.pow, 2);
pow2N(3); // 8
pow2N(5); // 32
pow2N(10); // 1024
If we want to fix the second parameter instead, for example, to create a partial function cube(x)
that calculates ( x^3 ), we can use _
as a placeholder:
javascript
let cube = _.partial(Math.pow, _, 3);
cube(3); // 27
cube(5); // 125
cube(10); // 1000
The purpose of creating a partial function is to fix certain parameters of the original function, making it easier to call.
memoize
If a function is costly to call, we might want to cache the results for faster subsequent calls. For example, calculating the factorial is time-consuming:
javascript
function factorial(n) {
console.log('start calculate ' + n + '!...');
let s = 1, i = n;
while (i > 1) {
s = s * i;
i--;
}
console.log(n + '! = ' + s);
return s;
}
factorial(10); // 3628800
// Console output:
// start calculate 10!...
// 10! = 3628800
Using memoize()
allows us to automatically cache the results of function calculations:
javascript
let factorial = _.memoize(function(n) {
console.log('start calculate ' + n + '!...');
let s = 1, i = n;
while (i > 1) {
s = s * i;
i--;
}
console.log(n + '! = ' + s);
return s;
});
// First call:
factorial(10); // 3628800
// Console output:
// start calculate 10!...
// 10! = 3628800
// Second call:
factorial(10); // 3628800
// No console output
For repeated calls with the same argument, like factorial(10)
, the second call doesn’t compute again; it returns the cached result. However, calling factorial(9)
will still compute anew.
We can improve factorial()
to allow recursive calls:
javascript
let factorial = _.memoize(function(n) {
console.log('start calculate ' + n + '!...');
if (n < 2) {
return 1;
}
return n * factorial(n - 1);
});
factorial(10); // 3628800
// Output indicates that factorial(1) through factorial(10) have all been cached:
// start calculate 10!...
// start calculate 9!...
// start calculate 8!...
// start calculate 7!...
// start calculate 6!...
// start calculate 5!...
// start calculate 4!...
// start calculate 3!...
// start calculate 2!...
// start calculate 1!...
factorial(9); // 362880
// No console output
once
As the name suggests, once()
ensures that a function is executed only once. If you have a method called register()
that can be triggered by clicking either of two buttons, you can use once()
to guarantee that the function is called only once, no matter how many times the user clicks:
javascript
let register = _.once(function () {
console.log('Register ok!');
});
// Test the effect:
register();
register();
register();
delay
delay()
allows a function to execute after a delay, similar to setTimeout()
, but with simpler syntax:
javascript
// Call alert() after 2 seconds:
_.delay(alert, 2000);
If the delayed function has parameters, you can pass them in as well:
javascript
let log = _.bind(console.log, console);
_.delay(log, 2000, 'Hello,', 'world!');
// Prints 'Hello, world!' after 2 seconds.
For more complete functions, please refer to the Underscore documentation: https://underscorejs.org/#functions