js-design patterns in a nutshell

js-design patterns in a nutshell

The design pattern is a kind of thought, the code is clean, easy to understand, maintain, expand, and optimize

Singleton mode

Singleton mode: manage the content of a certain module based on a separate instance to achieve independent division of modules and mutual calling

  • Class has only one instance
  • The instance can be accessed globally (we use closure and function scope here)

When the project starts, divide the modules, one module per person

var A = ( function () { var data = []; function change ( val ) { data.push(val) } function handleClick () {} } return { change } )() //Call each other through the methods exposed by A var B = ( function () { var data = []; function handleClick () {} } A.change( 11 ); return { handleClick } )() The shortcomings of this model are also obvious. Everyone is under the same closure. If you modify the amount, it will affect the things in the current closure. If you want to privatize, each has its own container. A.change( 10 ) A.change( 20 ) Copy code

In business development, everyone must have encountered this situation. You need to execute 1, in execution 2, and in execution 3. There is a mutual dependency relationship, which will be generated at this time.

Command mode

Let's take a look at the singleton + command mode

  • Perform different operations according to different commands
let SearchModule = ( function () { function QueryData () {}; function bindHTML () {}; } return { //Command mode: It is equivalent to the controller, which controls who executes first and who executes later! init : function () { QueryData(); bindHTML(); } } )() SearchModule.init() Copy code

The shortcomings of the singleton mode we mentioned above: At this time, you can take another look:

Constructor pattern

Each instance belongs to itself, that is, there is a separate space, independent of each other, with its own private properties and methods or public method classes & instances
private & public property methods

class A { constructor () { //this = each class instance this .arr = []; } change ( val ) { this .arr.push(val) } //If you are using ES5, if you put it in the function //you need to recreate it every new time, you can directly mount it to the prototype prompt () { console .log( this .arr) } } A.prototype.prompt = function () { console .log( this .arr) } var a1 = new A; var a2 = new A; console .log(a1 === a2) false console .log(a1.arr === a2.arr) false console .log(a1.change === a2. change) true //Because they are all hung to the prototype, the same method is called Copy code

Factory mode

Core idea: transfer station

It can help us realize the switching or transfer processing of the call, unified processing, and pipeline

function tory ( options ) { options = options || {}; let {type, payload,} = options; if (type === 'array' ){ //Processing some logic return } else { //Processing another logic return } } Copy code

Observer mode (vue2.0 responsive principle)

Once the target changes, the observer will be notified to take the corresponding action

Above: The picture is combined with the code and the next idea

//Observer class Obsever { update ( message ) { //The message is delivered and the update is notified to the console .log(message) } } //Observer class Demo { update ( message ) { //The message is delivered and the update is notified to the console .log(message) } } //Target class Subject { //Of course, you can also pull out these methods to become an observer management exclusive class, and call the constructor () { this .observerList = []; } add ( observer ) { //You can do to remove the duplication this .observerList.push(observer) return this ; } remove () { this .observerList = this .observerList.filter( ob => ob !== observer) return this ; } get ( index ) { //Determine whether to meet or not meet the requirements return this .observerList[index] } count () { return this .observerList.length; } ......... //notify notify ( ...params ) { //why use for loop, because for loop has better performance than forEach for ( let i = 0 ;i< this .count(); i++){ let item = this . get(i); item.update(...params) } } } let sub = new Subject; sub.add( new Obsever); sub.add( new Demo); setTimtout( () => { sub.notify( 'The company's new products are listed, welcome everyone to snap up' ) }, 1000 ) Copy code

Intermediary model

Hosting a third party to do it can effectively reduce the degree of coupling

//Realize the mediator mode by the singleton mode let mediator = ( function () { let topics = {}; //Subscription: The method topic of component A can be understood as the identification let subscribe = function subscribe ( topic, callback ) { !topics[topic]? topics[topic] = []: null ; topics[topic].push({ context : this , callback }) }, //Publish: B component notifies the method of subscribing before let publish = function publish ( topic,...params ) { if (!topics[topic]) return ; topics[topic].forEach( item => { let {callback,context} = item; callback.call(context,...params) }) } return { subscribe, publish } })() Copy code

Publish and subscribe model

Personally think that the publish and subscribe model is very similar to the DOM2 level event addEventListener

  • Binding a method to an event of the current element also counts as an event pool mechanism
  • The event behavior is triggered, and the method execution in the event pool will be notified in turn
  • Must be a built-in event (support event)

Application scenario: When a certain stage arrives, many methods need to be executed, all of which can be based on this mode

![image.png]( p3-juejin.byteimg.com/tos-cn-i-k3 watermark.image)

If implemented in singleton mode:

( function () { let pond = []; //Inject custom events into the event pool function subscribe ( func ) { //You can do some de- duplication judgments if (!pond.includes(func)) { pond.push(func) }; //Do one at the same time to remove the currently added method return function unSubscribe () { pond = pond.filter( itm => itm !== func) } } subscribe.fire = function () { pond.forEach( itm => { if ( typeof itm === "function" ){ itm() } }) } window .subscribe = subscribe; })() let unsubscribe = subscribe( function () { console .log( 111 )}) subscribe( function () { console .log( 22222 )}) subscribe( function () { console .log( 333333 ),unsubscribe()}) subscribe( function () { console .log( 444444 )}) subscribe( function () { console .log( 555555 )}) setTimeout ( () => { subscribe.fire() }, 1000 ); setTimeout ( () => { subscribe.fire() }, 3000 ) Copy code

After introducing the ES5 version above, there is a shortcoming that there is only one event pool. If you need multiple event pools, it will be powerless, so at this time, our demand style needs to support multiple event pools.

So ES6 object-oriented is here

class Sub { pond = []; //Set the method on the prototype subscribe ( func ) { let self = this ; pond = self.pond; if (!pond.includes(func)) this .pond.push(func) return function unsubscribe () { //You can use filter let i = 0 ; let len = pond.length; for (;i<len;i++){ if (pond[i] === func){ pond.splice(i, 1 ) break } } } } fire ( ...params ) { let self = this ; self.pond.forEach( itm => { if ( typeof itm === 'function){ itm(...params) } }) } } Copy code

When you see this, you may be wondering that this is not the same as addEventListener at all.

Ok, next I will expand based on the singleton writing above, and I can also do it based on ES6

let sub = ( function () { pond = {}; //The setting method on the prototype const on = ( type, func ) => { //First judge whether there is this item in the pond, and not create an array ! Array .isArray(pond[type])? Pond[type] = [] : null ; let arr = pond[type]; if (arr.includes(func)) return ; arr.push(func); } const remove = ( type,func ) => { let arr = pond[type]; let i = 0 ; item = null ; for (;i<arr.length; i++){ if (item === func){ //cannot be removed directly, otherwise it will cause the array collapse problem, just assign a value of null, the filter is before the next execution null arr[i] = null ; break ; } } } const fire = ( type,...params ) => { let arr = pond[type]; //It can also be judged whether it is an array i = 0 ; item = null ; for (;i <arr.length; i++){ item = arr[i]; if ( typeof itm === 'function' ){ itm(...params); continue ; } //Remove everything that is not a function (including null) i-- arr.splice(i, 1 ); i-- } } return { on, remove, fire } })() const fn1 = () => console .log( 1 ); const fn2 = () => console .log( 2 ); const fn3 = () => console .log( 3 ); const fn4 = () => console .log( 4 ); sub.on( 'A' ,fn1) sub.on( 'A' ,fn2) sub.on( 'B' ,fn3) sub.on( 'B' ,fn4) setTimeout ( () => { sub.fire( 'A' , 1 ) }, 1000 ) Copy code

Continue to update