Detailed explanation of common design patterns in Javascript

Original link: https://www.cnblogs.com

1: Understand factory model

The factory model is similar to the factory in real life, which can produce a large number of similar goods, do the same things and achieve the same effect; You need to use factory mode at this time.

A simple factory model can be understood as solving multiple similar problems; This is also her advantage; For example, the following code:

function CreatePerson(name,age,sex) {
    var obj = new Object();
    obj.name = name;
    obj.age = age;
    obj.sex = sex;
    obj.sayName = function(){
        return this.name;
    }
    return obj;
}
var p1 = new CreatePerson("longen",'28','male');
var p2 = new CreatePerson("tugenhua",'27','female');
console.log(p1.name); // longen
console.log(p1.age);  // 28
console.log(p1.sex);  // male
console.log(p1.sayName()); // longen

console.log(p2.name);  // tugenhua
console.log(p2.age);   // 27
console.log(p2.sex);   // female
console.log(p2.sayName()); // tugenhua

// The returned objects are the types of objects that the object cannot recognize. I don't know which object they are
console.log(typeof p1);  // object
console.log(typeof p2);  // object
console.log(p1 instanceof Object); // true

The above code: the function CreatePerson can accept three parameters, such as name,age,sex, etc. this function can be called countless times. Each return will contain three properties and a method object.

Factory pattern is to solve the problem of multiple similar object declarations; That is to solve the problem of repetition of materialized objects.

Advantages: it can solve multiple similar problems.

Disadvantages: the problem of object recognition cannot be known (the type of object is not known).

The definition of complex factory pattern is to postpone the materialization of its member objects to subclasses. Subclasses can override the parent class interface methods to specify their own object types when they are created.

The parent class only handles the general problems in the creation process, which will be inherited by the subclasses. The subclasses are independent of each other, and the specific business logic will be written in the subclasses.

The parent class becomes an abstract class, but the parent class can execute the same and similar methods in the child class, and the specific business logic needs to be implemented in the child class; For example, I now open several bicycle stores, so each store has several models of bicycles for sale. Let's now write this code using the factory pattern;

The constructor of the parent class is as follows:

// Defines the constructor for the bicycle
var BicycleShop = function(){};
BicycleShop.prototype = {
    constructor: BicycleShop,
    /*
    * The way to buy a bike
    * @param {model} Bicycle model
    */
    sellBicycle: function(model){
        var bicycle = this.createBicycle(mode);
        // Execute A business logic
        bicycle.A();

        // Execute B business logic
        bicycle.B();

        return bicycle;
    },
    createBicycle: function(model){
        throw new Error("The parent class is an abstract class and cannot be called directly. The method needs to be overridden by the child class");
    }
};

The above defines a bicycle abstract class to write the real column of the factory pattern, and defines the createpolicy method. However, if you directly instantiate the parent class and call the createpolicy method in the parent class, an error will be thrown, because the parent class is an abstract class and cannot be materialized. You can only implement this method through subclasses to realize your own business logic, Next, let's define subclasses. We learn how to rewrite this method using the factory pattern. First, we need to inherit the members in the parent class, and then write subclasses; The following code:

// Defines the constructor for the bicycle
var BicycleShop = function(name){
    this.name = name;
    this.method = function(){
        return this.name;
    }
};
BicycleShop.prototype = {
    constructor: BicycleShop,
    /*
     * The way to buy a bike
     * @param {model} Bicycle model
    */
    sellBicycle: function(model){
            var bicycle = this.createBicycle(model);
            // Execute A business logic
            bicycle.A();

            // Execute B business logic
            bicycle.B();

            return bicycle;
        },
        createBicycle: function(model){
            throw new Error("The parent class is an abstract class and cannot be called directly. The method needs to be overridden by the child class");
        }
    };
    // Implement prototype inheritance
    function extend(Sub,Sup) {
        //Sub represents subclass and Sup represents superclass
        // First, define an empty function
        var F = function(){};

        // Set the prototype of the empty function to the prototype of the superclass
        F.prototype = Sup.prototype; 

        // Instantiate the empty function and pass the superclass prototype reference to the subclass
        Sub.prototype = new F();
                    
        // Reset the constructor of the subclass prototype to the subclass itself
        Sub.prototype.constructor = Sub;
                    
        // Save the prototype of superclass in subclass to avoid the coupling between subclass and superclass
        Sub.sup = Sup.prototype;

        if(Sup.prototype.constructor === Object.prototype.constructor) {
            // Detect whether the constructor of the superclass prototype is the prototype itself
            Sup.prototype.constructor = Sup;
        }
    }
    var BicycleChild = function(name){
        this.name = name;
// Inherit properties and methods from the constructor's parent class
        BicycleShop.call(this,name);
    };
    // The subclass inherits the prototype method of the parent class
    extend(BicycleChild,BicycleShop);
// The subclass of BicycleChild overrides the method of the parent class
BicycleChild.prototype.createBicycle = function(){
    var A = function(){
        console.log("implement A Business operation");    
    };
    var B = function(){
        console.log("implement B Business operation");
    };
    return {
        A: A,
        B: B
    }
}
var childClass = new BicycleChild("Long en");
console.log(childClass);

Instantiate the subclass and print the instance, as shown in the following screenshot:

console.log(childClass.name); / / long en

//The following is the sellcycle in the parent class after instantiation. After this method is executed, A in the parent class will be called in turn

//And method B; Method A and method B write specific business logic in subclasses in turn.

childClass.sellBicycle("mode"); / / print out A business operation and B business operation

The above is only a model of "long en" bicycle. If you need to generate other models of bicycles, you can write other subclasses. The most important advantage of factory mode is that you can implement some of the same methods. We can write code in the parent class, so you need to implement specific business logic, Then you can override the method of the parent class in a subclass to implement your own business logic; In professional terms, there are two points: first, weaken the coupling between objects and prevent code duplication. Instantiating a class in a method can eliminate repetitive code. Second: repetitive code can be written in the parent class. Subclasses inherit all member properties and methods of the parent class. Subclasses only focus on implementing their own business logic.

2: Understand monomer mode

Monomer mode provides a means to organize code into a logical unit, in which the code can be accessed through a single variable.

The advantages of monomer mode are:

  1. It can be used to divide namespaces and reduce the number of global variables.
  2. Using monomer mode can make the code organization more consistent and make the code easy to read and maintain.
  3. Can be instantiated and instantiated once.

What is monomer mode? Monomer pattern is an object used to divide namespaces and organize a group of attributes and methods together. If it can be instantiated, it can only be instantiated once.

However, not all object literals are monomeric. For example, if you simulate an array or hold data, it is not monomeric. However, if you organize a group of related attributes and methods together, it may be monomeric mode, so it depends on the developer's intention to write code;

Let's take a look at the basic structure of defining an object literal (the structure is similar to the monomer mode):

// Object Literal 
var Singleton = {
    attr1: 1,
    attr2: 2,
    method1: function(){
        return this.attr1;
    },
    method2: function(){
        return this.attr2;
    }
};

For example, the above is only a simple literal structure. All member variables above are accessed through Singleton, but it is not a monomer mode; A more important feature of monomer mode is that it can be instantiated only once. The above class is only a class that cannot be instantiated, so it is not a monomer mode; Object literals are one of the methods used to create monomer patterns;

The structure of using monomer mode is as follows: demo

What we understand is that if a monomer pattern is instantiated, it is instantiated only once. To implement a monomer pattern, we simply use a variable to identify whether the class is instantiated. If it is not instantiated, we can instantiate it once. Otherwise, we directly return the instantiated object.

The following code is the basic structure of monomer mode:

// Monomer mode
var Singleton = function(name){
    this.name = name;
    this.instance = null;
};
Singleton.prototype.getName = function(){
    return this.name;
}
// Get instance object
function getInstance(name) {
    if(!this.instance) {
        this.instance = new Singleton(name);
    }
    return this.instance;
}
// Example of testing monomer mode
var a = getInstance("aa");
var b = getInstance("bb");

/Because the monomer pattern is instantiated only once, the following examples are equal

console.log(a === b); // true

Since the monomer mode is instantiated only once, the first call returns the instance object of a. when we continue to call, the instance of b is the instance of a. therefore, aa is printed below;

console.log(a.getName());// aa

console.log(b.getName());// aa

The above encapsulation monomer mode can also be changed to the following structure:

// Monomer mode
var Singleton = function(name){
    this.name = name;
};
Singleton.prototype.getName = function(){
    return this.name;
}
// Get instance object
var getInstance = (function() {
    var instance = null;
    return function(name) {
        if(!instance) {
            instance = new Singleton(name);
        }
        return instance;
    }
})();
// Example of testing monomer mode
var a = getInstance("aa");
var b = getInstance("bb");

//Because the monomer pattern is instantiated only once, the following examples are equal

console.log(a === b); // true

console.log(a.getName());// aa

console.log(b.getName());// aa

Understand the benefits of using proxies to implement single column patterns
For example, if I need to create a div element on the page now, we certainly need a function to create a div, but now I only need this function to be responsible for creating a div element. It doesn't want to care about others, that is, it wants to implement the single responsibility principle, just like kissy on Taobao. At the beginning, they defined kissy to do only one thing and do it well, The instantiation class in the specific monomer mode is handled by the agent function. The advantage of this is that the specific business logic is separated. The agent only cares about the business logic of the agent. Here, the role of the agent is to instantiate the object and instantiate it only once; When creating div code, just create div and ignore others; The following code:

// Monomer mode
var CreateDiv = function(html) {
    this.html = html;
    this.init();
}
CreateDiv.prototype.init = function(){
    var div = document.createElement("div");
    div.innerHTML = this.html;
    document.body.appendChild(div);
};
// Agent implementation monomer mode
var ProxyMode = (function(){
    var instance;
    return function(html) {
        if(!instance) {
            instance = new CreateDiv("Let me test it");
        }
        return instance;
    } 
})();
var a = new ProxyMode("aaa");
var b = new ProxyMode("bbb");
console.log(a===b);// true

Understand the basic principle of using monomer mode to realize pop-up window

Next, let's continue to use monomer mode to implement a pop-up demo; Let's not discuss the use of monomer mode. Let's think about how we usually write code to achieve pop-up effect; For example, we have a pop-up window, which must be hidden by default. When I click, it needs to be displayed; Write the code as follows:

// Realize pop-up window
var createWindow = function(){
    var div = document.createElement("div");
    div.innerHTML = "I'm the pop-up content";
    div.style.display = 'none';
    document.body.appendChild('div');
    return div;
};
document.getElementById("Id").onclick = function(){
    // Click to create a div element first
    var win = createWindow();
    win.style.display = "block";
}

The above code; You can see that there are obvious disadvantages. For example, if I click an element, I need to create a div, and if I click the second element, I will create a div again. If we click so and so frequently, they will frequently create div elements. Although we can remove the pop-up code when we click close, our frequent creation and deletion are not good, In particular, it will have a great impact on performance. Frequent operations on DOM will cause redrawing, which will affect performance; So this is a very bad habit; We can now use the monomer mode to achieve the pop-up effect. We can only instantiate it once; The following code:

