1. The closure

The formation of closures is closely related to the scope and lifetime of variables

1.1 Scope of variables

The valid range of a variable. A common one is the scope of a variable declared in a function.

When a variable is declared in a function, if it is not preceded by the keyword var, it is a global variable (causing naming conflicts).

Use var to declare a variable in a function. It is a local variable that can only be accessed from inside the function, not outside.

Example:

var func = function () {
    var a = 1;
    alert(a); // Output: 1
}
func();
alert(a) // Output: Uncaught ReferenceError: A is not defined
Copy the code

In JS, functions can be used to create function scopes. The outside variables can be seen inside the function, while the outside variables cannot be seen inside the function. This is because when searching for a variable in a function, if the variable is not declared in the function, the search will follow the scope chain created by the code execution environment, until it reaches the global object. Variables are searched from the inside out rather than the outside in.

Example:

var a = 1;

var func1 = function() {
    var b = 2;
    var func2 = function() {
        var c = 3;
        alert(b); // Output: 2
        alert(c); // Output: 1
    }
    func2();
    alert(c); Uncaught ReferenceError: C is not defined
}

func1();
Copy the code

1.2 Life cycle of variables

In addition to the scope of a variable, another concept related to closures is the life cycle of a variable.

For global variables, the lifetime of the global variable is permanent unless the global variable is actively destroyed.

Local variables declared within a function with the var keyword lose their value when the function exits. They are destroyed at the end of the call:

var func = function(){
    var a = 1; // After the function exits, the local variable a will be destroyed
    alert(a);
}

func();
Copy the code

Look at this code:

var func = function(){
    var a = 1;
    return function(){ a++; alert(a); }}var f = func();

f(); // Output: 2
f(); // Output: 3
f(); // Output: 4
f(); // Output: 5
Copy the code

Contrary to the previous inference, the local variable A does not disappear after exiting the function, but seems to have always lived somewhere. This is because when executing var f = func(); F returns a reference to an anonymous function that has access to the context in which func() was called and the local variable a is always in. Since the environment in which the local variable is located can still be accessed by the outside world, the local variable has a reason not to be destroyed. A closure structure is generated here, and the life of the local variable appears to be continued.

A classic use of closures: Suppose you have five div nodes on your page. Loop around and bind onClick events to each div. In index order, 0 will pop up when you click on the first div, 1 will pop up when you click on the second div, and so on

<html>
    <body>
        <div>1</div>
        <div>2</div>
        <div>3</div>
        <div>4</div>
        <div>5</div>
    <script>
        var nodes = document.getElementsByTagName('div');
        
        for(var i=0,len=nodes.length; i<len; i++) {
            nodes[i].onclick = function() { alert(i); }}</script>
    </body>
</html>
Copy the code

Testing this code shows that no matter which div you click on, the result that pops up is 5. This is because the onclick event of the div node is fired asynchronously. By the time the event is fired, the for loop has already ended and the value of the variable I is already 5. Therefore, when looking for the variable I from inside to outside of the scope chain in the onclick event function of the div node, the value is always 5.

The solution is to close the I value for each loop with the help of closures. When looking for the variable I from the inside out of the scope chain in the event function, it will first find the I enclosed in the closure environment. If there are 5 divs, I will be 0,1,2,3,4:

for(var i=0,len=nodes.length; i<len; i++) {
    (function(i){
      nodes[i].onclick = function() {
        alert(i);
      }  
    })(i)
}
Copy the code

1.3 More effects of closures

1.3.1 Encapsulating variables

Closures can help wrap “private variables” that do not need to be exposed globally. Suppose we have a simple function to calculate the chance:

var mult = function() {
    var a = 1;
    for (var i=0, l = arguments.length; i<l; i++) {
        a = a * arguments[i];
    }
    return a;
}
Copy the code

The mult function takes some arguments of type number and returns an opportunity for those arguments. For the same parameters, it is wasteful to calculate each time, and caching can be added to improve the performance of the function.

