preface

The usual development may not need to use design patterns, but JS design patterns for performance optimization and project engineering is also very helpful, the following is a simple introduction and summary of commonly used design patterns.

1. Singleton mode

Definition: Ensures that a class has only one instance and provides a global access point to access it.

class Singleton {
  constructor(age) {
    this.age = age;
  }
  static getInstance(age) {
    const instance = Symbol.for('Singleton'); // Hide attributes, pseudo private
    if(! Singleton[instance]) { Singleton[instance] =new Singleton(age);
    }
    returnSingleton[instance]; }}const singleton = Singleton.getInstance(30);
const singleton2 = Singleton.getInstance(20);
console.log(singleton === singleton2); // true
Copy the code

2. Policy mode

Definition: Define a set of algorithms, encapsulate them one by one, and make them interchangeable.

The core of the strategy mode is divided into two parts:

  • The first part is the policy class, which encapsulates the specific algorithm.

  • The second part is the environment class, which receives customer requests and dispatches them to the policy class.

Now let’s assume that there is A need to calculate the year-end bonus for colleagues whose performance is S, A and B, corresponding to 4 times, 3 times and 2 times salary respectively. The common formula is as follows:

const calculateBonus = (performanceLevel, salary) = > {

  if (performanceLevel === 'S') {
    return salary * 4;
  }

  if (performanceLevel === 'A') {
    return salary * 3;
  }

  if (performanceLevel === 'B') {
    return salary * 2; }}; calculateBonus('B'.20000); / / 40000
Copy the code

As can be seen, there are a lot of if and else statements in the code. If the corresponding calculation method is changed or the level is added, we need to adjust the function inside, and the salary algorithm is not reusable, so we can use the policy mode to reconstruct, the code is as follows:

// Solve the magic string
const strategyTypes = {
  S: Symbol('S'),
  A: Symbol('A'),
  B: Symbol('B'),};/ / strategy
const strategies = {
  // The salary of class S is calculated
  [strategyTypes.S](salary) {
    return salary * 4;
  },

  // A salary calculation
  [strategyTypes.A](salary) {
    return salary * 3;
  },

  // B salary calculation
  [strategyTypes.B](salary) {
    return salary * 2;
  }
  // More levels of computation can be added freely without affecting the original part
};

/ / environment
const calculateBonus = (level, salary) = > { 
  return strategies[level](salary);
};

calculateBonus(strategyTypes.S, 300); / / 1200
Copy the code

Advantages of the strategic pattern:

  • Multiple if-else statements are avoided by using combination, delegate and polymorphism.

  • It provides perfect support for the open-closed principle and encapsulates algorithms in a separate strategy, making them easy to switch, understand, and extend.

  • The algorithm in Strategy can also be used elsewhere, avoiding a lot of copy-and-paste;

Disadvantages:

  • Add many policy classes or policy objects;

  • Violate the least knowledge principle;

3. Proxy mode

Definition: Provides a proxy or placeholder for an object to control access to it.

3.1 Virtual Proxy

In the program world, where operations can be expensive, B can reduce overhead by listening to THE state of C to send A’s request (A would need to access C in real time to prepare the request).

Meaning of agency

Single responsibility: There should be only one reason for a class (usually objects, functions, etc.) to change. If an object takes on more than one responsibility, it means that the object becomes large and can change for more than one reason.

Example: Display a loading diagram before loading an image.

// Immediately execute the function to create image, and the closure to set SRC
const myImage = (() = > {
    const imgNode = document.createElement('img'); 
    document.body.appendChild(imgNode);
    return (src) = > {
      imgNode.src = src;
    }
})();

// proxy myImage to display load.gif before test.jpg onload
const proxyImage = (() = > {
    const img = new Image();
    img.onload = (a)= > {
      myImage(this.src);
    }
    return (src) = > {
      myImage('./loading.gif');
      img.src = src;
    }
})();