// Realize single mode pop-up window
var createWindow = (function(){
    var div;
    return function(){
        if(!div) {
            div = document.createElement("div");
            div.innerHTML = "I'm the pop-up content";
            div.style.display = 'none';
            document.body.appendChild(div);
        }
        return div;
    }
})();
document.getElementById("Id").onclick = function(){
    // Click to create a div element first
    var win = createWindow();
    win.style.display = "block";
}

Understand and write common monomer patterns

Although the above pop-up code completes the use of monomer mode to create pop-up effect, the code is not universal. For example, the above is the code to complete pop-up. What if we need an iframe in the page in the future? Do we need to rewrite a set of code to create iframe? For example, create iframe as follows:

var createIframe = (function(){
    var iframe;
    return function(){
        if(!iframe) {
            iframe = document.createElement("iframe");
            iframe.style.display = 'none';
            document.body.appendChild(iframe);
        }
        return iframe;
    };
})();

We can see the above code. The code for creating div is very similar to the code for creating iframe. Now we can consider separating the general code to make the code completely abstract. Now we can write a set of code encapsulated in getInstance function, as follows:

var getInstance = function(fn) {
    var result;
    return function(){
        return result || (result = fn.call(this,arguments));
    }
};

The above code: we use a parameter fn to pass it in. If there is an instance of result, it will be returned directly. Otherwise, the current getInstance function calls the fn function, and the this pointer points to the fn function; After that, the return is saved in the result; Now we can pass a function in, whether it is creating div or iframe. In short, if this is the case, we can use getInstance to obtain their instance objects;

The code for creating iframe and div is as follows:

// Create div
var createWindow = function(){
    var div = document.createElement("div");
    div.innerHTML = "I'm the pop-up content";
    div.style.display = 'none';
    document.body.appendChild(div);
    return div;
};
// Create iframe
var createIframe = function(){
    var iframe = document.createElement("iframe");
    document.body.appendChild(iframe);
    return iframe;
};
// Get the encapsulation code of the instance
var getInstance = function(fn) {
    var result;
    return function(){
        return result || (result = fn.call(this,arguments));
    }
};
// Test create div
var createSingleDiv = getInstance(createWindow);
document.getElementById("Id").onclick = function(){
    var win = createSingleDiv();
    win.style.display = "block";
};
// Test create iframe
var createSingleIframe = getInstance(createIframe);
document.getElementById("Id").onclick = function(){
    var win = createSingleIframe();
    win.src = "http://cnblogs.com";
};

3: Understanding module mode

We understand that monomer patterns are created in the way of object literals through monomer patterns; For example, the method code of the following object literal is as follows:

var singleMode = {
    name: value,
    method: function(){
                
    }
};

The idea of module mode is to add private variables and private methods to monomer mode, which can reduce the use of global variables; The following is the code structure of a module mode:

var singleMode = (function(){
    // Create private variable
    var privateNum = 112;
    // Create private function
    function privateFunc(){
        // Implement your own business logic code
    }
    // Returns an object containing public methods and properties
    return {
        publicMethod1: publicMethod1,
        publicMethod2: publicMethod1
    };
})();

Module mode uses an anonymous function that returns an object. Inside this anonymous function, private variables and functions are defined for internal functions, and then an object literal is returned as the value of the function. The returned object literal contains only properties and methods that can be exposed. In this way, external use of the method can be provided; Since the public method in the return object is defined inside the anonymous function, it can access the internal private variables and functions.

When do we use module mode?

If we have to create an object and initialize it with some data, and expose some methods that can access these private data, we can use the module mode at this time.

Understanding enhanced module patterns

The enhanced module mode is used when a single column must be an instance of a certain type, and some attributes or methods must be added to enhance it. For example, the following code:

function CustomType() {
    this.name = "tugenhua";
};
CustomType.prototype.getName = function(){
    return this.name;
}
var application = (function(){
    // Define private
    var privateA = "aa";
    // Define private functions
    function A(){};

    // After instantiating an object, return the instance, and then add some public properties and methods to the instance
    var object = new CustomType();

    // Add public attribute
    object.A = "aa";
    // Add public method
    object.B = function(){
        return privateA;
    }
    // Returns the object
    return object;
})();

Next, let's print the application object; As follows:

console.log(application);

Continue printing the public attribute and method as follows:

console.log(application.A);// aa

console.log(application.B()); // aa

console.log(application.name); // tugenhua

console.log(application.getName());// tugenhua

4: Understanding agent patterns

Management is an object that can be used to control access to ontology objects. It implements the same interface as ontology objects. Proxy objects will pass all calling methods to ontology objects; The most basic form of proxy mode is to control access, and the ontology object is responsible for executing the function or class of the assigned object. In short, the local object focuses on executing the code on the page, and the proxy controls when the local object is instantiated and used; We have used some proxy patterns in the monomer pattern above, that is, we use the proxy pattern to instantiate the monomer pattern, and leave other things to the ontology object to deal with;

Advantages of agent:

  1. Proxy object can be instantiated instead of ontology and can be accessed remotely;
  2. It can also postpone ontology instantiation until it is really needed; For ontologies that are time-consuming to instantiate, or ontologies that are too large to be saved in memory when not in use, we can delay instantiating the object;

Let's first understand that the proxy object is instantiated instead of the ontology object; For example, now JD ceo wants to give a gift to the milk tea girl, but if the ceo is embarrassed to give it, or has no time to give it due to busy work, he wants to entrust his agent to do it at this time, so we can use the agent mode to write the following code:

// First declare a milk tea girl object
var TeaAndMilkGirl = function(name) {
    this.name = name;
};
// This is Mr. Jingdong ceo
var Ceo = function(girl) {
    this.girl = girl;
    // Send wedding gifts to tea girl
    this.sendMarriageRing = function(ring) {
        console.log("Hi " + this.girl.name + ", ceo A gift for you:" + ring);
    }
};
// Jingdong ceo's agent is an agent to replace the delivery
var ProxyObj = function(girl){
    this.girl = girl;
    // The agent sent gifts to the tea girl
    this.sendGift = function(gift) {
        // The proxy pattern is responsible for ontology object instantiation
        (new Ceo(this.girl)).sendMarriageRing(gift);
    }
};
// initialization
var proxy = new ProxyObj(new TeaAndMilkGirl("Milk tea sister"));
proxy.sendGift("Wedding ring"); // Hi, the ceo gives you a gift: wedding ring

According to the basic structure of the code above, TeaAndMilkGirl is a sent object (here is the milk tea girl); CEO , is the object of giving gifts. He saves the attribute of milk tea girl and has his own privileged method sendMarriageRing , which is the method of giving gifts to milk tea girl; Then, he wants to finish this through his agent, so he needs to create an agent model of economic man, named ProxyObj; His main task is to give the CEO's gift to his lover. Therefore, the object also needs to save the CEO's lover's object as its own attribute. At the same time, it also needs a privileged method sendGift, which is to give gifts. Therefore, the ontology object can be instantiated in this method. The ontology object here is the CEO's flower giving, Therefore, you need to instantiate the ontology object and call the method of the ontology object (sendMarriageRing)

Finally, we need the proxy object ProxyObj for initialization; Call the flower sending method (sendGift) of ProxyObj# object;

For the advantages we mentioned, the second point is that we can understand the virtual agent. The virtual agent is used to control the access to the ontology with high creation cost. It will delay the instantiation of the ontology until a method is called; For example, if the instantiation of an object is very slow and cannot be completed immediately when the web page is loaded, we can create a virtual agent for it to delay the instance of the object until it is needed.

Understand the use of virtual agents to preload images

In web page development, image preloading is a commonly used technology. If you directly set src attribute to img tag node, if the image is large or the network speed is relatively slow, the image will be blank for a period of time before the image is loaded, which is not good for user experience, At this time, we can use a loading image as a placeholder to prompt the user that the image is loading before the image is loaded. After the image is loaded, we can directly assign a value to the image; Below, we do not need the proxy mode to realize the preloading of pictures. The code is as follows:

The first scheme: the preload image function without agent is as follows

// The preload picture function without proxy is as follows
var myImage = (function(){
    var imgNode = document.createElement("img");
    document.body.appendChild(imgNode);
    var img = new Image();
    img.onload = function(){
        imgNode.src = this.src;
    };
    return {
        setSrc: function(src) {
            imgNode.src = "http://img.lanrentuku.com/img/allimg/1212/5-121204193Q9-50.gif";
            img.src = src;
        }
    }
})();
// Call mode
myImage.setSrc("https://img.alicdn.com/tps/i4/TB1b_neLXXXXXcoXFXXc8PZ9XXX-130-200.png");

The above code is implemented without using the proxy mode;

The second scheme: use the proxy mode to write the code of preloaded pictures as follows:

var myImage = (function(){
    var imgNode = document.createElement("img");
    document.body.appendChild(imgNode);
    return {
        setSrc: function(src) {
            imgNode.src = src;
        }
    }
})();
// proxy pattern
var ProxyImage = (function(){
    var img = new Image();
    img.onload = function(){
        myImage.setSrc(this.src);
    };
    return {
        setSrc: function(src) {
                         myImage.setSrc("http://img.lanrentuku.com/img/allimg/1212/5-121204193Q9-50.gif");
        img.src = src;
        }
    }
})();
// Call mode
ProxyImage.setSrc("https://img.alicdn.com/tps/i4/TB1b_neLXXXXXcoXFXXc8PZ9XXX-130-200.png");

The first is to use the general encoding method to realize the preloading technology of the picture. First, create the imgNode element, then call myImage.. When using the setsrc method, first give the picture a preloaded picture, and then assign a value to the img element when the picture is loaded. The second scheme is implemented using the proxy mode. The myimage  function is only responsible for creating the img element, and the proxy function ProxyImage  is responsible for setting the loading picture for the picture. When the picture is really loaded, call myimage in myimage The setsrc method sets the path of the picture; Their advantages and disadvantages are as follows:

  1. In the first scheme, the coupling of the general method code is too high. A function is responsible for several things, such as creating img elements and setting the loading state before completing the loading of unloaded images, which does not meet the single responsibility principle in the object-oriented design principle; And when you don't need an agent at some time, you need to delete the code from the myImage} function, so the code coupling is too high.
  2. The second scheme uses the proxy mode, in which the myImage # function is only responsible for doing one thing, creating an img element and adding it to the page. The loading picture is handed over to the proxy function ProxyImage # to do. When the picture is loaded successfully, the proxy function ProxyImage # will notify and execute the method of the myImage # function. At the same time, if the proxy object is not needed in the future, We can directly call the methods of ontology objects;

From the above proxy mode, we can see that the proxy mode and ontology object have the same method setSrc, which has the following two advantages:

  1. Users can safely request agents. They only care about whether they can get the desired results. If we don't need a proxy object, we can directly call the method instead of an ontology object.
  2. Wherever ontology objects are used, proxies can be used instead.

Of course, if both proxy objects and ontology objects return an anonymous function, they can also be considered to have a constant interface; For example, the following code:

