In usual programming languages, the parameters of a function can only be basic types or object references, and the return value can only be basic data types or object references. But in Javascript, functions are first-class citizens and can be passed as parameters or returned as return values. The so-called higher-order function is a function that can take a function as a parameter or a function as a return value. These two situations have many application scenarios in actual development. This article is a summary of several application scenarios I encountered in work and study.
Callback function
Code reuse is one of the important criteria for measuring an application. By extracting the changed business logic and encapsulating it in the callback function, the code reuse rate can be effectively improved. For example, the forEach method added to arrays in ES5 traverses the array and calls the same function on each element.
array = {}; array.forEach = function(arr, fn){ for (var i = 0, len = arr.length; i < len; i++) { fn(arr[i], i, arr); } }
Focus the business focus on the callback function without having to write the traversal code again every time.
Partial function
As a typical application of outputting a function as a return value, it is a partial function. The so-called partial function refers to the usage of creating a function that calls another part-a function whose parameters or variables have been preset. Anyway, looking at the definition, I don’t understand what this stuff is for. Let’s look at examples first. The most typical example of partial functions is type judgment.
Javascript objects have three attributes: prototype attributes, class attributes, and extensibility. (Students who don’t know should go back and read the Rhino book, page: 138) The class attribute is a string, which is not directly provided in Javascript, but we can use Object.prototype.toString to obtain it indirectly. This function always returns the following form:
[object Class]
So we can write a series of isType functions.
The code is as follows:
isString = function(obj){ return Object.prototype.toString.call(obj) === "[object String]"; } isNumber = function(obj){ return Object.prototype.toString.call(obj) === "[object Number]"; } isArray = function(obj){ return Object.prototype.toString.call(obj) === "[object Array]"; }
Most of the codes in these functions are repeated. At this time, high-order functions make a gorgeous debut:
isType = function(type) { return function(obj) { return Object.prototype.toString.call(obj) === "[object " + type + "]"; } } isString = isType('String'); isNumber = isType('Number'); isArray = isType('Array');
So the form of returning a new customized function by specifying some parameters is a partial function.
Currying
Currying is also called partial evaluation. A currying function will first accept some parameters. After accepting these parameters, the function will not evaluate immediately, but will continue to return another function. The parameters just passed in are saved in the closure formed by the function. When the function is actually evaluated, all parameters passed in will be used for evaluation at once.
var currying = function(fn) { var args = []; return function() { if (arguments.length === 0) { return fn.applay(this, args); } else { args = args.concat(arguments); return arguments.callee; } } }
Suppose we take the calculation of daily expenses for a month as an example:
var currying = function(fn) { debugger; var args = []; return function() { if (arguments.length === 0) { return fn.apply(this, args); } else { Array.prototype.push.apply(args, arguments); return arguments.callee; } } } cost = function(){ var sum = 0; for (var i = 0, len = arguments.length; i < len; i++) { sum += arguments[i]; } return sum; } var cost = currying(cost); cost(100); cost(200); alert(cost())
Event throttling
In some scenarios, certain events may be triggered repeatedly, but the event processing function does not need to be executed every time. For example, complex logical calculations are performed in the window.resize event. If the user frequently changes the browser size, complex calculations will have a serious impact on performance; sometimes these logical calculations do not need to be triggered every time rezise occurs, and only limited calculations are required. A few times will do. At this time we need to ignore some event requests based on the time period. Take a look at the following throttling function:
function throttle(fn, interval) { var doing = false; return function() { if (doing) { return; } doing = true; fn.apply(this, arguments); setTimeout(function() { doing = false; }, interval); } } window.onresize = throttle(function(){ console.log('execute'); }, 500);
By controlling the function execution time, you can achieve a perfect balance between the number of function executions and functional requirements. Another event is mousemove. If we bind this event to a DOM element, the event will be triggered repeatedly when the mouse moves over the element.
The event is over
For some events that can be triggered frequently, sometimes we want to perform a series of operations after the event ends. At this time we can use higher-order functions to do the following processing:
function debounce(fn, interval) { var timer = null; function delay() { var target = this; var args = arguments; return setTimeout(function(){ fn.apply(target, args); }, interval); } return function() { if (timer) { clearTimeout(timer); } timer = delay.apply(this, arguments); } }; window.onresize = throttle(function(){ console.log('resize end'); }, 500);
If the event is triggered during this process, clear the last event handle and rebind the execution time.
Reference:
《In-depth explanation of node》
"Javascript Design Patterns and Development Practices"