proxyImage('./test.jpg');
Copy the code

Here, myImage only does the image SRC setting, and other agents are entrusted to the proxyImage method, in line with the single responsibility principle. In addition, the interface consistency between the proxy and ontology is guaranteed.

3.2 Cache Proxy

The cache proxy can provide temporary storage for some expensive operation results, and can return the previously stored operation results directly on the next operation if the parameters passed in are the same as before.

Examples: Compute products and cache Ajax data.

// Compute the product of all parameters
const multi = (. args) = > {
  let result = 1;
  args.forEach(arg= > {
    result = result * arg;
  });
  return result;
};

// Cache the result function
const proxyMulti = (() = > {
  const cache = {}; / / the buffer pool
  return (. args) = > {
    const param = args.join(', ');
    if (param in cache) {
      return cache[param]; // the key is 1,2,3,4, and the value is 24
    }
    cache[param] = multi.apply(this, args);
    returncache[param]; }; }) ();console.time();
console.log(proxyMulti(1.2.3.4)); / / 24
console.timeEnd(); / / 0.7 ms

console.time();
console.log(proxyMulti(1.2.3.4)); / / 24
console.timeEnd(); // 约 0.1ms
Copy the code

4. Observer mode

The observer pattern, also known as the publisk-subscribe pattern, defines a one-to-many dependency between objects. When an object’s state changes, all dependent objects are notified. In JavaScript development, we generally use the event model instead of the traditional observer model.

4.1 the DOM event

One of the first observer patterns to come into contact with was probably DOM events, such as user clicks. We have no way of knowing when the user clicks, but when the user clicks, the clicked node sends a message to the subscriber.

document.body.addEventListener('click', () => {
  alert('I got clicked! ');
});
Copy the code

4.2 Custom Events

To implement custom events, there are three steps:

  1. Specify the publisher;
  2. Add a cached list for publishers to notify subscribers;
  3. Traversing the cache list triggers callback functions for the subscribers stored there in turn;
class Event {
  constructor() {
    this.eventListObj = {}; // Event list object
  }

  / / the singleton
  static getInstance() {
    const instance = Symbol.for('instance');
    if(! Event[instance]) { Event[instance] =new Event();
    }
    return Event[instance];
  }

  // Add a listening event. The same command can have multiple events
  listen(key, fn) {
    if (!this.eventListObj[key]) {
      this.eventListObj[key] = [];
    }
    // Subscribe messages to the cache list
    this.eventListObj[key].push(fn);
  }

  // Trigger a listening eventtrigger(key, ... args) {const fns = this.eventListObj[key];
    if(fns && fns.length ! = =0) {
      fns.forEach(fn= > {
        fn.apply(this, args); }); }}// Remove the listening event
  remove(key, fn) {
    let fns = this.eventListObj[key];
    // Operate only when subscribed
    if (fns) {
      // The fn parameter is used to determine whether to remove all or specify to remove
      if(! fn) { fns.length =0; // Remove all
      } else {
        // Remove one of them
        fn.forEach((f, index) = > {
          if (f === fn) {
            fns.splice(index, 1); }}); }}}}const event = Event.getInstance(); // Create a global publisher

const add = (a, b) = > {
  console.log(a + b);
};
const minus = (a, b) = > {
  console.log(a - b);
};

event.listen('add', add); // Subscribe to add messages
event.listen('minus', minus); // Subscribe subtraction messages

event.trigger('add'.1.3); // Triggers the add subscription message
event.trigger('minus'.3.1); // Triggers subtraction subscription messages

console.log(event); // The eventListObj property of the Event object contains add

event.remove('add', add); // Cancel the add subscription event
console.log(event); The eventListObj property of the Event object does not contain add
Copy the code

Execution Result:

Examples: Ajax requests for multiple actions after logging in, emit and on in vue, events in Node.js

5. Template method pattern

