The following high-level JS programming techniques are advanced applications of closures. The article is a bit long, please be patient to eat ~

Lazy function idea

Let’s take an example to prove the idea of an inert function in JS.

Take the click response event as an example.

  • DOM 0-level event binding is as follows:Onclick = function(){... }
  • DOM level 2 event bindings are (mostly) :Elements. The addEventListener (" click ", function () {... }), but this mode is not supported by Internet Explorer 6 to 8. This mode is used in earlier versions of browsers:Elements. AttachEvent (" onclick ", function () {... })

We want dom2-based event binding and compatible with all browsers. A natural need for judgment:

/* observerEvent: dom2-based event binding * @params: * Element: the element that needs to bind the event *type: Binding event type * func: event response function * @return: undefined
*/
function observerEvent(element,type,func){
	if(element.addEventListener){ 
		element.addEventListener(type, func); // If you have this, use this}else if(element.attachEvent){
		element.attachEvent("on" + type, func); // If you don't have this, you can use it.else{
		element["on" + type] = func; // use DOM0 event binding method}}Copy the code

This method works, but if you do it multiple times, each time checking for browser compatibility, it’s a bit of a hassle. We want to make a judgment the first time we execute the method and not repeat it.

function observerEvent(element,type,func){
	if(element.addEventListener){ 
		observerEvent = function(element,type,func){
			element.addEventListener(type, func); }}else if(element.attachEvent){
		observerEvent = function(element,type,func){
			element.attachEvent("on" + type, func); }}else{
		observerEvent = function(element,type,func){
			element["on" + type] = func; 
		}
	}
	observerEvent(element,type,func);
}
Copy the code

There are two main changes to the above code:

  • The first time the function executes, regardless of which judgment it enters, the private context formed by the large function generates a new function heap and assigns the address of the heap to the global observerEvent variable, forming an undestroyed context and thus forming the closure. The observerEvent function has been refactored, and the newly pointed function does not require compatibility tests.
  • addedobserverEvent(element,type,func)This code. Because function refactoring creates a new function heap and changes the function orientation the first time the function is executed, it does not execute the function, so you need to add a line of code to manually execute the refactored method to achieve event binding.

Singleton design pattern

Singleton design pattern is the embodiment of the earliest modular development thought. For example, if two people collaborate on A project, A is responsible for the function development of Module1 and B is responsible for the function development of Module2. To prevent variable collisions, they put their code in a closure where all variables are private:

(function module1{
	let index = 0;
	function queryData() {}; }) () (function module2{
	let index = 0;
	function queryData() {}; }) ()Copy the code

One day, A encapsulates A very useful getElement method, and B wants to “copy” it for its own use (we’re not talking about CTRL + C/CTRL + V emMM here), but normally B cannot call A method in A closure. There are two ways to solve this problem:

  • B said to A, you can expose the method I want to use to the whole world, so THAT I can use it. I have to say that if you want to expose too many methods globally, you can also cause variable conflicts, just like writing directly to the global.
(function module1{
	let index = 0;
	function queryData() {};function getElement() {}; window.getElement = getElement; }) () (function module2{
	let index = 0;
	function queryData() {}; window.getElement(); }) ()Copy the code
  • Expose methods by returning an object.
let moduleA = (function module1{
	let index = 0;
	function queryData() {};function getElement() {};return{getElement: getElement}})() {getElement: getElement}}let moduleB = (function module2{
	let index = 0;
	function queryData() {}; moduleA.getElement(); // Module 2 calls the methods exposed in module 1 in this way})()Copy the code

In this way, only namespace names such as moduleA and moduleB are exposed globally. The attributes & methods describing the same transaction (same module) are grouped into the same namespace to achieve group management, which will not pollute the global variable space and cause variable conflicts, but also expose some of their own private methods for other modules to call.

In addition, we will add an init property to the return object, whose value is a function. On the basis of singleton pattern design, a command pattern is added. Init as the entry to the current module’s business:

let moduleA = (function module1{
	let index = 0;
	function queryData() {};function getElement() {};function bindHTML() {};function handleEvent() {};return{
		init(){
			getElement();
			bindHTML();
			handleEvent();
		}
	}
})()
moduleA.init();
Copy the code

When calling this module method, only need to execute the init method, we in init method, according to the business needs, write the method in order to call the execution.

The idea of a Coriolization function

Introduction: This is a very common idea ~ its core concept — “pre-storage & processing”; In its common form, a large function passes in some arguments and returns a small function that processes the arguments passed in.

Here’s a question to explain:

Write a function fn that does the following:letRes = fn (1, 2) (3); console.log(res); = > 6 (1 + 2 + 3)Copy the code

Fn (1,2) returns a function with a parameter 3. Fn (1,2) returns a function with a parameter. Let’s assume for a moment that the parameters are a fixed number:

function fn(x,y){
	return function(z){
		returnx+y+z; }}letRes = fn (1, 2) (3); console.log(res);Copy the code

As you can see, the first time, when performing a function fn form private context to create a function of the pile is quoted by the global variable res, therefore this private context cannot be destroyed, its private variables x, y and have survived, when performing the function, the scope chain to find the superior in the context of x, y, and for subsequent processing. This is the embodiment of the Idea of The Coriolization function.

Function.prototype.bind prehandles this redux, react-redux, react-redux

Compose function – Flattening function calls

const add1 = (x) => x+1;
const mul3 = (x) => x*3;
const div2 = (x) => x/2;
let result = div2(mul3(add1(add1(0))));
console.log(result);
Copy the code

By nested calls to the function, we get a final result of 3. Compose (div2,mul3,add1,add1)(0) is composed (div2,mul3,add1,add1)(0) for the same function.

Scenario: The execution result of one function is passed as an argument to the next function. Premise: These functions take only one argument. F (g (h (x))) — — — — — – > compose (f, g, h) (x)/compose (h, g, f (x)), the order of function arguments can be specified. Now, what about the compose function?

First let’s look at the use of the array iteration method reduce:

Array reduce method

letArr =,20,30,40 [10]; arr.reduce((N,item)=>{ console.log(N,item); }) = = = = = = = = = = = = = = = = = = = = = = = output results as follows: 10 20 undefined undefined 30 and 40Copy the code
  • The reduce method can pass two parameters:
    • The first argument is a function (required) : the callback function that executes when the array iterates through each item. The function takes two parameters N,item. Where item is the current item of the array iteration.
    • The second argument is a basic type value (selected)
  • When only one argument is passed (as in the above example), the initial value of N is the first item of arr, and item is traversed from the second item. The subsequent value of N is the result of the previous function (return value). Since the function in the above example returns no value, the value of N is undefined.
  • When two parameters are passed, the initial value of N is the value of the second parameter, and item is traversed from the first item. The subsequent value of N is the result of the previous function (return value).
  • Conclusion:
    • N is the result returned by the previous function, but its initial value depends on whether a second argument is passed.
    • Item is the current item when the array is iterated. The beginning of the iteration depends on whether the second argument is passed.
    • The original array remains unchanged, and the result is returned as a return value.

The compose function is composed for compose. The compose function is composed for compose.

functioncompose(... funcs){return functionAnonymous (val){// Since it can be executed continuously, the return value is a functionif(funcs.length === 0) returnval; // If the function parameter list is empty, the value passed in is returnedif(funcs.length === 1) returnfuncs[0](val); // If there is only one function, execute itreturnFuncs.reverse ().reduce((N,item)=>{ Parsing is belowreturn typeof N==="function"? item(N(val)) : item(N); }}})Copy the code

The reduce method is used to traverse the funcs parameter array when there are more than two functions in the function list.

  • Whether the funcs array needs to be reversed depends on the order in which the function parameters are executed after flattening the array. In this case, compose(div2,mul3,add1,add1)(0) is executed from back to front, so it needs to be reversed.

  • In the above code, reduce does not pass the second parameter. Let’s summarize the results of each iteration:

    N item operation
    add1 add1 Add1 (add1(0)) => next round N ==item(N(val))==
    N mul3 Mul3 (add1(add1(0))) => next round N ==item(N)==
    N div2 Div2 (mul3(add1(add1(0)))) => next round N ==item(N)==
    N ==item(N)==

    Hence this code:

    returnFuncs.reverse ().reduce((N,item)=>{ Parsing is belowreturn typeof N==="function" ? item(N(val)) : item(N);
    })
    Copy the code

    There’s actually an easier way to write it:

    return funcs.reverse().reduce((N,item)=>{  
    	return item(N)
    },val)
    Copy the code

    Since the second argument val is passed, the initial value of N is val. We simply execute item(N) in each iteration and assign the result to N.

Finally finished, thank you for your company!