var cache = {};
var mult = function() {
    var args = Array.prototype.join.call(arguments.', ')
    if (cache[args]) {
        return cache[args];
    }
    var a = 1;
    for (var i=0, l = arguments.length; i<l; i++) {
        a = a * arguments[i];
    }
    return  cache[args] = a;
}

alert(mult(1.2.3)); // Output: 6
alert(mult(1.2.3)); // Output: 6
Copy the code

The variable cache is only used in the mult function. It should be enclosed inside the mult function to reduce the global variable on the page and prevent it from being accidentally modified elsewhere and causing an error.

var mult = (function() {
    var cache = {};
    return function(){
        var args = Array.prototype.join.call(arguments.', ')
        if (args in cache) {
            return cache[args];
        }
        var a = 1;
        for (var i=0, l = arguments.length; i<l; i++) {
            a = a * arguments[i];
        }
        return  cache[args] = a;
    }
})();
Copy the code

1.3.2 Extend the life of a local variable

Img objects are often used for data reporting

var report = function(src) {
    var img = new Image();
    img.src = src;
}

report('http://xxx.com/getUSerInfo');
Copy the code

Due to bugs in the implementation of some older browsers, data reporting using the report function in these browsers loses about 30% of the data. In other words, the REPORT function does not successfully initiate HTTP requests every time. The reason for the loss is that img is a local variable in the report function, and when the report function is called, the img local variable is destroyed, perhaps before the HTTP request has been made, so the request will be lost.

The img variable is closed with a closure to solve the problem of missing requests:

var report = (function(src) {
    var imgs = [];
    return function(src) {
      var img = new Image();
      imgs.push(img);
      img.src = src;  
    }
})()
Copy the code

1.4 Closures and object-oriented design

The combination of process and data is a common expression used to describe “objects” in object-oriented terms. Objects contain procedures in the form of methods, while closures contain data in the form of environments within procedures. What you can usually do with object-oriented thinking, you can do with closures. And vice versa.

Closure related code

var extent = function(){
    var value = 0;
    return {
        call: function(){
            value++;
            console.log(value); }}};var extent = extent();

extent.call(); // Output: 1
extent.call(); // Output: 2
extent.call(); // Output: 3
Copy the code

Let me write it object-oriented

var extent = {
    value: 0.call: function(){
        this.value++;
        console.log(this.value); }}; extent.call();// Output: 1
extent.call(); // Output: 2
extent.call(); // Output: 3
Copy the code

or

var Extent = function(){
    this.value = 0;
};

Extent.prototype.call = function(){
    this.value++;
    console.log(this.value);
}

var extent = new Extent();

extent.call(); // Output: 1
extent.call(); // Output: 2
extent.call(); // Output: 3
Copy the code

1.5 Using closures to implement command mode

Start by writing a piece of command mode code in an object-oriented way