The template method pattern is a very simple pattern that can be implemented using inheritance alone.

The template method pattern consists of two parts, the first part is the abstract parent class, the second part is the concrete implementation child class.

The algorithm framework of a subclass is usually encapsulated in an abstract superclass, including the implementation of some common methods and the execution order of all methods in the subclass.

By inheriting this abstract class, subclasses also inherit the entire algorithm structure and can choose to override the methods of the parent class.

Let’s say we want to make a cup of tea and a cup of coffee. The steps are as follows:

  1. The water to a boil
  2. To make (coffee/tea) in boiling water
  3. Pour (coffee/tea) into a cup
  4. Add sugar and milk/lemon

It is easy to see that the first step is common and the others are generally the same, so we can implement it using the template approach. (What if someone doesn’t want sugar and milk?)

// The beverage class is abstracted to represent coffee and tea
class Beverage {
  // Hook: Solved the problem of someone not wanting sugar and milk
  customerWantsCondiments = true;
  init() {
    this.boilWater();
    this.brew();
    this.pourInCup();
    if (this.customerWantsCondiments) {
      this.addCondiments(); }}// Step 1: Boil the water
  boilWater(){
    console.log('Boil the water');
  }
  // Step 2: Brew beverage, rewrite in subclass
  brew(){
    throw new Error('brew function must override in child');
  }
  // Step 3: Pour out the drink and override it in subclass
  pourInCup(){
    throw new Error('pourInCup function must override in child');
  }
  // Step 4: Personalized beverage, rewrite in subclass
  addCondiments(){
    throw new Error('addCondiments function must override in child'); }}class Coffee extends Beverage {
  customerWantsCondiments = false;
  brew(){
    console.log('Brew coffee in boiling water');
  }
  pourInCup(){
    console.log('Pour the coffee into the cup');
  }
  addCondiments(){
    console.log('With sugar and milk'); }}class Tea extends Beverage {
  brew(){
    console.log('Soak tea leaves in boiling water');
  }
  pourInCup(){
    console.log('Pour the tea into the cup');
  }
  addCondiments(){
    console.log('Add lemon'); }}new Coffee().init(); // Boil the water, brew the coffee, and pour the coffee into the cup
console.log('-- -- -- -- -- -- -- -- -- -- -- -- -- -- --');
new Tea().init(); // Boil the water, soak the tea leaves in boiling water, pour the tea into a cup, and add lemon
Copy the code

6. Chain of responsibility mode

The linkage pattern of responsibilities: Resolves the coupling between sender and receiver by concatenating objects into a chain and passing requests along the chain until one object can handle them.

A –> B –> C –> … –> N, there is an object in the middle can handle the request of object A, if there is no need to handle the exception at the end.

When you pass your bus pass in the morning rush hour, you just pass it forward and it will always be handed to the conductor, regardless of who you pass it to.

Suppose there is now an e-negotiated gold coupon feature

  • If you pay a 500 yuan deposit, you can get a 100 yuan coupon and be sure to buy the goods.
  • If you pay a 200 yuan deposit, you can get a 50 yuan coupon and be sure to buy the goods.
  • If the deposit can only enter the general purchase, you need to buy the goods when the inventory is enough;
  • Do not pay the deposit is ordinary purchase;

We define a function that takes three arguments:

  • OrderType: 1, 2, 3 table represents 500 yuan deposit, 200 yuan deposit and no deposit mode;
  • Pay: true and false indicate whether the order will be paid.
  • Stock: number stands for stock balance.
const order = (orderType, pay, stock) = > {
  if (orderType === 1) {
    if (pay === true) {
      console.log('Get a $100 coupon');
    } else {
      if (stock > 0) {
        console.log('Regular purchase, no coupons');
      } else {
        console.log('Understock'); }}}else if (orderType === 2) {
    if (pay === true) {
      console.log('Get a $50 coupon');
    } else {
      if (stock > 0) {
        console.log('Regular purchase, no coupons');
      } else {
        console.log('Understock'); }}}else if (orderType === 3) {
    if (stock > 0) {
      console.log('Regular purchase, no coupons');
    } else {
      console.log('Understock');
    }
  }
}
order(1.true.20); // Get a 100 yuan coupon
Copy the code