var myImage = (function(){
    var imgNode = document.createElement("img");
    document.body.appendChild(imgNode);
    return function(src){
        imgNode.src = src; 
    }
})();
// proxy pattern
var ProxyImage = (function(){
    var img = new Image();
    img.onload = function(){
        myImage(this.src);
    };
    return function(src) {
                myImage("http://img.lanrentuku.com/img/allimg/1212/5-121204193Q9-50.gif");
        img.src = src;
    }
})();
// Call mode
ProxyImage("https://img.alicdn.com/tps/i4/TB1b_neLXXXXXcoXFXXc8PZ9XXX-130-200.png");

Understanding of virtual proxy merging http requests:

For example, in the back-end system, there is table data, and there is a check box button in front of each piece of data. When you click the check box button, you need to obtain the id and pass it to the server to send an ajax request. The server needs to record this data and make a request. If we click every time to send an http request to the server, For the server, the pressure is high and the network requests are frequent. However, if the real-time data of the system is not very high, we can collect all IDS within a period of time (for example, 2-3 seconds) through a proxy function and send ajax requests to the server at one time. Relatively speaking, the network requests are reduced and the server pressure is reduced;

// First, the html structure is as follows:
<p>
    <label>Selection box</label>
    <input type="checkbox" class="j-input" data-id="1"/>
</p>
<p>
    <label>Selection box</label>
    <input type="checkbox" class="j-input" data-id = "2"/>
</p>
<p>
    <label>Selection box</label>
    <input type="checkbox" class="j-input" data-id="3"/>
</p>
<p>
    <label>Selection box</label>
    <input type="checkbox" class="j-input" data-id = "4"/>
</p>

In general, JS is written as follows:

<script>
    var checkboxs = document.getElementsByClassName("j-input");
    for(var i = 0,ilen = checkboxs.length; i < ilen; i+=1) {
        (function(i){
            checkboxs[i].onclick = function(){
                if(this.checked) {
                    var id = this.getAttribute("data-id");
                    // Here are the ajax requests
                }
            }
        })(i);
    }
</script>

Next, we use the virtual agent to delay for 2 seconds, obtain the button IDs of all the selected check boxes after 2 seconds, and send a request to the server at one time.

Click the check box on the page, add an attribute isflag when it is selected, delete the attribute isflag when it is not selected, and then delay for 2 seconds. After 2 seconds, re judge the id on the attribute of isflag in all check boxes on the page, store it in the array, and then the proxy function calls the method of the ontology function to send all IDS delayed for 2 seconds to the ontology method at one time, The ontology method can obtain all IDs and send ajax requests to the server. In this way, the request pressure of the server is relatively reduced.

The code is as follows:

// Ontology function
var mainFunc = function(ids) {
    console.log(ids); // You can print all the selected IDs
    // Then send ajax requests to the server for all IDS at one time
};
// The proxy function obtains all IDS through the proxy function and passes them to the ontology function for execution
var proxyFunc = (function(){
    var cache = [],  // Save id for a period of time
        timer = null; // timer
    return function(checkboxs) {
        // Judge that if there is a timer, the override operation will not be carried out
        if(timer) {
            return;
        }
        timer = setTimeout(function(){
            // Obtain all selected IDS within 2 seconds and judge whether they are selected through the attribute isflag
            for(var i = 0,ilen = checkboxs.length; i < ilen; i++) {
                if(checkboxs[i].hasAttribute("isflag")) {
                    var id = checkboxs[i].getAttribute("data-id");
                    cache[cache.length] = id;
                }
            }
            mainFunc(cache.join(',')); // After 2 seconds, you need to pass all IDs to the ontology function
            // Clear timer
            clearTimeout(timer);
            timer = null;
            cache = [];
        },2000);
    }
})();
var checkboxs = document.getElementsByClassName("j-input");
for(var i = 0,ilen = checkboxs.length; i < ilen; i+=1) {
    (function(i){
        checkboxs[i].onclick = function(){
            if(this.checked) {
                // Add an attribute to the current
                this.setAttribute("isflag",1);
            }else {
                this.removeAttribute('isflag');
            }
            // Call proxy function
            proxyFunc(checkboxs);
        }
    })(i);
}

Understand cache proxy:

The meaning of cache agent is to cache the first run. When the same is run again, it is taken directly from the cache. The advantage of this is to avoid repeating the operation function. If the operation is very complex and expensive, using cache objects can improve the performance; We can first understand a simple cache column, which is the common addition and multiplication operations on the Internet. The code is as follows:

// Calculate multiplication
var mult = function(){
    var a = 1;
    for(var i = 0,ilen = arguments.length; i < ilen; i+=1) {
        a = a*arguments[i];
    }
    return a;
};
// Computational addition
var plus = function(){
    var a = 0;
    for(var i = 0,ilen = arguments.length; i < ilen; i+=1) {
        a += arguments[i];
    }
    return a;
}
// Surrogate function
var proxyFunc = function(fn) {
    var cache = {};  // Cache object
    return function(){
        var args = Array.prototype.join.call(arguments,',');
        if(args in cache) {
            return cache[args];   // Using cache proxy
        }
        return cache[args] = fn.apply(this,arguments);
    }
};
var proxyMult = proxyFunc(mult);
console.log(proxyMult(1,2,3,4)); // 24
console.log(proxyMult(1,2,3,4)); // Cache fetch 24

var proxyPlus = proxyFunc(plus);
console.log(proxyPlus(1,2,3,4));  // 10
console.log(proxyPlus(1,2,3,4));  // Cache fetch 10

5: Understand the responsibility chain model

The advantage is to eliminate the coupling between the sender and receiver of the request.

The responsibility company is composed of several different objects. The sender is the object that sends the request, and the receiver is the object in the chain that receives the request and processes or transmits it. Sometimes the request itself can also be an object, which encapsulates all data related to the operation. The basic implementation process is as follows:

1. The sender knows the first receiver in the chain and sends the request to this receiver.

2. Each receiver analyzes the request and either processes it or passes it down.

3. Each receiver knows that there is only one other object, that is, its success in the chain.

4. If no receiver processes the request, the request will leave the chain.

We can understand that the responsibility chain pattern is A chain composed of processing requests. Requests are passed between these objects in turn until an object that can process them is encountered. We call these objects nodes in the chain. For example, object A sends A request to object B. if object B does not process it, it will give the request to C. If Object C does not process it, it will give the request to D, and so on until an object can process the request. Of course, if no object processes the request, the request will leave the chain.

For example, if some outsourcing companies receive a project, it may be the person in charge of the project or the person at the manager level. After receiving the project, the manager does not develop it by himself, but directly hands it over to the project manager for development. The project manager is certainly not happy to develop it by himself. It will hand over the project to the following code farmers, Therefore, the coder will deal with it. If the coder does not deal with it, the project may die directly. However, after the final completion, the outsourcing company does not know who developed that part of these projects, and it does not know or care, What it cares about is that the project has been handed over to the outsourcing company and has been developed without any bug s; Therefore, the advantages of the responsibility chain model are here:

Eliminate the coupling between the sender of the request (the company that needs to outsource the project) and the receiver (the outsourcing company).

The following list illustrates the benefits of the responsibility chain:

Tmall will do lottery every year, for example, Alibaba wants to increase the payment of Bora, if each user recharges 500 yuan to Alipay, then 100% won 100 yuan in 11 Alipay.

Recharge 200 yuan to Alipay, then 100% can win 20 yuan of red packets, of course, if not recharge, then also can draw, but the probability is very low, basically can not be, of course, it is possible to get.

We can analyze several field values in the following code to judge:

1. Ordertype (recharge type): if the value is 1, it means that it is a user with a recharge of 500 yuan; if it is 2, it means that it is a user with a recharge of 200 yuan; if it is 3, it means that it is a user without a recharge.

2. Ispay (whether it has been successfully recharged): if the value is true, it indicates that it has been successfully recharged; otherwise,) it indicates that it has not been successfully recharged; Just buy it as an ordinary user.

3. Count (quantity); Ordinary users can get the coupon if they have a certain number of Raffles. Otherwise, they can't get the coupon.

// We usually write code to handle the following operations
var order =  function(orderType,isPay,count) {
    if(orderType == 1) {  // User recharge $500 to Alipay
        if(isPay == true) { // If the recharge is successful, 100% will win the prize
            console.log("Dear user, you have won a red envelope of 100 yuan");
        }else {
            // If the recharge fails, it will be treated as an ordinary user to process the winning information
            if(count > 0) {
                console.log("Dear user, you have drawn a 10 yuan coupon");
            }else {
                console.log("Dear users, please make persistent efforts");
            }
        }
    }else if(orderType == 2) {  // User recharge $200 to Alipay
        if(isPay == true) {     // If the recharge is successful, 100% will win the prize
            console.log("Dear user, you have won a red envelope of 20 yuan");
        }else {
            // If the recharge fails, it will be treated as an ordinary user to process the winning information
            if(count > 0) {
                console.log("Dear user, you have drawn a 10 yuan coupon");
            }else {
                console.log("Dear users, please make persistent efforts");
            }
        }
    }else if(orderType == 3) {
        // Ordinary users to process the winning information
        if(count > 0) {
            console.log("Dear user, you have drawn a 10 yuan coupon");
        }else {
            console.log("Dear users, please make persistent efforts");
        }
    }
};

Although the above code can meet the requirements, the code is not easy to expand and difficult to read. If I want one or two conditions in the future and I want to recharge 300 yuan successfully, I can win a red envelope of 150 yuan. At this time, I have to change the code inside. In this way, the coupling between business logic and code is relatively high, and I accidentally change the wrong code; At this time, we try to use the responsibility chain pattern to transfer objects in turn;

The following code:

function order500(orderType,isPay,count){
    if(orderType == 1 && isPay == true)    {
        console.log("Dear user, you have won a red envelope of 100 yuan");
    }else {
        // If you don't handle it yourself, pass it to the next object order200 for processing
        order200(orderType,isPay,count);
    }
};
function order200(orderType,isPay,count) {
    if(orderType == 2 && isPay == true) {
        console.log("Dear user, you have won a red envelope of 20 yuan");
    }else {
        // If you don't handle it yourself, pass it to the next object ordinary user for processing
        orderNormal(orderType,isPay,count);
    }
};
function orderNormal(orderType,isPay,count){
    // Ordinary users to process the winning information
    if(count > 0) {
        console.log("Dear user, you have drawn a 10 yuan coupon");
    }else {
        console.log("Dear users, please make persistent efforts");
    }
}