< HTML > <body> <button id="execute"> </button> <button id="undo"> </button> <script> var Tv = {open: Function (){console.log(' turn on the TV '); }, close: function(){console.log(' turn off the TV '); }}; var OpenTvCommand = function(receiver){ this.receiver = receiver; }; OpenTvCommand.prototype.execute = function(){ this.receiver.open(); / / execute commands, turn on the TV} OpenTvCommand prototype. Undo = function () {this. Receiver. The close (); // Undo the command, } var setCommand = function(command){document.getelementByid ('execute').onclick = function(){command-execute (); } document.getElementById('undo').onclick = function(){ command.undo(); }}; setCommand(new OpenTvCommand(Tv)); </script> </body> </html>Copy the code

The intent of the command pattern is to encapsulate the request as an object, thereby decoupling the relationship between the originator of the request and the receiver (executor) of the request. Before the command is executed, the receiver of the command can be pre-implanted into the command object.

In the closure version of the command mode, the receiver of the command is enclosed in the context of the closure, as follows:

var Tv = {
    open: function(){
        console.log('Turn on the TV');
    },
    close: function(){
        console.log('Turn off the TV'); }};var createCommand = function(receiver){
    var execute = function(){
        return receiver.open(); // Execute the command to turn on the TV
    }
    var undo = function(){
        return receiver.close(); // Undo the command to turn off the TV
    }
    return {
        execute: execute,
        undo: undo
    }
};

var setCommand = function(command){
    document.getElementById('execute').onclick = function(){
        command.execute();
    }

    document.getElementById('undo').onclick = function(){ command.undo(); }}; setCommand(createCommand(Tv));Copy the code

1.6 Closures and Memory Management

Myth: Closures cause memory leaks

Local variables are supposed to be dereferenced when the function exits, but if the local variable is enclosed in the context of the closure, the local variable can survive forever. In this sense, closures do prevent some data from being destroyed in time. Part of the reason for using closures is that we have chosen to actively enclose some variables in closures because we may need to use them in the future, and putting them in the closure has the same effect on memory as putting them in the global scope, which is not a memory leak. If we need to reclaim these variables in the future, we can manually set them to null.

Related to closures and memory leaks is that it is easier to form circular references while using closures, which can cause memory leaks if there are DOM nodes in the closure’s scope chain. But this is not a closure problem per se, nor is it a JavaScript problem. In Internet explorer, objects in BOM and DOM are implemented as COM objects using C++, and the garbage collection mechanism of COM objects adopts the reference technology strategy. In a garbage collection mechanism based on a reference counting strategy, if a circular reference is formed between two objects, neither object can be collected, but the memory leak caused by the circular reference is not caused by the closure per se.

Similarly, to solve the memory leaks caused by circular references, all we need to do is set the variable in the circular reference to null. Setting a variable to NULL means breaking the link between the variable and the value it previously referred to. The next time the garbage collector runs, these values are removed and the memory they occupy is reclaimed.

2. Higher-order functions

An advanced function is one that satisfies at least one of the following conditions.

  • Functions can be passed as arguments
  • Functions can be output as return values

Application scenarios

2.1 Functions are passed as arguments

You can separate the changing from the invariant parts of the business code by pulling out the part of the business logic that is easy to change and putting it in the function parameters. One important application scenario is the common callback function.

2.1.1 Callback function

Callbacks are used very frequently in the application of ajax asynchronous requests. When we want to do something after the Ajax request returns, but we don’t know the exact time when the request will return, the most common solution is to pass the callback function as an argument to the method that made the Ajax request and execute the callback function after the request has completed:

var getUserInfo = function( userId, callback ) {
    $.ajax('http://xxx.com/getUserInfo?' + userId, function(data){
        if (typeOf callback === 'function') { callback(data); }}); } getUserInfo(111.function(data) {
    alert(data.userName);
})
Copy the code

Callbacks are not only useful for asynchronous requests. When a function is not suitable to perform some request, we can also encapsulate the request into a function and pass it as an argument to another function, “delegating” it to another function for execution.

For example, create 100 div nodes on the page and set them to hidden.

var appendDiv = function(){
    for(var i =0; i<100; i++) {
        var div = document.createElement('div');
        div.innerHTML = i;
        document.body.appendChild(div);
        div.style.display = 'none'; }}; appendDiv();Copy the code

Hard-coding div.style.display = ‘none’ logic in appendDiv is obviously not reasonable. Functions are hard to reuse, and not everyone wants nodes to be hidden immediately after they are created.

Extract div.style.display = ‘none’ and pass it as a callback to appendDiv

var appendDiv = function(callback){
    for(var i =0; i<100; i++) {
        var div = document.createElement('div');
        div.innerHTML = i;
        document.body.appendChild(div);
        if (typeof callback === 'function') {
            callback(div)
        }
    }
};

appendDiv(function(node) {
     node.style.display = 'none';
});
Copy the code

As you can see, the request to hide the node was actually made by the client, but the client does not know when the node will be created, so the logic for the hidden node is “delegated” to the appendDiv method in the callback function. The appendDiv method of course knows when the node is created, so when the node is created, appendDiv executes the callbacks passed in by the client.

2.1.2 Array. Prototype. Sort

Array.prototype.sort takes as an argument a function that encapsulates the collation of the Array elements. As you can see from the use of array.prototype. sort, the purpose is to sort the Array, which is the invariable part; What rules to use to sort things is the variable part. The array.prototype. sort method is a very flexible one by wrapping the mutable part in a function argument and passing it dynamically

// Order from smallest to largest
[1.4.3].sort(function(a, b) {
    return a - b;
})
// output: [1,3,4]

// Order from largest to smallest
[1.4.3].sort(function(a, b) {
    return b - a;
})
// output: [4,3,1]
Copy the code

2.2 Functions are output as return values

Instead of passing a function as an argument, there are many more ways to output a function as a return value, which is a more clever way to use functional programming. Letting a function continue to return an executable function means that the operation is continuable.

2.2.1 Determine the function type

With the Object. The prototype. ToString to calculate. Object. The prototype. ToString. Call (obj) returns a string, Such as the Object. The prototype. ToString. Call ([1, 2, 3]) always returns “[Object Array]”, and the Object. The prototype. ToString. Call (” STR “) always returns “[Object String] “.

var isString = function( obj ) {
    return Object.prototype.toString.call(obj) === '[object String]';
}

var isArray = function( obj ) {
    return Object.prototype.toString.call(obj) === '[object Array]';
}

var isNumber = function( obj ) {
    return Object.prototype.toString.call(obj) === '[object Number]';
}
Copy the code

Found that most of the implementation of these functions are the same, just different Object. The prototype. ToString. Call (obj) the returned string. To avoid unnecessary code, embed these strings as arguments in isType functions ahead of time:

var isType = function(type) {
    return function (obj) {
       return Object.prototype.toString.call(obj) === '[object' + type + '] '; }}var isString = isType('String');
var isArray = isType('Array');
var isNumber = isType('Number');

console.log(isArray([1.2.3])) // Output: true
Copy the code

2.2.2 getSingle

Here is an example of a singleton pattern

var getSingle = function(fn) {
    var ret;
    return function() {
        return ret || (ret = fn.apply(this.arguments)); }}var getScript = getSingle(function() {
    return document.createElement('script');
});

var script1 = getScript();
var script2 = getScript();

alert(script1 === script2); // Output: true
Copy the code

It passes a function as an argument and returns another function after execution.

2.3 AOP for higher-order functions

The main role of AOP(Aspect Oriented Programming) is to extract functions that are not related to the core business logic modules, such as log statistics, security control, exception handling, and so on. Once these functions are taken out, they are incorporated into the business logic modules in a ‘dynamic embedding’ manner. The benefits of this are firstly that the business logic modules are kept pure and cohesive, and secondly that functional modules such as log statistics can be easily reused.

Implementing AOP in JavaScript is all about “dynamically inserting” one function into another. There are many ways to do this, including by extending Funtion.prototype

Function.prototype.before = function(beforefn) {
    var __self = this; // Save the reference to the original function
    return function() { // Return the "proxy" function containing the original function and the new function
        beforefn.apply(this.arguments); // Execute a new function to fix this
        return __self.apply(this.arguments); // Execute the original function}}Function.prototype.after = function(afterfn) {
    var __self = this;
    return function() {
        var ret = __self.apply(this.arguments);
        afterfn.apply(this.arguments);
        returnret; }}var func = function(){
    console.log(2)
}

func = func.before(function(){
    console.log(1)
}).after(function(){
    console.log(3)}); func();/ / output:
1
2
3
Copy the code

The functions responsible for printing the numbers 1 and 3 are dynamically incorporated into the FUNc function via AOP.

Adding responsibility to functions using AOP is also a very special and clever implementation of the decorator pattern in the JavaScript language.

2.4 Other applications of higher order functions

Against 2.4.1 currying

Function currying. Also known as partial evaluation. A currying function will first take some arguments, and once those arguments are taken, the function does not evaluate immediately, but goes on to return another function, and the arguments passed in are stored in the closure formed by the function. By the time the function is actually asked for a value, all parameters passed in are evaluated once.

Example: Write a function that calculates the monthly overhead. At the end of each day, record how much money you spent today.

var monthlyCost = 0;

var cost = function(money) {
    monthlyCost += money;
};

cost(100); // Day 1 overhead
cost(200); // The cost of the second day
cost(300); // Day 3 costs

alert(monthlyCost) // Output: 600
Copy the code

As you can see from this code, at the end of each day, the money spent so far is recorded and calculated. But we don’t really care much about how much money we spend each day, we just want to know how much we’ll spend at the end of the month. In other words, you only really need to do it once at the end of the month.

If, for the first 29 days of each month, we just kept the day’s expenses and evaluated them until the 30th day, we would have met our requirements. The following cost function is not a full implementation of a currying function, but helps us understand the idea:

var cost = (function(){
    var args = [];
    
    return function(){
        if (arguments.length === 0) {
            var money = 0;
            for (var i=0, l = args.length; i<l; i++ ) {
                money += args[i];
            }
            return money;
        } else {
            [].push.apply(args, arguments); }}}) (); cost(100); // Not really evaluated
cost(200); // Not really evaluated
cost(300); // Not really evaluated

console.log(cost()); // Evaluate and print: 600
Copy the code

Write a generic function currying(){},function currying(){} that takes one argument and is about to be curried. The purpose of this function is to iterate over the daily costs of the month and sum them up.

var currying = function(fn) {
    var args = [];
    return function(){
        if (arguments.length === 0) {
            return fn.apply(this, args)
        } else {
            [].push.apply(args, arguments);
            return arguments.callee; }}};var cost = (function(){
    var money = 0;
    return function(){
        for(var i=0, l=arguments.length; i<l; i++) {
            money += arguments[i];
        }
        return money;
    }
})();

var cost = currying(cost);

cost(100); // Not really evaluated
cost(200); // Not really evaluated
cost(300); // Not really evaluated

console.log(cost()); // Evaluate and print: 600
Copy the code

When cost() is called, if some parameters are explicitly included, it means that the actual evaluation is not performed, but the parameters are saved, and the cost function returns another function. Only when cost() is executed with no arguments does the evaluation actually begin, taking advantage of all the previously saved parameters.

2.4.2 uncurrying

In JavaScript, when we call a method on an object, we don’t really care whether the object was designed to have that method or not. This is a feature of dynamically typed languages, and is often referred to as duck typing.

Similarly, an object does not have to use only its own methods, so is there any way for an object to borrow a method that does not belong to it?

Both Call and Apply can fulfill this requirement:

var obj1 = {
    name: 'xiaoming'
}

var obj2 = {
    getName: function(){
       return this.name; }}console.log(obje2.getName.call(obj1)); // Output: xiaoming
Copy the code

We often use array-like objects to borrow array. prototype methods. This is one of the most common application scenarios for call and apply:

(function(){
    Array.prototype.push.call(arguments.4); // Arguments borrow the array.prototype. push method
    console.log(arguments); // output: [1,2,3,4]}) (1.2.3)
Copy the code

As expected, the methods on Array.prototype were intended to operate only on Array objects. But with call and apply, you can pass any object as this into a method. In this way, the use of this in a method is not limited to the specified object, but is generalized and more applicable.

Is there any way to extract the process that generalizes this? Uncurrying is used to solve this problem.

Function.prototype.uncurrying = function(){
    var self = this;
    return function() {
        var obj = Array.prototype.shift.call(arguments);
        return self.apply(obj, arguments); }}Copy the code

Before I explain how this code works, let’s analyze what it does.

In class Array object the arguments to borrow Array. Before the prototype method, the Array. The first prototype. Push. Call this code into a general push function:

var push = Array.prototype.push.uncurrying();

(function(){
    push(arguments.4);
    console.log(arguments); // output: [1,2,3,4]}) (1.2.3);
Copy the code

, by means of uncurrying Array. Prototype. Push. Call into a generic push function. In this way, the scope of the push function is the same as that of array.prototype. push. For the user, the way to call the push function is more concise and the intention is clear.

We can also “copy” array. prototype’s methods to Array objects at once, and these methods can operate on more than just Array objects:

for(var i=0, fn, ary = ['push'.'shift'.'forEach']; fn = ary[i++];) {
    Array[fn] = Array.prototype[fn].uncurrying();
};

var obj = {
    "length": 3."0": 1."1": 2."2": 3
}

Array.push(obj, 4); // Add an element to the object
console.log(obj.length);  // Output: 4

var first = Array.shift(obj); // Cut the first element
console.log(first); / / output
console.log(obj); // Output: {0:2,1:3,2:4,length: 3}

Array.forEach(obj, function(i, n) {
    console.log(n) // output: 0,1,2
})
Copy the code

Even function.prototype. call and function.prototype. apply themselves can be uncurried, but this has no practical value, it just makes calls to functions look more like Scheme, the predecessor of the JavaScript language:

var call = Function.prototype.call.uncurrying();
var fn = function(name) {
    console.log(name);
}
call(fn, window.'xiaoming'); // Output: xiaoming

var apply = Function.prototype.apply.uncurrying();
var fn = function(name) {
    console.log(this.name); // Output: xiaoming
    console.log(arguments); // output: [1,2,3]
}
call(fn, {name: 'xiaoming'},1.2.3]);
Copy the code

Now cut used to analyze the Array. The prototype. Push. Uncurrying () what happened when the code:

Function.prototype.uncurrying = function(){
    var self = this; //self: array.prototype.push
    return function() {
        var obj = Array.prototype.shift.call(arguments);
        / / obj is {
        // "length": 1,
        / / "0" : 1
        // }
        // Arguments the first element of the object is truncated, leaving [2]
        return self.apply(obj, arguments);
        / / equivalent to the Array. The prototype. Push. Apply (obj, 2)}}var push = Array.prototype.push.uncurrying();
var obj = {
    "length": 1."0": 1
}

push(obj, 2);
console.log(obj); // Output: {0:1,1:2,length: 2}
Copy the code

Another implementation of uncurrying:

Function.prototype.uncurrying = function() {
    var self = this;
    return funtion() {
        return Function.prototype.call.apply(self, arguments); }}Copy the code

2.4.3 Function throttling

Functions in JavaScript are mostly triggered by user calls, and unless the implementation of the function itself is inappropriate, you don’t typically encounter performance-related problems. In a few cases, however, the triggering of a function is not directly controlled by the user. In these scenarios, functions can be called very frequently, causing major performance problems. Here are some scenarios.

(1) The scenario where the function is called frequently

  • Window. The onresize event. The window object is bound to a resize event, which is triggered very frequently when the browser window size is changed by dragging. If we do something related to the DOM node in the window.onresize event function, it can be very performance consuming and may cause the browser to stall.

  • Mousemove events. Similarly, a div node is attached with a drag event (mainly mousemove) that is frequently triggered when the div node is dragged.

(2) Function throttling principle

You can use setTimeout

(3) Function throttling code implementation

There are a number of code implementations of throttling functions. The following throttle function delays the execution of the function to be executed for a certain period of time with setTimeout. If this delayed execution is not complete, subsequent requests to call the function are ignored. The throttle function accepts two requests. The first argument is the function to be delayed, and the second argument is the time to delay execution.

var throttle = function(fn, interval){
    var __self = fn, // Save function references that need to be delayed
        timer, / / timer
        firstTime = true; // Whether it is the first call
        
    return function() {
        var args = arguments,
            __me = this;
            
        if (firstTime) { // If this is the first call, no delay is required
            __self.apply(__me, args);
            return firstTime = false;
        }
        
        if (timer) { // If the timer is still in place, the previous delay has not completed
            return false;
        }
        
        timer = setTimeout(function() { // Delay execution for some time
            clearTimeout(timer);
            timer = null;
            __self.apply(__me, args);
        }, interval || 500);
    };    
}

window.onresize = throttle(function(){
    console.log(1)},500)
Copy the code

2.4.4 Time-sharing function

Some functions are initiated by the user, but for several reasons, these functions can seriously affect the performance of the page.

For example, create a list of friends. There are hundreds or thousands of friends in the list, and if a friend is represented by a node, when we render the list on the page, we may have to create hundreds or thousands of nodes on the page at once.

Adding a large number of DOM nodes to a page in a short period of time can also obviously overwhelm the browser, causing it to stall or even fake death.

var ary = [];

for(var i=1; i<=1000; i++) {
    ary.push(i);
};

var renderFriendList = function(data) {
    for (var i=0, l= data.length; i<l; i++) {
        var div = document.createElement('div');
        div.innerHTML = i;
        document.body.appendChild(div);
    }
}

renderFriendList(ary);
Copy the code

One solution: Let node creation be done in batches, say, instead of 1000 nodes per second, create 8 nodes every 200 milliseconds.

The timeChunk function takes three arguments. The first argument is the data needed to create the node, the second argument is the logic that encapsulates the creation of the node, and the third argument represents the number of nodes to be created in each batch.

var timeChunk = function(ary, fn, count) {
    var obj,
        t;
    
    var len = ary.length;
    
    var start = funciton(){
        for(var i=0; i<Math.min(count || 1, array.length); i++) {
            varobj = ary.shift(); fn(obj); }}return function(){
        t = setInterval(function(){
            if (ary.length === 0) { // If all nodes have been created
                return clearInterval(t);
            }
            start();
        }, 200) // The batching interval can also be passed as a parameter
    };
}
Copy the code
var ary = [];

for(var i=1; i<=1000; i++) {
    ary.push(i);
};

var renderFriendList = timeChunk(ary, function(n) {
  var div = document.createElement('div');
  div.innerHTML = n;
  document.body.appendChild(div);      
}, 8)

renderFriendList();
Copy the code

2.4.5 Lazy loading functions

In Web development, some sniffing is inevitable because of implementation differences between browsers. For example, we need an event binding function called addEvent that is common across browsers.

var addEvent = function(elem, type, handler) {
    if (window.addEventListener) {
        return elem.addEventListener(type, handler, false);
    }
    if (window.attachEvent) {
        return elem.attachEvent('on'+type, hanlder); }}Copy the code

The downside of this function is that every time it is called, it will execute the if condition branch inside. Although the overhead of executing these if branches is not too great, there may be ways for your program to avoid this repetitive execution.

The second option is to advance browser sniffing until the code loads, and make an immediate judgment when the code loads so that addEvent returns a function that wraps the correct logic.

var addEvent = (function(){
    if (window.addEventListener) {
        return function(elem, type, handler){
            elem.addEventListener(type, handler, false); }}if (window.attachEvent) {
        return function(elem, type, handler){
            elem.attachEvent('on'+type, hanlder); }}}) ();Copy the code

One drawback to the current addEvent function is that we haven’t used addEvent from the beginning to the end of the business, so the previous browser sniffing is completely redundant, and it also slightly lengthens the page ready time.

The third option: lazy loading functions. AddEvent is still declared as a normal function, and there are still some branching decisions within the function. However, after entering the conditional branch for the first time, the function is overwritten inside the function. The overwritten function is the expected addEvent function. The next time we enter the addEvent function, there is no conditional branch in the addEvent function:

var addEvent = function(elem, type, handler) {
    if (window.addEventListener) {
        addEvent = function(elem, type, handler){
            elem.addEventListener(type, handler, false); }}if (window.attachEvent) {
        addEvent = function(elem, type, handler){
            elem.attachEvent('on'+type, hanlder);
        } 
    }
    
    addEvent(elem, type, handler);
}

var div = document.getElementById('div');

addEvent(div, 'click'.function(){
    alert(1)
})

addEvent(div, 'click'.function(){
    alert(2)})Copy the code

Summary: Closures and higher-order functions are used a lot in JavaScript development. In terms of design patterns, because of the characteristics of JavaScript, the implementation of many design patterns in JavaScript is quite different from that in some traditional object-oriented languages. In JavaScript, many design patterns are implemented through closures and higher-order functions.