This is obviously not a good piece of code, a lot of if else conditional branching, and if the business gets a little more complicated, it ends up being unreadable at all.

So we implement the chain of responsibility via AOP(faceted programming) :

const order500 = (orderType, pay, stock) = > {
  // If you pay 500 yuan deposit, you will get 100 yuan coupon successfully
  if (orderType === 1 && pay === true) {
    return console.log('Deposit paid, get $100 coupon');
  }
  // Otherwise go to the next step
  return 'NEXT';
}

const order200 = (orderType, pay, stock) = > {
  // If you pay a deposit of 200 yuan, you will successfully get a coupon of 50 yuan
  if (orderType === 2 && pay === true) {
    return console.log('Deposit paid, get $50 coupon');
  }
  // Otherwise go to the next step
  return 'NEXT';
}

// Normal purchase mode
const orderNormal = (orderType, pay, stock) = > {
  // If the inventory is greater than 0, you can buy it
  if (stock > 0) {
    return console.log('Regular purchase, no coupons');
  }
  // Otherwise, there is not enough stock to purchase
  return console.log('Understock');

}

// Mount the after method to Funciton and use NEXT to determine whether to proceed to the NEXT step
Function.prototype.after = function(fn) {
  const self = this;
  return (. args) = > {
    const result = self.apply(this, args);
    return result === 'NEXT' ? fn.apply(this, args) : result; }}const order = order500.after(order200).after(orderNormal); // Get the order method
order(1.false.10); // Regular purchase, no coupon
Copy the code

By splitting into three separate functions, the unprocessed result ‘NEXT’ is returned to the NEXT node for processing. After is used for binding. Finally, we can insert after in the middle of new requirements, which greatly reduces the coupling degree. However, there is a disadvantage of this, too long responsibility chain increases the scope of function.

7. The Mediator model

In the program, objects often communicate with other objects. When the project is large and there are many objects, this communication will form a communication network. When we want to modify an object, we need to be very careful, so that these changes will not affect the whole body, causing bugs, very complex.

The mediator pattern is used to decouple these objects and form simple object-to-mediator – to – object operations.

The following takes the actual airport control tower as an example.

  • If there is no control tower, each aircraft needs to communicate with other aircraft to ensure the safety of the route. We assume that the route is unsafe if the destination is the same:
