I have read the book “JavaScript Design Patterns and Development Practices” before, and have a certain understanding of the design patterns and some related cases in the book. At the same time, I have sorted out the application of these design patterns in some other projects, and the following is for reference only:
Add: If there is anything wrong with the following content, please correct it.
Design pattern Purpose
Design patterns are designed for better code reuse, readability, reliability, and maintainability.
Six principles of design
1) Single responsibility principle 2) Richter’s substitution principle 3) dependency reversal principle 4) interface isolation principle 5) Least knowledge principle (Demeter’s rule) 6) Open and closed principle
Classification of design patterns
Generally speaking, design patterns can be divided into three categories:
There are five types of creation patterns: factory method pattern, Abstract factory pattern, singleton pattern, Builder pattern, and prototype pattern.
Structural mode, a total of seven: adapter mode, decorator mode, agent mode, appearance mode, bridge mode, composite mode, share mode.
There are eleven behavioral modes: strategy mode, template method mode, observer mode, iteration sub-mode, responsibility chain mode, command mode, memo mode, state mode, visitor mode, intermediary mode, interpreter mode.
There are actually two more types: syndication mode and thread pool mode.
However, for the front end, some design patterns are almost not used or rarely used in the usual work, come here, to understand the common front end design patterns
Design patterns in JS
Common design patterns:
1. Factory mode
The common instantiation object pattern, the factory pattern, is the equivalent of new for creating instance objects, providing an interface for creating objects
// A specific object to be created
class Product {
constructor (name) {
this.name = name;
}
init () {}
}
// Factory object
class Creator {
create (name) {
return newProduct(name); }}const creator = new Creator();
const p = creator.create(); // The concrete object created from the factory object
Copy the code
Application scenarios: $, vue.comPonent, and React. CreateElement in JQuery
2. Singleton mode
Ensure that a class has only one instance and provide a global access point to access it, typically a singleton such as login, shopping cart, and so on.
// Singleton object
class SingleObject {
login () {}
}
// Access methods
SingleObject.getInstance = (function () {
let instance;
return function () {
if(! instance) { instance =new SingleObject();
}
return instance;
}
})()
const obj1 = SingleObject.getInstance();
const obj2 = SingleObject.getInstance();
console.log(obj1 === obj2); // true
Copy the code
Application scenarios: $in JQuery, Store in Vuex, Store in Redux, etc
3. Adapter mode
To solve the problem of incompatibility between two interfaces, a single object is used to wrap incompatible objects, such as parameter conversion, allowing direct access
class Adapter {
specificRequest () {
return German standard plug; }}// Adaptor object that wraps incompatible objects
class Target {
constructor () {
this.adapter = new Adapter();
}
request () {
const info = this.adapter.specificRequest();
console.log(`${info}- Converter - China standard plug ')}}const target = new Target();
console.log(target.request()); // German standard plug - converter - Chinese standard plug
Copy the code
Application scenarios: Computed in Vue, converting old JSON format to new format, etc
4. Decorator mode
To dynamically add new functionality to an object without changing its interface without changing the object itself
class Plane {
fire () {
console.log('Send regular bullets'); }}// Decorated objects
class Missile {
constructor (plane) {
this.plane = plane;
}
fire () {
this.plane.fire();
console.log('Launch a missile'); }}let plane = new Plane();
plane = new Missile(plane);
console.log(plane.fire()); // Print and send normal bullets to launch missiles
Copy the code
Use AOP to add functionality to functions dynamically, that is, after or before functions
Function.prototype.before = function (beforeFn) {
const _self = this;
return function () {
beforeFn.apply(this.arguments);
return _self.apply(this.arguments); }}Function.prototype.after = function (afterFn) {
const _self = this;
return function () {
const ret = _self.apply(this.arguments);
afterFn.apply(this.arguments);
returnret; }}let func = function () {
console.log('2');
}
func = func.before(function() {
console.log('1');
}).after(function() {
console.log('3');
})
func();
console.log(func()); // Print 1, 2 and 3 in sequence
Copy the code
Application scenarios: Rewrite init method and array variation method in Vue when ES7 decorator and Vuex 1.0 version are mixed with Vue
5. Proxy mode
Provide a proxy for other objects to control access to that object instead of directly accessing the target object
class Flower {}
/ / the source object
class Jack {
constructor (target) {
this.target = target;
}
sendFlower (target) {
const flower = new Flower();
this.target.receiveFlower(flower)
}
}
// Target object
class Rose {
receiveFlower (flower) {
console.log('Received flowers:' + flower)
}
}
// Proxy object
class ProxyObj {
constructor () {
this.target = new Rose();
}
receiveFlower (flower) {
this.sendFlower(flower)
}
sendFlower (flower) {
this.target.receiveFlower(flower)
}
}
const proxyObj = new ProxyObj();
const jack = new Jack(proxyObj);
jack.sendFlower(proxyObj); // Receive flowers: [object object]
Copy the code
Application scenario: ES6 Proxy, Vuex, getters access, image preloading, etc
6. Appearance mode
To provide a higher level unified interface for a complex set of subsystem interfaces, through which access to subsystem interfaces is easier, not consistent with the single responsibility principle and open closed principle
class A {
eat () {}
}
class B {
eat () {}
}
class C {
eat () {
const a = new A();
const b = newB(); a.eat(); b.eat(); }}// Cross-browser event listeners
function addEvent(el, type, fn) {
if (window.addEventListener) {
el.addEventListener(type, fn, false);
} else if (window.attachEvent) {
el.attachEvent('on' + type, fn);
} else {
el['on'+ type] = fn; }}Copy the code
Application scenarios: JS events can be processed by different browsers, and the same method can be passed for different parameter compatible processing
7. Observer Mode
Defines a one-to-many dependency between objects in which all dependent objects are notified when an object’s state changes
class Subject {
constructor () {
this.state = 0;
this.observers = [];
}
getState () {
return this.state;
}
setState (state) {
this.state = state;
this.notify();
}
notify () {
this.observers.forEach(observer= > {
observer.update();
})
}
attach (observer) {
this.observers.push(observer); }}class Observer {
constructor (name, subject) {
this.name = name;
this.subject = subject;
this.subject.attach(this);
}
update () {
console.log(`The ${this.name} update, state: The ${this.subject.getState()}`); }}let sub = new Subject();
let observer1 = new Observer('o1', sub);
let observer2 = new Observer('o2', sub);
sub.setState(1);
Copy the code
Difference between observer and publish/subscribe: The essential difference is where to schedule
Although the two modes are subscribers and publishers (specific observer can be regarded as the subscriber, specific goals can be considered a publisher), but the observer pattern is scheduled by specific goals, and publish/subscribe pattern is unified by the dispatching center, so the observer pattern between subscribers and publishers are dependent, and publish/subscribe pattern will not.
— Observer mode: The target and observer are base classes. The target provides a set of methods to maintain the observer, and the observer provides an update interface. The specific observer and the specific target inherit their respective base classes, and then the specific observer registers itself with the specific target, scheduling the update method of the observer when the specific target changes. For example, there is A specific target, A, of the “weather center”, which is dedicated to monitoring the weather changes, and an observer, B, who has an interface to display the weather, will register himself in A. When A triggers the weather changes, B will schedule the update method of B, and bring its own context.
— Publish/subscribe mode: subscribers register the event they want to subscribe to the dispatch center. When the event is triggered, the publisher publishes the event to the dispatch center (along with the context), and the dispatch center uniformly dispatches the processing code registered by subscribers to the dispatch center. For example, if an interface displays the weather in real time, it subscribs to weather events (registering with the dispatch center, including handlers), and when the weather changes (regularly retrieving data), it publishes weather information as a publisher to the dispatch center, which dispatches the subscriber’s weather handlers.
Application scenarios: JS event, JS Promise, jquery. $CallBack, Vue watch, NodeJS custom event, file stream, etc
Iterator pattern
Provides a way to access elements of an aggregate object sequentially without exposing the internal representation of the object
It can be divided into: inner iterator and outer iterator
Inner iterator: The rules of iteration are defined internally and only need to be called once externally.
const each = (args, fn) = > {
for (let i = 0, len = args.length; i < len; i++) {
const value = fn(args[i], i, args);
if (value === false) break; }}Copy the code
Application scenario: jquery. each
External iterators: Requests that must be displayed to iterate over the next element.
/ / the iterator
class Iterator {
constructor (list) {
this.list = list;
this.index = 0;
}
next () {
if (this.hasNext()) {
return this.list[this.index++]
}
return null;
}
hasNext () {
if (this.index === this.list.length) {
return false;
}
return true; }}const arr = [1.2.3.4.5.6];
const ite = new Iterator();
while(ite.hasNext()) {
console.log(ite.next()); // Print 1, 2, 3, 4, 5, 6
}
Copy the code
Application scenario: JS Iterator, JS Generator
9. State mode
The key is to distinguish between internal states of things, which often bring about behavioral changes of things, that is, allowing an object to change its behavior when its internal states change
/ / a red light
class RedLight {
constructor (state) {
this.state = state;
}
light () {
console.log('turn to red light');
this.state.setState(this.state.greenLight)
}
}
/ / the green light
class greenLight {
constructor (state) {
this.state = state;
}
light () {
console.log('turn to green light');
this.state.setState(this.state.yellowLight)
}
}
/ / yellow light
class yellowLight {
constructor (state) {
this.state = state;
}
light () {
console.log('turn to yellow light');
this.state.setState(this.state.redLight)
}
}
class State {
constructor () {
this.redLight = new RedLight(this)
this.greenLight = new greenLight(this)
this.yellowLight = new yellowLight(this)
this.setState(this.redLight) // initialize to red
}
setState (state) {
this.currState = state; }}const state = new State();
state.currState.light() // turn to red light
setInterval((a)= > {
state.currState.light() // Prints red, green, and yellow lights every 3 seconds
}, 3000)
Copy the code
Application scenarios: bulb status, traffic light switch, etc
Other design patterns:
Command mode 11, Combination mode 12, Share mode 13, Policy mode 14, Responsibility chain mode 15, template method mode 16, intermediary mode 17, memo mode 18, visitor mode 19, interpreter mode 20, bridge mode
For other design modes, go to github.com/jefferyE. For more design modes, see www.runoob.com