In the above code, we use three functions order500, order200 and orderNormal to handle our own business logic respectively. If the current self function cannot handle things, we pass them to the following functions for processing, and so on until a function can handle them. Otherwise, the responsibility chain mode directly leaves the chain and tells us that it cannot be handled, Throw an error message. Although the above code can be used as the responsibility chain mode, we can see from the above code that the order500 function relies on a function such as order200, so there must be this function, which also violates the {open closed principle in object-oriented. Let's continue to understand the flexible and separable responsibility chain nodes.

function order500(orderType,isPay,count){
    if(orderType == 1 && isPay == true)    {
        console.log("Dear user, you have won a red envelope of 100 yuan");
    }else {
        //I don't know who the next node is. Anyway, I pass the request back
        return "nextSuccessor";
    }
};
function order200(orderType,isPay,count) {
    if(orderType == 2 && isPay == true) {
        console.log("Dear user, you have won a red envelope of 20 yuan");
    }else {
        //I don't know who the next node is. Anyway, I pass the request back
        return "nextSuccessor";
    }
};
function orderNormal(orderType,isPay,count){
    // Ordinary users to process the winning information
    if(count > 0) {
        console.log("Dear user, you have drawn a 10 yuan coupon");
    }else {
        console.log("Dear users, please make persistent efforts");
    }
}
// Next, you need to write the encapsulated constructor method of the responsibility chain pattern
var Chain = function(fn){
    this.fn = fn;
    this.successor = null;
};
Chain.prototype.setNextSuccessor = function(successor){
    return this.successor = successor;
}
// Pass the request down
Chain.prototype.passRequest = function(){
    var ret = this.fn.apply(this,arguments);
    if(ret === 'nextSuccessor') {
        return this.successor && this.successor.passRequest.apply(this.successor,arguments);
    }
    return ret;
}
//Now we wrap the three functions into responsibility chain nodes:
var chainOrder500 = new Chain(order500);
var chainOrder200 = new Chain(order200);
var chainOrderNormal = new Chain(orderNormal);

// Then specify the order of nodes in the responsibility chain
chainOrder500.setNextSuccessor(chainOrder200);
chainOrder200.setNextSuccessor(chainOrderNormal);

//Finally, pass the request to the first node:
chainOrder500.passRequest(1,true,500);  // Dear user, you have won a red envelope of 100 yuan
chainOrder500.passRequest(2,true,500);  // Dear user, you have won a red envelope of 20 yuan
chainOrder500.passRequest(3,true,500);  // Dear user, you have drawn a 10 yuan coupon 
chainOrder500.passRequest(1,false,0);   // Dear users, please make persistent efforts

Above code; Write three functions order500, order200 and orderNormal respectively, and process their own business logic in the function. If their own function cannot handle it, return the string nextSuccessor , and pass it later. Then encapsulate the chain constructor, pass in an fn object, and have their own attribute success, There are two methods on the prototype: setnextsuccessor and passRequest;setNextSuccessor , this method specifies the order of nodes in the responsibility chain and saves the corresponding methods to this On the attribute success, chainorder500 setNextSuccessor(chainOrder200); chainOrder200. setNextSuccessor(chainOrderNormal); Specifies the order in the chain, so this The successor refers to the order200 method and the orderNormal method, so it is chainorder500 for the first time If passrequest (1, true, 500) is called, the order500 method is called to output directly, and chainorder500 is called for the second time passRequest(2,true,500); This method starts from the first node order500 in the chain. If it does not comply, it returns the success string, and then this successor && this.successor.passRequest.apply(this.successor,arguments); Execute this code; We said this above The attribute success refers to two methods {order200 and orderNormal respectively. Therefore, the method order200 is called, so the value is returned. This principle is followed by analogy. If we want to recharge the red envelope of 300 yuan in the future, we can write the order300 function, then list the chain chain and wrap it, and specify the order in the responsibility chain. There is no need to deal with the business logic inside;

Understand asynchronous responsibility chain

The above is only the synchronization responsibility Chain. We let each node function synchronously return a specific value "nextSuccessor" to indicate whether to pass the request to the next node. We often encounter ajax asynchronous requests in our development. After the request is successful, we need to do something. At this time, if we apply the above synchronization request again, it will not take effect, Let's understand how to use asynchronous responsibility Chain to solve this problem; We add a prototype method to the Chain class prototype. Next means to manually pass the request to the next node in the responsibility Chain.

The following code:

function Fn1() {
    console.log(1);
    return "nextSuccessor";
}
function Fn2() {
    console.log(2);
    var self = this;
    setTimeout(function(){
        self.next();
    },1000);
}
function Fn3() {
    console.log(3);
}
// Next, you need to write the encapsulated constructor method of the responsibility chain pattern
var Chain = function(fn){
    this.fn = fn;
    this.successor = null;
};
Chain.prototype.setNextSuccessor = function(successor){
    return this.successor = successor;
}
// Pass the request down
Chain.prototype.passRequest = function(){
    var ret = this.fn.apply(this,arguments);
    if(ret === 'nextSuccessor') {
        return this.successor && this.successor.passRequest.apply(this.successor,arguments);
    }
    return ret;
}
Chain.prototype.next = function(){
    return this.successor && this.successor.passRequest.apply(this.successor,arguments);
}
//Now we wrap the three functions into responsibility chain nodes:
var chainFn1 = new Chain(Fn1);
var chainFn2 = new Chain(Fn2);
var chainFn3 = new Chain(Fn3);

// Then specify the order of nodes in the responsibility chain
chainFn1.setNextSuccessor(chainFn2);
chainFn2.setNextSuccessor(chainFn3);

chainFn1.passRequest();  // Print out 1, 2, and print out 3 after 1 second

Call the function {chainfn1 passRequest(); After that, the sender Fn1 function will be executed to print out 1, and then the string "nextSuccessor" will be returned;

Then execute return this successor && this.successor.passRequest.apply(this.successor,arguments); This function to Fn2, print 2, and then there is a setTimeout timer asynchronous function, which needs to send the request to the next node in the responsibility chain, so 3 will be printed after one second;

The advantages of the responsibility chain model are:

 1. It decouples the complex relationship between the request sender and N receivers. You don't need to know which node in the chain can handle your request, so you

Just pass the request to the first node.

 2. The node objects in the chain can be flexibly split and reorganized. It is very simple to add or delete a node, or change the position of the node.

 3. We can also manually specify the starting position of the node, which does not mean that we have to start from the actual node

Disadvantages: there are a little more node objects in the responsibility chain mode. Most nodes may not play a substantive role in a request process, and their role is to make

The request is passed down. In terms of performance, avoid too long responsibility chain and improve performance.

6: Understanding of command patterns

A command in command mode refers to an instruction that performs certain things.

The usage scenarios of the command mode are: sometimes it is necessary to send a request to some objects, but you do not know who the receiver of the request is or what the operation of the request is. At this time, you want to design the program code in a loosely coupled way; Make the request sender and the request receiver eliminate the coupling relationship in each other's code.

Let's first list a list in our life to illustrate the command mode: for example, we often buy things on tmall and then place orders. After placing an order, I want to receive the goods and hope that the goods are true. For users, it doesn't care how the seller delivers after placing an order. Of course, the seller also has time to deliver goods, such as within 24 hours, Users don't care who the express is sent to. Of course, some people will care about what the express delivery is; For users, as long as they deliver the goods within the specified time and generally receive the goods within a considerable time, of course, the command mode also includes cancellation orders and redo orders. For example, after we place an order, I suddenly don't want to buy it. I can cancel the order or re order before delivery (that is, redo orders); For example, my clothes are taken in the wrong size. I cancel the order and take a large one again.

1. Columns of command mode

I remember when I first worked as a front-end, that is, the first company I just graduated from, and entered a company that did outsourcing projects. The company generally outsourced Taobao activity pages and Tencent game pages. At that time, we should call cutting the front-end of the page and be responsible for doing some html and css work. Therefore, when I worked as Tencent game pages, I often helped them make static pages, For example, when we put a few buttons on the page, we just help Tencent make the style of the game according to the design draft, such as the buttons on the page, such as how to operate the specific buttons and what will happen after clicking the buttons. We don't know. We don't know what their business is. Of course, we know that there will be click events, We don't know what business to deal with. Here we can use the command mode: after clicking the button, we must send a request to some objects responsible for specific behavior, and these objects are the recipients of the request. However, at present, we do not know what the receiver is or what the receiver will do. At this time, we can use the command mode to eliminate the code coupling relationship between the sender and the receiver.

We first use the traditional object-oriented pattern to design the code:

hypothesis html The structure is as follows:
<button id="button1">Refresh menu directory</button>
<button id="button2">Add submenu</button>
<button id="button3">Delete submenu</button>

JS code is as follows:

var b1 = document.getElementById("button1"),
     b2 = document.getElementById("button2"),
     b3 = document.getElementById("button3");
     
 // Define the setCommand function, which is responsible for installing commands on the button. After clicking the button, the execute() method of the command object is executed.
 var setCommand = function(button,command){
    button.onclick = function(){
        command.execute();
    }
 };
 // Let's define each object to complete our business operations
 var MenuBar = {
    refersh: function(){
        alert("Refresh menu directory");
    }
 };
 var SubMenu = {
    add: function(){
        alert("Add submenu");
    },
    del: function(){
        alert("Delete submenu");
    }
 };
 // The following is the writing command class
 var RefreshMenuBarCommand = function(receiver){
    this.receiver = receiver;
 };
 RefreshMenuBarCommand.prototype.execute = function(){
    this.receiver.refersh();
 }
 // Add command operation
 var AddSubMenuCommand = function(receiver) {
    this.receiver = receiver;
 };
 AddSubMenuCommand.prototype.execute = function() {
    this.receiver.add();
 }
 // Delete command action
 var DelSubMenuCommand = function(receiver) {
    this.receiver = receiver;
 };
 DelSubMenuCommand.prototype.execute = function(){
    this.receiver.del();
 }
 // Finally, the command receiver is passed into the command object, and the command object is installed on the button
 var refershBtn = new RefreshMenuBarCommand(MenuBar);
 var addBtn = new AddSubMenuCommand(SubMenu);
 var delBtn = new DelSubMenuCommand(SubMenu);
 
 setCommand(b1,refershBtn);
 setCommand(b2,addBtn);
 setCommand(b3,delBtn);

From the above command class code, we can see that any operation has an execute method to execute the operation; The above code uses the traditional object-oriented programming to realize the command mode. The procedural request call of the command mode is encapsulated in the execute method of the command object. Did we find the above code a little cumbersome? We can use the callback function in javascript to do these things. In object-oriented, the receiver of command mode is saved as the attribute of command object, and the operation of executing command is agreed to call command Execute method, but if we use the callback function, the receiver will be enclosed in the environment where the callback function is generated, and the operation will be easier. Just execute the callback function. Let's see the code below:

The code is as follows:

var setCommand = function(button,func) {
    button.onclick = function(){
        func();
    }
 }; 
 var MenuBar = {
    refersh: function(){
        alert("Refresh menu interface");
    }
 };
 var SubMenu = {
    add: function(){
        alert("Add menu");
    }
 };
 // Refresh menu
 var RefreshMenuBarCommand = function(receiver) {
    return function(){
        receiver.refersh();    
    };
 };
 // Add menu
 var AddSubMenuCommand = function(receiver) {
    return function(){
        receiver.add();    
    };
 };
 var refershMenuBarCommand = RefreshMenuBarCommand(MenuBar);
 // Add menu
 var addSubMenuCommand = AddSubMenuCommand(SubMenu);
 setCommand(b1,refershMenuBarCommand);
 
 setCommand(b2,addSubMenuCommand);

We can also use javascript callback functions to code as follows:

// Four button click events on the following code
var b1 = document.getElementById("button1"),
    b2 = document.getElementById("button2"),
    b3 = document.getElementById("button3"),
    b4 = document.getElementById("button4");
/*
 bindEnv The function is responsible for installing the click command on the button. After clicking the button, it will call
 function
 */
var bindEnv = function(button,func) {
    button.onclick = function(){
        func();
    }
};
// Now let's write the code to deal with the business logic
var Todo1 = {
    test1: function(){
        alert("I'm here for the first test");
    }    
};
// Add, delete and modify operations in business
var Menu = {
    add: function(){
        alert("I'm here to handle some adding operations");
    },
    del: function(){
        alert("I'm here to handle some deletion operations");
    },
    update: function(){
        alert("I'm here to handle some update operations");
    }
};
// Call function
bindEnv(b1,Todo1.test1);
// Add button
bindEnv(b2,Menu.add);
// Delete button
bindEnv(b3,Menu.del);
// change button
bindEnv(b4,Menu.update);

2. Understand macro commands:

A macro command is a set of commands. By executing macro commands, you can execute a batch of commands at a time.

In fact, it is similar to putting all the functions and methods of the page in an array, and then traversing the array in turn

To execute the method.

The code is as follows:

var command1 = {
    execute: function(){
        console.log(1);
    }
}; 
var command2 = {
    execute: function(){
        console.log(2);
    }
};
var command3 = {
    execute: function(){
        console.log(3);
    }
};
// Define macro commands, command The add method adds a subcommand to the macro command object,
// When the execute method of the macro command object is called, this group of command objects will be iterated,
// And execute their execute methods in turn.
var command = function(){
    return {
        commandsList: [],
        add: function(command){
            this.commandsList.push(command);
        },
        execute: function(){
            for(var i = 0,commands = this.commandsList.length; i < commands; i+=1) {
                this.commandsList[i].execute();
            }
        }
    }
};
// Initialize macro command
var c = command();
c.add(command1);
c.add(command2);
c.add(command3);
c.execute();  // 1,2,3

7: Understanding policy patterns in javascript

1. Understand the policy pattern in javascript

The definition of policy pattern is to define a series of algorithms, encapsulate them one by one, and make them replace each other.

The advantages of using policy mode are as follows:

Advantages: 1 The strategy pattern uses combination, delegation and other technologies and ideas to effectively avoid many if conditional statements.

      2. The policy pattern provides the open close principle, which makes the code easier to understand and extend.

      3. The code in the policy pattern can be reused.

1: Use the strategy mode to calculate the bonus;

The following demo is what I saw in the book, but it doesn't matter. We just want to understand the use of the strategy mode. We can use the strategy mode to calculate the bonus problem;

For example, the company's year-end bonus is assessed according to the employee's salary and performance. For those with performance A, the year-end bonus is 4 times the salary, for those with performance B, the year-end bonus is 3 times the salary, and for those with performance C, the year-end bonus is 2 times the salary; Now we use the general coding method to write the code as follows:

var calculateBouns = function(salary,level) {
    if(level === 'A') {
        return salary * 4;
    }
    if(level === 'B') {
        return salary * 3;
    }
    if(level === 'C') {
        return salary * 2;
    }
};
// Call as follows:
console.log(calculateBouns(4000,'A')); // 16000
console.log(calculateBouns(2500,'B')); // 7500

The first parameter is salary and the second parameter is grade;

The disadvantages of the code are as follows:

The calculatebounds() function contains many if else statements.

The calculatesprings , function is inflexible. if there is a level D, we need to add an if statement to judge level D in the calculatesprings , function;

The reusability of the algorithm is poor. If there are similar algorithms in other places, but the rules are different, our codes can not be universal.

2. Refactoring code with composite functions

Combinatorial function encapsulates various algorithms into small functions. For example, if the level is A, it encapsulates A small function. If the level is B, it also encapsulates A small function, and so on; The following code:

var performanceA = function(salary) {
    return salary * 4;
};
var performanceB = function(salary) {
    return salary * 3;
};
        
var performanceC = function(salary) {
    return salary * 2
};
var calculateBouns = function(level,salary) {
    if(level === 'A') {
        return performanceA(salary);
    }
    if(level === 'B') {
        return performanceB(salary);
    }
    if(level === 'C') {
        return performanceC(salary);
    }
};
// Call as follows
console.log(calculateBouns('A',4500)); // 18000

The code looks a little better, but it still has the following disadvantages:

The calculatebounds} function may become larger and larger, for example, when increasing the D level, and it lacks elasticity.

3. Refactor the code using the policy pattern

Policy pattern refers to} defining a series of algorithms, encapsulating them one by one, and separating the unchanged part from the changed part. In fact, it is to separate the use and implementation of the algorithm; The usage of the algorithm is unchanged. The calculated bonus is obtained according to an algorithm, and the implementation of the algorithm corresponds to different performance rules according to performance;

A program based on policy pattern consists of at least two parts. The first part is a group of policy classes, which encapsulates the specific algorithm and is responsible for the specific calculation process. The second part is the environment class Context, which receives the client's request and then delegates the request to a policy class. We first use the traditional object-oriented to implement;

The following code:

var performanceA = function(){};
performanceA.prototype.calculate = function(salary) {
    return salary * 4;
};      
var performanceB = function(){};
performanceB.prototype.calculate = function(salary) {
    return salary * 3;
};
var performanceC = function(){};
performanceC.prototype.calculate = function(salary) {
    return salary * 2;
};
// Bonus category
var Bouns = function(){
    this.salary = null;    // Original salary
    this.levelObj = null;  // Policy object corresponding to performance level
};
Bouns.prototype.setSalary = function(salary) {
    this.salary = salary;  // Save employee's original salary
};
Bouns.prototype.setlevelObj = function(levelObj){
    this.levelObj = levelObj;  // Set the policy object corresponding to employee performance level
};
// Amount of bonus obtained
Bouns.prototype.getBouns = function(){
    // Delegate the bonus calculation operation to the corresponding policy object
    return this.levelObj.calculate(this.salary);
};
var bouns = new Bouns();
bouns.setSalary(10000);
bouns.setlevelObj(new performanceA()); // Set policy object
console.log(bouns.getBouns());  // 40000
       
bouns.setlevelObj(new performanceB()); // Set policy object
console.log(bouns.getBouns());  // 30000

The above code uses the policy pattern to reconstruct the code. You can see that the code responsibilities are updated clearly and the code becomes clearer.

4. Policy mode of Javascript version

//The code is as follows:
var obj = {
        "A": function(salary) {
            return salary * 4;
        },
        "B" : function(salary) {
            return salary * 3;
        },
        "C" : function(salary) {
            return salary * 2;
        } 
};
var calculateBouns =function(level,salary) {
    return obj[level](salary);
};
console.log(calculateBouns('A',10000)); // 40000

You can see that the code is simpler and clearer;

Policy pattern refers to defining a series of algorithms and encapsulating them. However, policy pattern not only encapsulates algorithms, but also encapsulates a series of business rules. As long as these business rules have the same objectives, we can use policy pattern to encapsulate them;

Form validation

For example, we often perform form verification, such as registering the login dialog box. We need to perform verification before logging in: for example, there are the following logic:

User name cannot be empty

The password length cannot be less than 6 digits.

The mobile phone number must conform to the format.

For example, the HTML code is as follows:

<form action = "http://www.baidu.com" id="registerForm" method = "post">
        <p>
            <label>Please enter user name:</label>
            <input type="text" name="userName"/>
        </p>
        <p>
            <label>Please input a password:</label>
            <input type="text" name="password"/>
        </p>
        <p>
            <label>Please enter your mobile phone number:</label>
            <input type="text" name="phoneNumber"/>
        </p>
</form>

We normally write the form verification code as follows:

var registerForm = document.getElementById("registerForm");
registerForm.onsubmit = function(){
    if(registerForm.userName.value === '') {
        alert('User name cannot be empty');
        return;
    }
    if(registerForm.password.value.length < 6) {
        alert("The length of the password cannot be less than 6 digits");
        return;
    }
    if(!/(^1[3|5|8][0-9]{9}$)/.test(registerForm.phoneNumber.value)) {
        alert("Incorrect mobile phone number format");
        return;
    }
}

However, writing code in this way has the following disadvantages:

1.registerForm. The onsubmit function is relatively large, and the code contains many if statements;

2.registerForm. The onsubmit function lacks flexibility. If a new validation rule is added, or if we want to change the password length validation from 6 to 8, we must change registerform The code inside the onsubmit function. Violation of the open closed principle.

3. The reusability of the algorithm is poor. If another form is added to the program and this form also needs some similar validation, we may need to copy the code again;

Next, we can use the policy pattern to reconstruct the form validation;

The first step is to encapsulate the policy object; The following code:

var strategy = {
    isNotEmpty: function(value,errorMsg) {
        if(value === '') {
            return errorMsg;
        }
    },
    // Limit minimum length
    minLength: function(value,length,errorMsg) {
        if(value.length < length) {
            return errorMsg;
        }
    },
    // Mobile number format
    mobileFormat: function(value,errorMsg) {
        if(!/(^1[3|5|8][0-9]{9}$)/.test(value)) {
            return errorMsg;
        }
    } 
};

Next, we are going to implement the Validator class. Here, the Validator class is used as the Context to receive the user's request and delegate it to the strategy} object, as shown in the following code:

var Validator = function(){
    this.cache = [];  // Preservation validation rules
};
Validator.prototype.add = function(dom,rule,errorMsg) {
    var str = rule.split(":");
    this.cache.push(function(){
        // str returns minLength:6 
        var strategy = str.shift();
        str.unshift(dom.value); // Add the value of input to the parameter list
        str.push(errorMsg);  // Add errorMsg to the parameter list
        return strategys[strategy].apply(dom,str);
    });
};
Validator.prototype.start = function(){
    for(var i = 0, validatorFunc; validatorFunc = this.cache[i++]; ) {
        var msg = validatorFunc(); // Start validation and obtain the returned information after validation
        if(msg) {
            return msg;
        }
    }
};

Here, the validator class acts as a Context, which is responsible for receiving the user's request and delegating it to the strategys object. In the above code, we first create a validator object, and then use validator The add method adds some validation rules to the validator object, validator The add method receives three parameters, as shown in the following code:

validator.add(registerForm.password,'minLength:6', 'password length cannot be less than 6 digits');

registerForm.password is the dom node of the valid input box;

minLength:6: a string separated by a colon. minLength in front of the colon represents the strategys object selected by the customer. The number 6 after the colon represents the parameters that must be verified in the validation process. minLength:6 means validate registerform Password , the minimum length of value in the text input box is 6 bits; If the string does not contain a colon, it indicates that no additional validation information is required in the validation process;

The third parameter is the error information returned when the validation fails;

After adding a series of validation rules to the validator object, we will call validator Start () method to start validation. If validator Start() returns an errorMsg string as the return value, indicating that the validation failed. At this time, registerform is required The onsubmit method returns false to prevent form submission. Let's take a look at the initialization code as follows:

var validateFunc = function(){
    var validator = new Validator(); // Create a Validator object
    /* Add some validation rules */
    validator.add(registerForm.userName,'isNotEmpty','User name cannot be empty');
    validator.add(registerForm.password,'minLength:6','Password length cannot be less than 6 digits');
    validator.add(registerForm.userName,'mobileFormat','Incorrect mobile phone number format');

    var errorMsg = validator.start(); // Obtain valid results
    return errorMsg; // Return validation results
};
var registerForm = document.getElementById("registerForm");
registerForm.onsubmit = function(){
    var errorMsg = validateFunc();
    if(errorMsg){
        alert(errorMsg);
        return false;
    }
}

Here are all the codes:

var strategys = {
    isNotEmpty: function(value,errorMsg) {
        if(value === '') {
            return errorMsg;
        }
    },
    // Limit minimum length
    minLength: function(value,length,errorMsg) {
        if(value.length < length) {
            return errorMsg;
        }
    },
    // Mobile number format
    mobileFormat: function(value,errorMsg) {
        if(!/(^1[3|5|8][0-9]{9}$)/.test(value)) {
            return errorMsg;
        }
    } 
};
var Validator = function(){
    this.cache = [];  // Preservation validation rules
};
Validator.prototype.add = function(dom,rule,errorMsg) {
    var str = rule.split(":");
    this.cache.push(function(){
        // str returns minLength:6 
        var strategy = str.shift();
        str.unshift(dom.value); // Add the value of input to the parameter list
        str.push(errorMsg);  // Add errorMsg to the parameter list
        return strategys[strategy].apply(dom,str);
    });
};
Validator.prototype.start = function(){
    for(var i = 0, validatorFunc; validatorFunc = this.cache[i++]; ) {
        var msg = validatorFunc(); // Start validation and obtain the returned information after validation
        if(msg) {
            return msg;
        }
    }
};

var validateFunc = function(){
    var validator = new Validator(); // Create a Validator object
    /* Add some validation rules */
    validator.add(registerForm.userName,'isNotEmpty','User name cannot be empty');
    validator.add(registerForm.password,'minLength:6','Password length cannot be less than 6 digits');
    validator.add(registerForm.userName,'mobileFormat','Incorrect mobile phone number format');

    var errorMsg = validator.start(); // Obtain valid results
    return errorMsg; // Return validation results
};
var registerForm = document.getElementById("registerForm");
registerForm.onsubmit = function(){
    var errorMsg = validateFunc();
    if(errorMsg){
        alert(errorMsg);
        return false;
    }
};

As mentioned above, we can see the benefits of using the policy mode to write the form validation code. We have completed the validation of a form through add configuration; In this way, the code can be used as a component and can be called at any time. It is also very convenient when modifying the form verification rules. It can be called by passing parameters;

Add multiple validation rules to a text input box. We can see from the above code that we can only correspond to one validation rule for the input box. For example, we can only verify whether the input box is empty, validator Add (registerform. Username, 'isnotempty', 'username cannot be empty'); However, if we want to verify whether the input box is empty and the length of the input box is not less than 10 bits, we expect to pass parameters as follows:

validator.add(registerForm.userName,[{strategy: 'isNotEmpty', errorMsg: 'user name cannot be empty'}, {strategy: 'minLength:6',errorMsg: 'user name length cannot be less than 6 bits'}])

We can write the following code:

// Policy object
var strategys = {
    isNotEmpty: function(value,errorMsg) {
        if(value === '') {
            return errorMsg;
        }
    },
    // Limit minimum length
    minLength: function(value,length,errorMsg) {
        if(value.length < length) {
            return errorMsg;
        }
    },
    // Mobile number format
    mobileFormat: function(value,errorMsg) {
        if(!/(^1[3|5|8][0-9]{9}$)/.test(value)) {
            return errorMsg;
        }
    } 
};
var Validator = function(){
    this.cache = [];  // Preservation validation rules
};
Validator.prototype.add = function(dom,rules) {
    var self = this;
    for(var i = 0, rule; rule = rules[i++]; ){
        (function(rule){
            var strategyAry = rule.strategy.split(":");
            var errorMsg = rule.errorMsg;
            self.cache.push(function(){
                var strategy = strategyAry.shift();
                strategyAry.unshift(dom.value);
                strategyAry.push(errorMsg);
                return strategys[strategy].apply(dom,strategyAry);
            });
        })(rule);
    }
};
Validator.prototype.start = function(){
    for(var i = 0, validatorFunc; validatorFunc = this.cache[i++]; ) {
    var msg = validatorFunc(); // Start validation and obtain the returned information after validation
    if(msg) {
        return msg;
    }
    }
};
// Code call
var registerForm = document.getElementById("registerForm");
var validateFunc = function(){
    var validator = new Validator(); // Create a Validator object
    /* Add some validation rules */
    validator.add(registerForm.userName,[
        {strategy: 'isNotEmpty',errorMsg:'User name cannot be empty'},
        {strategy: 'minLength:6',errorMsg:'The length of user name cannot be less than 6 characters'}
    ]);
    validator.add(registerForm.password,[
        {strategy: 'minLength:6',errorMsg:'Password length cannot be less than 6 digits'},
    ]);
    validator.add(registerForm.phoneNumber,[
        {strategy: 'mobileFormat',errorMsg:'Incorrect mobile phone number format'},
    ]);
    var errorMsg = validator.start(); // Obtain valid results
    return errorMsg; // Return validation results
};
// Click OK to submit
registerForm.onsubmit = function(){
    var errorMsg = validateFunc();
    if(errorMsg){
        alert(errorMsg);
        return false;
    }
}

Note: the above codes are all written according to the book. They are all the codes for reading the book. The most important thing is that we understand the implementation of the strategy mode. For example, the form verification function above is encapsulated in this way. We usually use the jquery plug-in to encapsulate the form verification code in this way. Therefore, we can also use this way to encapsulate forms and other learning in the future;

8: Understanding publish subscribe mode in Javascript

1. Introduction to publish subscribe mode

Publish subscribe mode is also called observer mode. It defines a one to many relationship between objects, allowing multiple observer objects to listen to a topic object at the same time. When an object changes, all objects that depend on it will be notified.

Publish subscribe mode in real life;

For example, Xiaohong recently saw a pair of shoes on Taobao. However, after contacting the seller, she found that the shoes were sold out, but Xiaohong liked the shoes very much. Therefore, she contacted the seller and asked when the seller would have the goods. The seller told her that it would take a week to have the goods. The seller told Xiaohong that if you like, you can collect our store, I'll let you know when the goods are available, so Xiaohong collects the shop, but at the same time, Xiaoming and Xiaohua also like the shoes and collect the shop; They will be notified in turn when the goods arrive;

In the above story, we can see that it is a typical publish and subscribe mode. The seller belongs to the publisher, and Xiaohong, Xiaoming, etc. belong to the subscriber. Subscribe to the store. The seller, as the publisher, will notify Xiaoming, Xiaohong, etc. in turn when the shoes arrive, and use Wangwang and other tools to publish messages to them in turn;

Advantages of publish subscribe mode:

  1. It supports simple broadcast communication. When the object state changes, it will automatically notify the subscribed objects.

For example, Liezi, Xiaoming and Xiaohong above do not need to visit Taobao every day to see if the shoes have arrived. At the appropriate time point, the publisher (seller) will notify the subscriber (Xiaohong, Xiaoming, etc.).

  2. The coupling between the publisher and the subscriber is reduced. The publisher only publishes a message. It doesn't care how the message is used by the subscriber. At the same time, the subscriber only listens to the publisher's event name. As long as the publisher's event name remains unchanged, it doesn't care how the publisher changes; Similarly, the seller (publisher) only needs to tell the subscriber (buyer) about the arrival of shoes. He doesn't care whether the buyer buys or not, or buys from other sellers. As long as the shoes arrive, you can notify the subscriber.

For the first point, we often use it in our daily work. For example, our ajax requests have success and error callback functions. We can subscribe to ajax success and error events. We don't care about the state of objects running asynchronously. We only care about success or error. We need to do our own things~

Disadvantages of publish subscribe mode:

Creating subscribers takes time and memory.

Although it can weaken the relationship between objects, if overused, it will make the code difficult to understand and maintain.

2. How to implement publish subscribe mode?

   1. First, think about who is the publisher (such as the seller above).

   2. Then add a cache list to the publisher to store the callback function to notify the subscriber (for example, the buyer above collects the seller's store, and the seller collects a list of the store).

   3. Finally, publish the message. The publisher traverses the cache list and triggers the subscriber callback function stored in it in turn.

We can also add some parameters to the callback function, such as shoe color, shoe size and other information;

Let's first implement a simple publish subscribe mode; The code is as follows:

var shoeObj = {}; // Define publisher
shoeObj.list = []; // The cache list stores the subscriber callback function
        
// Add subscriber
shoeObj.listen = function(fn) {
    shoeObj.list.push(fn);  // Add subscription message to cache list
}

// Release news
shoeObj.trigger = function(){
    for(var i = 0,fn; fn = this.list[i++];) {
        fn.apply(this,arguments); 
    }
}
// Xiao Hong subscribes to the following message
shoeObj.listen(function(color,size){
    console.log("The color is:"+color);
    console.log("Size:"+size);  
});

// Xiaohua subscribes to the following message
shoeObj.listen(function(color,size){
    console.log("The color to print again is:"+color);
    console.log("Print the size again:"+size); 
});
shoeObj.trigger("gules",40);
shoeObj.trigger("black",42);

Print the screenshot above. We can see that the subscriber receives every message from the publisher. However, for Xiaohong, she only wants to receive messages with red color and does not want to receive messages with black color. Therefore, we need to make the following modifications to the code. We can first add a key to make the subscriber subscribe to only messages of interest to herself. The code is as follows:

var shoeObj = {}; // Define publisher
shoeObj.list = []; // The cache list stores the subscriber callback function
        
// Add subscriber
shoeObj.listen = function(key,fn) {
    if(!this.list[key]) {
        // If you have not subscribed to such messages, create a cache list for such messages
        this.list[key] = []; 
    }
    this.list[key].push(fn);  // Add subscription message to cache list
}

// Release news
shoeObj.trigger = function(){
    var key = Array.prototype.shift.call(arguments); // Fetch message type name
    var fns = this.list[key];  // Get the collection of callback functions corresponding to the message

    // If you have not subscribed to the message, return
    if(!fns || fns.length === 0) {
        return;
    }
    for(var i = 0,fn; fn = fns[i++]; ) {
        fn.apply(this,arguments); // arguments is the parameter attached when publishing the message
    }
};

// Xiao Hong subscribes to the following message
shoeObj.listen('red',function(size){
    console.log("Size:"+size);  
});

// Xiaohua subscribes to the following message
shoeObj.listen('block',function(size){
    console.log("Print the size again:"+size); 
});
shoeObj.trigger("red",40);
shoeObj.trigger("block",42);

Let's run the above code and print it as follows:

You can see that subscribers only subscribe to the messages they are interested in;

3. Code encapsulation of publish subscribe mode

We know that for the above code, Xiao Hong subscribes to the shoeObj object such as buying shoes, but if we need to subscribe to buying a house or other objects in the future, we need to copy the above code and change the object code again; Therefore, we need code encapsulation;

The following code packages:

var event = {
    list: [],
    listen: function(key,fn) {
        if(!this.list[key]) {
            this.list[key] = [];
        }
        // Add subscribed messages to the cache list
        this.list[key].push(fn);
    },
    trigger: function(){
        var key = Array.prototype.shift.call(arguments);
        var fns = this.list[key];
        // If you have not subscribed to the message, return
        if(!fns || fns.length === 0) {
            return;
        }
        for(var i = 0,fn; fn = fns[i++];) {
            fn.apply(this,arguments);
        }
    }
};

We define another initEvent function, which enables all ordinary objects to have publish and subscribe functions, as shown in the following code:

var initEvent = function(obj) {
    for(var i in event) {
        obj[i] = event[i];
    }
};
// Let's test again. We still add the publish subscribe function to the shoeObj object;
var shoeObj = {};
initEvent(shoeObj);

// Xiao Hong subscribes to the following message
shoeObj.listen('red',function(size){
    console.log("Size:"+size);  
});

// Xiaohua subscribes to the following message
shoeObj.listen('block',function(size){
    console.log("Print the size again:"+size); 
});
shoeObj.trigger("red",40);
shoeObj.trigger("block",42);

4. How to unsubscribe from events?

For example, in the above Liezi, Xiao Hong suddenly doesn't want to buy shoes, so for the seller's store, he doesn't want to accept the news of the store, so Xiao Hong can cancel the subscription of the store.

The following code:

event.remove = function(key,fn){
    var fns = this.list[key];
    // If the message corresponding to the key has not been subscribed, the
    if(!fns) {
        return false;
    }
    // If no specific callback function is passed in, it means that all subscriptions to the message corresponding to the key need to be cancelled
    if(!fn) {
        fn && (fns.length = 0);
    }else {
        for(var i = fns.length - 1; i >= 0; i--) {
            var _fn = fns[i];
            if(_fn === fn) {
                fns.splice(i,1); // Delete subscriber's callback function
            }
        }
    }
};
// The test code is as follows:
var initEvent = function(obj) {
    for(var i in event) {
        obj[i] = event[i];
    }
};
var shoeObj = {};
initEvent(shoeObj);

// Xiao Hong subscribes to the following message
shoeObj.listen('red',fn1 = function(size){
    console.log("Size:"+size);  
});

// Xiaohua subscribes to the following message
shoeObj.listen('red',fn2 = function(size){
    console.log("Print the size again:"+size); 
});
shoeObj.remove("red",fn1);
shoeObj.trigger("red",42);

The operation results are as follows:

5. Global -- publish subscribe object code encapsulation

Let's take another look at our traditional ajax requests, such as our traditional ajax requests. After the request is successful, we need to do the following:

 1. Render data.

 2. Use the data to make an animation.

Then we must have written the code as follows:

$.ajax("http://127.0.0.1/index.php",function(data){
    rendedData(data);  // Render data
    doAnimate(data);  // Realize animation 
});

If we need to do something in the future, we also need to write the calling method in it; In this way, the code is highly coupled. Now let's use the publish subscribe mode to see how to reconstruct the above business requirements code;

Copy code
$.ajax("http://127.0.0.1/index.php",function(data){
    Obj.trigger('success',data);  // Message after successful publishing request
});
// Let's subscribe to this message. For example, I subscribe to the message of rendering data now;
Obj.listen("success",function(data){
   renderData(data);
});
// Subscribe to this message
Obj.listen("success",function(data){
   doAnimate(data); 
});

Therefore, we can encapsulate a global publish subscribe pattern object; The following code:

var Event = (function(){
    var list = {},
          listen,
          trigger,
          remove;
          listen = function(key,fn){
            if(!list[key]) {
                list[key] = [];
            }
            list[key].push(fn);
        };
        trigger = function(){
            var key = Array.prototype.shift.call(arguments),
                 fns = list[key];
            if(!fns || fns.length === 0) {
                return false;
            }
            for(var i = 0, fn; fn = fns[i++];) {
                fn.apply(this,arguments);
            }
        };
        remove = function(key,fn){
            var fns = list[key];
            if(!fns) {
                return false;
            }
            if(!fn) {
                fns && (fns.length = 0);
            }else {
                for(var i = fns.length - 1; i >= 0; i--){
                    var _fn = fns[i];
                    if(_fn === fn) {
                        fns.splice(i,1);
                    }
                }
            }
        };
        return {
            listen: listen,
            trigger: trigger,
            remove: remove
        }
})();
// The test code is as follows:
Event.listen("color",function(size) {
    console.log("Size:"+size); // Print out size 42
});
Event.trigger("color",42);

6. Understand inter module communication

We use the global publish subscribe object encapsulated above to realize the communication between the two modules; For example, there is a button on a page. After clicking this button every time, the total number of clicks of this button will be displayed in the div; The following code:

< button id = "count" > Click me < / button >

<div id="showcount"></div>

a.js among us is responsible for handling click operations and publishing messages; The following JS code:

var a = (function(){
    var count = 0;
    var button = document.getElementById("count");
    button.onclick = function(){
        Event.trigger("add",count++);
    }
})();

b.js is responsible for listening to the add message and displaying the total number of clicks on the page; The following code:

var b = (function(){
    var div = document.getElementById("showcount");
    Event.listen('add',function(count){
        div.innerHTML = count;
    });
})();

The following is the html code. JS can be referenced as follows:

<!doctype html>
<html lang="en">
 <head>
  <meta charset="UTF-8">
  <title>Document</title>
  <script src="global.js"></script>
 </head>
 <body>
    <button id="count">Point me</button>
    <div id="showcount"></div>
    <script src = "a.js"></script>
    <script src = "b.js"></script>
 </body>
</html>

In the above code, after clicking the button once, the div of showcount will automatically add 1. The above demonstrates how to use the publish subscribe mode for communication between the two modules;

Including global JS is the encapsulation code of the global publish subscribe mode object encapsulated above;

9: Understanding the mediator model

Let's first understand the problem. If the demand received by our front-end development is the demand given to us by the demand side, maybe one front-end development will deal with multiple demand parties, so it will maintain the contact of multiple demand parties. Then in the program, it means to maintain the reference of multiple objects. When the program is larger, there will be more and more objects, The relationship between them will become more and more complex. Now, if there is an intermediary (if it is our supervisor) to meet the needs of multiple demanders, the demander only needs to give all the requirements to our supervisor, and the supervisor will assign tasks to us according to our workload in turn. In this way, our front-end development does not need to contact multiple business parties, We only need to contact our supervisor (that is, intermediary), which weakens the coupling between objects.

Liezi in daily life:

The intermediary model is often encountered in our daily life. For example, we go to the housing intermediary to rent a house, and the housing intermediary forms an intermediary between the renter and the landlord lessor; The renter does not care who rents the house, and the landlord does not care who rents it to. Because there is an intermediary, it needs an intermediary to complete the transaction.

The role of the mediator mode is to decouple objects from each other. After adding a mediation object, all related objects communicate through the mediator object instead of referencing each other. Therefore, when an object sends changes, you only need to notify the mediator object. Mediators loose the coupling between objects and can change their interaction independently.

The following is the list of Intermediaries:

I don't know if you have ever played the game of hero killing. At the earliest time, heroes killed two people (the enemy and themselves respectively); For this game, we first use ordinary functions to realize the following:

For example, first define a function, which has three methods: win, lose, and die; As long as a player dies, the game is over and its opponent needs to be notified of victory; The code needs to be written as follows:

function Hero(name) {
    this.name = name;
    this.enemy = null; 
}
Hero.prototype.win = function(){
    console.log(this.name + 'Won');
}
Hero.prototype.lose = function(){
    console.log(this.name + 'lose');
}
Hero.prototype.die = function(){
    this.lose();
    this.enemy.win();
}
// Initialize 2 objects
var h1 = new Hero("Zhu Yuanzhang");
var h2 = new Hero("Bo Wen Liu");
// Set enemies for players
h1.enemy = h2;
h2.enemy = h1;
// Zhu Yuanzhang lost when he died
h1.die();  // Output Zhu Yuanzhang lose Liu Bowen Won

Now let's add teammates to the game

For example, now let's add teammates to the game. For example, if the hero kills a group of 6 people, there will be teammates and 3 enemies in this case; Therefore, we need to distinguish whether the enemy or teammates need the color of the team. If the color of the team is the same, it is the same team, otherwise it is the enemy;

We can first define an array of players to save all players. After creating players, cycle players to set teammates or enemies for each player;

var players = [];

Then we'll write the Hero function; The code is as follows:

var players = []; // Define an array to hold all players
function Hero(name,teamColor) {
    this.friends = [];    //Save teammate list
    this.enemies = [];    // Save enemy list
    this.state = 'live';  // Player status
    this.name = name;     // Role name
    this.teamColor = teamColor; // Team color
}
Hero.prototype.win = function(){
    // Win
    console.log("win:" + this.name);
};
Hero.prototype.lose = function(){
    // Lost
    console.log("lose:" + this.name);
};
Hero.prototype.die = function(){
    // All teammate deaths are alive by default
    var all_dead = true;
    this.state = 'dead'; // Set player status to death
    for(var i = 0,ilen = this.friends.length; i < ilen; i+=1) {
        // Traversal, if there is a teammate who is not dead, the game is not over
        if(this.friends[i].state !== 'dead') {
            all_dead = false; 
            break;
        }
    }
    if(all_dead) {
        this.lose();  // All teammates die, the game is over
        // Circular notification to all players of game failure
        for(var j = 0,jlen = this.friends.length; j < jlen; j+=1) {
            this.friends[j].lose();
        }
        // Notify all enemies of game victory
        for(var j = 0,jlen = this.enemies.length; j < jlen; j+=1) {
            this.enemies[j].win();
        }
    }
}
// Define a factory class to create players 
var heroFactory = function(name,teamColor) {
    var newPlayer = new Hero(name,teamColor);
    for(var i = 0,ilen = players.length; i < ilen; i+=1) {
        // If you're on the same team
        if(players[i].teamColor === newPlayer.teamColor) {
            // Add teammate list to each other
            players[i].friends.push(newPlayer);
            newPlayer.friends.push(players[i]);
        }else {
            // Add each other to the enemy list
            players[i].enemies.push(newPlayer);
            newPlayer.enemies.push(players[i]);
        }
    }
    players.push(newPlayer);
    return newPlayer;
};
        // Red team
var p1 = heroFactory("aa",'red'),
    p2 = heroFactory("bb",'red'),
    p3 = heroFactory("cc",'red'),
    p4 = heroFactory("dd",'red');
        
// Blue team
var p5 = heroFactory("ee",'blue'),
    p6 = heroFactory("ff",'blue'),
    p7 = heroFactory("gg",'blue'),
    p8 = heroFactory("hh",'blue');
// Let all red team players die
p1.die();
p2.die();
p3.die();
p4.die();
// lose:dd lose:aa lose:bb lose:cc
// win:ee win:ff win:gg win:hh

The above code: the Hero function has two parameters: name (player name) and teamcolor (team color),

First of all, we can judge whether it is a teammate or an enemy according to the color of the team; There are also three methods: win, lose, and die; If each time a person dies, whether all the dead teammates die in the cycle. If all the teammates die, they lose. Therefore, it is necessary to cycle their teammates and tell the members of each teammate that they lose. At the same time, it is necessary to cycle their enemies and tell their enemies that they win; Therefore, every time a person dies, it needs to cycle to judge whether all his teammates are dead; Therefore, each player is tightly coupled with other players.

Next, we can use the mediator pattern to improve the above demo;

First, we still define the Hero constructor and Hero object prototype methods. In these prototype methods of Hero object, we are no longer responsible for the specific execution logic, but transfer the operation to the mediator object, which is responsible for doing specific things. We can name the mediator object playerDirector;

When the playerDirector opens an externally exposed interface ReceiveMessage, it is responsible for receiving messages sent by the player object. When the player object sends messages, it always sends its own this as a parameter to the playerDirector, so that the playerDirector # identifies which player object the message comes from.

The code is as follows:

var players = []; // Define an array to hold all players
function Hero(name,teamColor) {
    this.state = 'live';  // Player status
    this.name = name;     // Role name
    this.teamColor = teamColor; // Team color
}
Hero.prototype.win = function(){
    // Win
    console.log("win:" + this.name);
};
Hero.prototype.lose = function(){
    // Lost
    console.log("lose:" + this.name);
};
// death
Hero.prototype.die = function(){
    this.state = 'dead';
    // Send a message to the intermediary and the player dies
    playerDirector.ReceiveMessage('playerDead',this);
}
// Remove player
Hero.prototype.remove = function(){
    // Send a message to the mediator to remove a player
    playerDirector.ReceiveMessage('removePlayer',this);
};
// Players change teams
Hero.prototype.changeTeam = function(color) {
    // Send a message to the intermediary and players change teams
    playerDirector.ReceiveMessage('changeTeam',this,color);
};
// Define a factory class to create players 
var heroFactory = function(name,teamColor) {
    // Create a new player object
    var newHero = new Hero(name,teamColor);
    // Send messages to intermediaries to add players
    playerDirector.ReceiveMessage('addPlayer',newHero);
    return newHero;
};
var playerDirector = (function(){
    var players = {},  // Save all players
        operations = {}; // Actions that intermediaries can perform
    // Add a player action
    operations.addPlayer = function(player) {
        // Get the color of player teammates
        var teamColor = player.teamColor;
        // If the player of this color does not have a team, a new team will be established
        players[teamColor] = players[teamColor] || [];
        // Add players to the team
        players[teamColor].push(player);
     };
    // Remove a player
    operations.removePlayer = function(player){
        // Get the color of the team
        var teamColor = player.teamColor,
        // Get all members of the team
        teamPlayers = players[teamColor] || [];
        // ergodic
        for(var i = teamPlayers.length - 1; i>=0; i--) {
            if(teamPlayers[i] === player) {
                teamPlayers.splice(i,1);
            }
        }
    };
    // Players change teams
    operations.changeTeam = function(player,newTeamColor){
        // First, delete from the original team
        operations.removePlayer(player);
        // Then change the color of the team
        player.teamColor = newTeamColor;
        // Add to team
        operations.addPlayer(player);
    };
    // Player death
operations.playerDead = function(player) {
    var teamColor = player.teamColor,
    // Player's team
    teamPlayers = players[teamColor];

    var all_dead = true;
    //ergodic 
    for(var i = 0,player; player = teamPlayers[i++]; ) {
        if(player.state !== 'dead') {
            all_dead = false;
            break;
        }
    }
    // If all_ If dead is true, it means all are dead
    if(all_dead) {
        for(var i = 0, player; player = teamPlayers[i++]; ) {
            // All players of our team lose
            player.lose();
        }
        for(var color in players) {
            if(color !== teamColor) {
                // It means this is another team
                // Get the players of this team
                var teamPlayers = players[color];
                for(var i = 0,player; player = teamPlayers[i++]; ) {
                    player.win(); // Traversal notifies other players of win
                }
            }
        }
    }
};
var ReceiveMessage = function(){
    // The first parameter of arguments gets the first parameter for the message name
    var message = Array.prototype.shift.call(arguments);
    operations[message].apply(this,arguments);
};
return {
    ReceiveMessage : ReceiveMessage
};
})();
// Red team
var p1 = heroFactory("aa",'red'),
    p2 = heroFactory("bb",'red'),
    p3 = heroFactory("cc",'red'),
        p4 = heroFactory("dd",'red');
        
    // Blue team
    var p5 = heroFactory("ee",'blue'),
        p6 = heroFactory("ff",'blue'),
        p7 = heroFactory("gg",'blue'),
        p8 = heroFactory("hh",'blue');
    // Let all red team players die
    p1.die();
    p2.die();
    p3.die();
    p4.die();
    // lose:aa lose:bb lose:cc lose:dd 
   // win:ee win:ff win:gg win:hh

We can see the above code; The coupling code between players has been removed, and all logical operations are processed in the intermediary object. Any operation of a player does not need to traverse to notify other players, but just need to send a message to the intermediary, and the intermediary will process it after receiving the message, After processing the message, it will feed back the processing results to other player objects. Using the mediator pattern to decouple the code between objects; Make the program more flexible

The intermediary mode implements the list of purchased goods

The following Liezi are Liezi in the book. For example, Liezi in Taobao or tmall is not implemented in this way. It doesn't matter. We can change it. We'll mainly learn the idea of using the intermediary mode.

First, let's introduce the business: in the purchase process, you can select the color of the mobile phone and enter the purchase quantity. At the same time, there are two display areas in the page to display the color and quantity just selected by the user. There is also a button to dynamically display the next operation. We need to query the inventory corresponding to the color mobile phone. If the inventory quantity is less than the purchase quantity this time, the button will be disabled and the copy with insufficient inventory will be displayed. On the contrary, the button will be highlighted and can be clicked and the shopping cart will be displayed.

The HTML code is as follows:

Select color:
    <select id="colorSelect">
        <option value="">Please select</option>
        <option value="red">gules</option>
        <option value="blue">blue</option>
    </select>
    <p>Enter the quantity purchased: <input type="text" id="numberInput"/></p>
    Color you selected:<div id="colorInfo"></div>
    <p>Quantity you entered: <div id="numberInfo"></div> </p>
    <button id="nextBtn" disabled="true">Please select phone color and purchase quantity</button>

First, there is a select selection box on the page, then there is the input box of the entered purchase quantity, and there are two display areas, which are the display areas of the selected color and the entered quantity, as well as the next button operation;

Let's define:

Suppose we get the inventory of all color mobile phones from the background in advance

var goods = {
    // Mobile phone inventory
    "red": 6,
    "blue": 8
};

Next, {let's listen to the onchange event in the drop-down box of colorSelect and the oninput event in the input box of numberInput respectively, and then deal with them accordingly

The general JS code is as follows:

// Suppose we get the inventory of all color mobile phones from the background in advance
var goods = {
    // Mobile phone inventory
    "red": 6,
    "blue": 8
};
/*
Let's listen to the onchange event in the drop-down box of colorSelect and the oninput event in the input box of numberInput,
Then make corresponding handling in these two events
*/
var colorSelect = document.getElementById("colorSelect"),
    numberInput = document.getElementById("numberInput"),
    colorInfo = document.getElementById("colorInfo"),
    numberInfo = document.getElementById("numberInfo"),
    nextBtn = document.getElementById("nextBtn");
        
// Listen for change events
colorSelect.onchange = function(e){
    select();
};
numberInput.oninput = function(){
    select();
};
function select(){
    var color = colorSelect.value,   // colour
        number = numberInput.value,  // quantity
        stock = goods[color];  // The current stock corresponding to the mobile phone of this color
            
    colorInfo.innerHTML = color;
    numberInfo.innerHTML = number;

    // If the user does not select a color, disable the button
    if(!color) {
        nextBtn.disabled = true;
        nextBtn.innerHTML = "Please select the phone color";
            return;
    }
    // Judge whether the purchase quantity entered by the user is a positive integer
    var reg = /^\d+$/g;
    if(!reg.test(number)) {
        nextBtn.disabled = true;
        nextBtn.innerHTML = "Please enter the correct purchase quantity";
        return;
    }
    // If the currently selected quantity is greater than the current inventory quantity, the inventory shortage will be displayed
    if(number > stock) {
        nextBtn.disabled = true;
        nextBtn.innerHTML = "Insufficient inventory";
        return;
    }
    nextBtn.disabled = false;
    nextBtn.innerHTML = "Put in shopping cart";
}

Although the above code completes the requirements on the page, our codes are coupled together. At present, although there are not many problems, if there are more and more SKU attributes with the change of requirements in the future, for example, when one or more drop-down boxes are added to the page, it represents the selection of mobile phone memory. Now we need to calculate the color, memory and purchase quantity, To determine whether nextBtn shows insufficient inventory or put it into the shopping cart; The code is as follows:

The HTML code is as follows:

Select color:
    <select id="colorSelect">
        <option value="">Please select</option>
        <option value="red">gules</option>
        <option value="blue">blue</option>
    </select>
    <br/>
    <br/>
    Select memory:
    <select id="memorySelect">
        <option value="">Please select</option>
        <option value="32G">32G</option>
        <option value="64G">64G</option>
    </select>
    <p>Enter the quantity purchased: <input type="text" id="numberInput"/></p>
    Color you selected:<div id="colorInfo"></div>
    You have selected memory:<div id="memoryInfo"></div>
    <p>Quantity you entered: <div id="numberInfo"></div> </p>
    <button id="nextBtn" disabled="true">Please select phone color and purchase quantity</button>

The JS code becomes as follows:

// Suppose we get the inventory of all color mobile phones from the background in advance
var goods = {
    // Mobile phone inventory
    "red|32G": 6,
    "red|64G": 16,
    "blue|32G": 8,
    "blue|64G": 18
};
/*
Let's listen to the onchange event in the drop-down box of colorSelect and the oninput event in the input box of numberInput,
Then make corresponding handling in these two events
 */
var colorSelect = document.getElementById("colorSelect"),
    memorySelect = document.getElementById("memorySelect"),
    numberInput = document.getElementById("numberInput"),
    colorInfo = document.getElementById("colorInfo"),
    numberInfo = document.getElementById("numberInfo"),
    memoryInfo = document.getElementById("memoryInfo"),
    nextBtn = document.getElementById("nextBtn");
        
// Listen for change events
colorSelect.onchange = function(){
    select();
};
numberInput.oninput = function(){
    select();
};
memorySelect.onchange = function(){
    select();    
};
function select(){
    var color = colorSelect.value,   // colour
        number = numberInput.value,  // quantity
        memory = memorySelect.value, // Memory
        stock = goods[color + '|' +memory];  // The current stock corresponding to the mobile phone of this color
            
    colorInfo.innerHTML = color;
    numberInfo.innerHTML = number;
    memoryInfo.innerHTML = memory;
    // If the user does not select a color, disable the button
    if(!color) {
        nextBtn.disabled = true;
        nextBtn.innerHTML = "Please select the phone color";
            return;
        }
        // Judge whether the purchase quantity entered by the user is a positive integer
        var reg = /^\d+$/g;
        if(!reg.test(number)) {
            nextBtn.disabled = true;
            nextBtn.innerHTML = "Please enter the correct purchase quantity";
            return;
        }
        // If the currently selected quantity is greater than the current inventory quantity, the inventory shortage will be displayed
        if(number > stock) {
            nextBtn.disabled = true;
            nextBtn.innerHTML = "Insufficient inventory";
            return;
        }
        nextBtn.disabled = false;
        nextBtn.innerHTML = "Put in shopping cart";
    }

Keywords: Javascript Front-end

Added by MrSarun on Mon, 17 Jan 2022 07:52:10 +0200