/ / the plane class
class Plane {
  constructor(name, to) {
    this.name = name;
    this.to = to;
    this.otherPlanes = []; // Assemble other aircraft
  }
  success() {
    console.log(`The ${this.name}Can fly normally);
  }
  fail(plane) {
    console.log(`The ${this.name}${plane.name}Route conflict, please adjust ');
  }
  fly() {
    let normal = true; // Check whether the plane can fly normally
    let targetPlane = {};
    for (let i = 0; i < this.otherPlanes.length; i++) {
      // You need to compare it with every other aircraft. If the other aircraft conflicts with the current flight path, they cannot fly.
      if (this.otherPlanes[i].to === this.to) {
        normal = false;
        targetPlane = this.otherPlanes[i]; // Remember the aircraft that conflicts with the current aircraft
        break; }}if (normal) {
      this.success(); // Successful, ready to fly
      return;
    }
    this.fail(targetPlane); // Failed, report conflicting aircraft}}// Create an airplane object
class PlaneFactory {
  constructor() {
    this.planes = []; // A collection of all created planes
  }
  static getInstance() {
    const instance = Symbol.for('instance');
    if(! PlaneFactory[instance]) { PlaneFactory[instance] =new PlaneFactory();
    }
    return PlaneFactory[instance];
  }
  // Create the plane, the name of the plane, and the destination
  createPlane(name, to) {
    const plane = new Plane(name, to);
    this.planes.push(plane);
    this.planes.forEach(planeItem= > {
      // If the plane name is different from another plane name, put the other plane into the otherPlanes array for destination comparison
      if (plane.name !== planeItem.name) {
        plane.otherPlanes.push(planeItem);
      }
    });
    // Return to current aircraft
    returnplane; }}// Get the airplane factory instance
const planeFactory = PlaneFactory.getInstance();

// Create four planes
const planeA = planeFactory.createPlane('planeA'.1);
const planeB = planeFactory.createPlane('planeB'.2);
const planeC = planeFactory.createPlane('planeC'.3);
const planeD = planeFactory.createPlane('planeD'.2);

planeA.fly(); // planeA can fly normally
planeB.fly(); // planeB can fly normally
planeC.fly(); // planeC can fly normally
planeD.fly(); // planeD is on collision course with planeB, please adjust
Copy the code

When there are enough planes, this becomes very complicated, and if one day a plane breaks down and does not fly for maintenance, it will be difficult to change it.

  • In the case of the control tower, the aircraft does not need to know the presence of other aircraft, but only need to communicate to the control tower, and the method of removing the faulty aircraft has been added.
/ * * * control tower model: broker * control tower: the receiving aircraft passed all the information, at the same time, need operation * aircraft types: define the plane objects, have the name and the destination, and the method to control tower communication * aircraft factory class: object definition to create the plane factory, adding the * / notification when creating the control tower
/ / control tower
class Tower {
  constructor() {
    this.planes = []; // Assemble the aircraft
    // Operate the aircraft object
    this.operations = {
      add: this.add,
      remove: this.remove,
      fly: this.fly,
    };
  }
  static getInstance() {
    const instance = Symbol.for('instance'); // Prevent being overwritten
    if(! Tower[instance]) { Tower[instance] =new Tower();
    }
    return Tower[instance];
  }
  // Receive information from the aircraft to the control towerreceiveMessage(msg, ... args) {this.operations[msg].apply(this, args);
  }
  // Add planes
  add(plane) {
    this.planes.push(plane);
  }
  // Remove the aircraft
  remove(plane) {
    for (let i = 0; i < this.planes.length; i++) {
      if (this.planes[i].name === plane.name) {
        this.planes.splice(i, 1); }}}// The plane began to fly
  fly(plane) {
    let normal = true;
    let targetPlane = {};
    for (let i = 0; i < this.planes.length; i++) {
      // If the name of the current aircraft is different from that of all aircraft, but the flight path is the same, the flight path is considered to be in conflict
      // Originally this was done in each aircraft, now it is done in the control tower
      if (
        this.planes[i].name ! == plane.name &&this.planes[i].to === plane.to
      ) {
        normal = false;
        targetPlane = this.planes[i];
        break; }}if (normal) {
      plane.success();
      return; } plane.fail(targetPlane); }}// Get the control tower instance
const tower = Tower.getInstance();

// Aircraft class: just set the name and the method to communicate to the control tower
class Plane {
  constructor(name, to) {
    this.name = name;
    this.to = to;
  }
  success() {
    console.log(`The ${this.name}Can fly normally);
  }
  fail(plane) {
    console.log(`The ${this.name}${plane.name}Route conflict, please adjust ');
  }
  // Notify the control tower that the aircraft is removed
  remove() {
    tower.receiveMessage('remove'.this);
  }
  // Notify the control tower that the aircraft has begun flight
  fly() {
    tower.receiveMessage('fly'.this); }}// Aircraft factory: Create an aircraft and inform the add method to the control tower to add an aircraft
class PlaneFactory {
  static plane(name, to) {
    const plane = new Plane(name, to);
    tower.receiveMessage('add', plane);
    returnplane; }}// Create four planes
const planeA = PlaneFactory.plane('planeA'.1);
const planeB = PlaneFactory.plane('planeB'.2);
const planeC = PlaneFactory.plane('planeC'.3);
const planeD = PlaneFactory.plane('planeD'.2);

planeA.fly(); // planeA can fly normally
planeB.fly(); // planeB is on collision course with planeD, please adjust
planeC.fly(); // planeC can fly normally
planeD.fly(); // planeD is on collision course with planeB, please adjust
planeD.remove(); // If planeD fails, remove it
planeB.fly(); // planeB can fly normally
Copy the code

Mediator pattern is an implementation, the principle of least knowledge refers to an object as little as possible about the other object, if the coupling between the objects is too high, after an object change, will inevitably affect the other object, the mediator pattern, almost don’t know other object, they can communicate through the mediator object. But as a result, the intermediary object will inevitably become bloated.

8. Decorator mode

Decorator pattern: A way to dynamically assign responsibility to an object.

We use it a lot in development because it’s so easy to manipulate objects dynamically in JavaScript.

const person = {
  name: 'shelly'.age: 18,
}
person.job = 'student';
Copy the code

8.1 Decoration Function

Extending properties and methods on objects is relatively easy, but rewriting functions is not so easy, especially if the open-close principle is kept as close as possible. We can achieve the desired effect by using AOP decorator functions.

let add = (a, b) = > {
  console.log(a + b);
};
// Execute before the function is executed
Function.prototype.before = function(beforeFn) {
  const self = this;
  return (. args) = > {
    beforeFn.apply(this, args);
    return self.apply(this, args);
  };
};
// execute after the function executes
Function.prototype.after = function(afterFn) {
  const self = this;
  return (. args) = > {
    const result = self.apply(this, args);
    afterFn.apply(this, args);
    return result;
  };
};
// Decorate the add function
add = add
  .before((a)= > {
    console.log('before add');
  })
  .after((a)= > {
    console.log('after add');
  });

add(1.2); // before add, 3, after add
Copy the code

9. Design principles and programming skills

9.1 Principle of single responsibility

Single responsibility Principle (SRP) : An object (method) does only one thing. If a method takes on too much responsibility, the more likely it is to be rewritten in the future.

This principle is widely used in singleton and proxy patterns.

When should we separate?

This is a difficult point to control, such as ajax requests, where creating XHR objects and sending requests are two responsibilities, but they change together and don’t need to be separated; The attR method, like jQuery, should have both assignment and value, but it is convenient for the user. So we have to actually balance that.

9.2 Minimum knowledge principle

Least Knowledge principle (LKP) : One software entity should interact with as few other entities as possible. Entities include objects, classes, modules, functions, and so on.

A common practice is to introduce third-party objects to assume communication between multiple objects, such as the mediator pattern, encapsulation.

9.3 The open-closure principle

Open-closed principle (OCP) : Software entities (classes, modules, functions) should be extensible, but not modifiable.

OCP is well represented in almost all design patterns.

9.3.1 extension

If we need to modify a function and the business logic is extremely complex, we can bind an After method to the original base following the open-closed principle, passing in a callback function to implement our new requirements without changing the previous code.

// The original function
let theMostComplicatedFn = (a, b) = > {
  console.log(I'm an extremely complex function.);
  console.log(a + b);
};
// Define a function to execute after the original function
theMostComplicatedFn.after = function(afterFn) { // We need to use fucntion
  const self = this;
  return (. args) = > {
    const result = self.apply(this, args);
    afterFn.apply(this, args);
    return result;
  };
};
// The mixed function
theMostComplicatedFn = theMostComplicatedFn.after((a, b) = > {
  console.log(a, b);
});

theMostComplicatedFn(1.2); // I am extremely complex function, 3, 1, 2
Copy the code

9.3.2 polymorphism

Taking advantage of object polymorphism also allows programs to follow the open-closed principle, a common technique.

We all know that cats eat fish and dogs eat meat, so let’s put this in code.

const food = (animal) = > {
  if (animal instanceof Cat) {
    console.log('Cats eat fish');
  } else if (animal instanceof Dog) {
    console.log('Dogs eat meat'); }}class Dog {}
class Cat {}
food(new Dog()); / / the dog meat
food(new Cat()); / / the cat eat the fish
Copy the code

So one day we add sheep, and we have to add another else if, what if there’s a lot of sheep? Then we have to keep changing the food function, which is obviously not a good idea. We can now use polymorphism to extract common foods.

const food = (animal) = > {
  animal.food();
}

class Dog {
  food() {
    console.log('Dogs eat meat'); }}class Cat {
  food() {
    console.log('Cats eat fish'); }}class Sheep {
  food() {
    console.log('Sheep eat grass');
  }
}
food(new Dog()); / / the dog meat
food(new Cat()); / / the cat eat the fish
food(new Sheep()); / / sheep are eating grass
Copy the code

This way, we don’t have to change the food function every time we add new animals later.

9.3.3 Other Methods

Hook function, callback function.

9.4 Code Refactoring

9.4.1 Refining functions

The benefits of distilling a piece of code into a function are:

  • Avoid super-large functions
  • Separate functions facilitate code reuse
  • Independent functions are easier to overwrite
  • A separate function with a good name acts as a comment in itself

9.4.2 Merging repeated conditional fragments

If a function has conditional branches that are strewn with duplicate code, merge de-duplication is necessary.

9.4.3 Extract conditional branch statements into functions

Here’s an example:

const getPrice = (price) = > {
  const date = new Date(a);if (date.getMonth() >= 6 && date.getMonth() <=9) { / / in the summer
    return price * 0.8;
  }
  return price;
}
Copy the code

Conditional statements at first glance take a while to understand, so here are some tweaks:

// It is also commented by the function name
const isSummer = (a)= > {
  const month = new Date().getMonth();
  return month >= 6 && month <=9;
}

const getPrice = function (price) {
  const date = new Date(a);if (isSummer()) {
    return price * 0.8;
  }
  return price;
}
Copy the code

9.4.4 Rational use of cycles

If some of the code in the function body is actually doing repetitive work, the proper use of loops can not only do the same thing, but also reduce the amount of code. Let’s take creating an XHR object as an example:

const createXHR = (a)= > {
  let xhr;
  try {
    xhr = new ActiveXObject('MSXML2. XMLHttp. 6.0');
  } catch (e) {
    try {
      xhr = new ActiveXObject('MSXML2. XMLHttp. 3.0');
    } catch (e) {
      xhr = new ActiveXObject('MSXML2.XMLHttp'); }}return xhr;
};
const xhr = createXHR();
Copy the code

Here we loop to achieve the same effect as above:

const createXHR = function () {
  const versions = ['MSXML2. XMLHttp. 6.0 DDD'.'MSXML2. XMLHttp. 3.0'.'MSXML2.XMLHttp'];
  for (let i = 0, version; version = versions[i++];) {
    try {
      return new ActiveXObject(version);
    } catch (e) {
    }
  }
};
const xhr = createXHR();
Copy the code

9.4.5 Let the function exit early instead of nested conditional branches

In multi-tier conditional branching statements, we can pick branches and exit functions as soon as we enter them, reducing non-critical code confusion.

9.4.6 Passing object Arguments instead of long argument lists

Too many function parameters will cause the caller discomfort, may appear less transmission or reverse transmission. If this is the case, we can pass the parameter as an object to the function and then evaluate it inside the function body.

conclusion

In the normal development process, the strategy mode, the chain of responsibility mode and the decorator mode may be used in many cases, intermediaries and observers can often see, although they do not necessarily design themselves, but from the principle of understanding they can also facilitate us to better use.