Peter Seibel, author of Practical Common Lisp, once said that if you need a model, something is wrong. The problem he is talking about is having to find and summarize a universal solution because of the inherent weakness of language.
Each language, weakly typed or strongly typed, static or dynamic, imperative or declarative, has its own strengths and weaknesses. A Jamaican athlete, who has some advantages in sprinting and even boxing, has less in yoga.
The warlock and shadow priest could easily make a great aid, while an enemy flying around the map with Macon on his back would be a bit awkward. In a static language, decorators can take a lot of work to implement, and JS can throw methods on objects at any time, so that decorator mode in JS becomes a weak point.
Pro Javascript Design Patterns is one of the classic books on Javascript Design Patterns, but the examples are verbose, so let me summarize my understanding with some code I’ve written at work. If there is any deviation in my understanding, please do not hesitate to correct me.
A singleton pattern
The singleton pattern is defined to produce a unique instance of a class, but JS itself is a “classless” language. It makes sense that many articles on JS design patterns use {} as a singleton. Since there are many ways in which JS can generate objects, let’s look at another singleton that makes more sense.
A common requirement is to pop up a mask layer on the page when a button is clicked. Like when web.qq.com clicks login.
The code that generates the gray background mask layer is easy to write.
JavaScript
var createMask = function(){
return document,body.appendChild( document.createElement(div) );
}Copy the code
JavaScript
$( 'button' ).click( function(){
Var mask = createMask();
mask.show();
})Copy the code
The problem is that the mask layer is globally unique, so each call to createMask creates a new div, although it can be removed when the mask layer is hidden. But clearly that is not reasonable.
The second option is to create the div at the beginning of the page. Then reference it with a variable.
JavaScript
var mask = document.body.appendChild( document.createElement( ''div' ) ); $( ''button' ).click( function(){ mask.show(); })Copy the code
This does create only one mask layer DIV on the page, but the other problem is that we may never need this mask layer, that wastes another DIV, and any manipulation of dom nodes should be very stingy.
If I could use a variable. To see if you’ve already created a div?
JavaScript
var mask; var createMask = function(){ if ( mask ) return mask; else{ mask = document,body.appendChild( document.createElement(div) ); return mask; }}Copy the code
This looks good, but it does accomplish a function that produces a single column object. Let’s take a closer look at what’s wrong with this code.
First of all, this function has some side effects. It changes the reference of the external variable mask inside the function. In multi-person collaborative projects, createMask is an unsafe function. Mask, on the other hand, is a global variable that is not required. Let’s make it better.
JavaScript
var createMask = function(){
var mask;
return function(){
return mask || ( mask = document.body.appendChild( document.createElement('div') ) )
}
}()Copy the code
Mask is wrapped in a simple closure that, at least for createMask, is closed.
You might look at this and think the singleton pattern is too simple. It is true that some design patterns are very simple, and even if you never pay attention to the concept of design patterns, you will unconsciously use some design patterns in your regular code. Just like years ago when I understood what the old man’s cart was, I thought, oh my god, this is the old man’s cart.
The 23 design patterns in GOF are also patterns that have long existed in software development and are used over and over again. If a programmer is not explicitly aware that he has used certain patterns, he may miss a more appropriate design next time (from Yuhiro Matsumoto’s Programming World).
Back to the point, the previous singleton still has its drawbacks. It can only be used to create mask layers. What if I need to write another function that creates a unique XHR object? Can you find a generic Singleton wrapper?
Js functions are of type 1, which means functions can also be passed as arguments. Take a look at the final code.
JavaScript
var singleton = function( fn ){
var result;
return function(){
return result || ( result = fn .apply( this, arguments ) );
}
}
var createMask = singleton( function(){
return document.body.appendChild( document.createElement('div') );
})Copy the code
A variable is used to hold the first return value, and if it has already been assigned, it will be returned first in future calls. The code that actually creates the mask layer is passed into the Singleton wrapper as a callback function. This is actually called bridge mode. The bridge mode is discussed a little bit later.
However, the Singleton function is not perfect; it still needs a variable result to hold a div reference. Unfortunately, the functional nature of JS is not enough to completely eliminate declarations and statements.
Simple factory model
In the simple factory pattern, a method determines which instances of a class to create, and these instances often have the same interface. This pattern is mainly used when the instantiated type is not determined at compile time, but rather at execution time. In layman’s terms, it’s like the beverage machine in the company’s water cooler, where coffee or milk depends on which button you press.
The simple factory pattern is also useful when creating Ajax objects.
Earlier I wrote a library that handles ajax asynchronous nesting at github.com/AlloyTeam/D…
The library provides several ajax requests, including get and POST for XHR objects, as well as jSONp and iframe for cross-domain use. For ease of use, these methods are abstracted into the same interface.
JavaScript
var request1 = Request('cgi.xx.com/xxx' , ''get' );
request1.start();
request1.done( fn );
var request2 = Request('cgi.xx.com/xxx' , ''jsonp' );
request2.start();
request2.done( fn );Copy the code
Request is essentially a factory method, whether it produces an instance of XHR or jSONP. It’s determined by later code.
In fact, in JS, the so-called constructor is also a simple factory. It’s just a new dress. Let’s take this shirt off and look inside.
With this code, you can simulate new perfectly in Firefox, Chrome and other browsers.
JavaScript
function A( name ){
this.name = name;
}
function ObjectFactory(){
var obj = {},
Constructor = Array.prototype.shift.call( arguments );
obj.__proto__ = typeof Constructor .prototype === 'number' ? Object.prototype
: Constructor .prototype;
var ret = Constructor.apply( obj, arguments );
return typeof ret === 'object' ? ret : obj;
}
var a = ObjectFactory( A, 'svenzeng' );
alert ( a.name ); //svenzengCopy the code
This code comes from the new and constructor instructions in ES5. As you can see, the new itself is just a copy and rewrite of the object, and what is generated depends on the arguments passed in when you call the ObjectFactory.
Three observer model
The observer pattern (aka the publisher-subscriber pattern) should be one of the most common. It is widely used in many languages. This includes the DOM events that we normally deal with. It is also an observer pattern implemented between JS and DOM.
JavaScript
div.onclick = function click (){
alert ( ''click' )
}Copy the code
Just subscribe to the div click event. Function click is triggered when a div is clicked.
So what is the observer model? Look at the observer model in life.
There is a famous Hollywood saying, “Don’t call me, I’ll call you,” which explains the context of the observer model. “I” is the publisher and “you” is the subscriber.
For example, when I came to the company for an interview, every interviewer would say to me after the interview, “Please leave your contact information and we’ll let you know when we hear from you.” Here “I” is the subscriber and the interviewer is the publisher. So I don’t have to check in on a daily or hourly basis. The communication is in the hands of the interviewer. All I have to do is provide a contact information.
The observer mode can realize the decoupling between the two modules well. Let’s say I’m working on an HTML5 game on a team. When the game starts, some graphics need to be loaded. Load these images before executing the game logic. Let’s say it’s a multi-person project. I finished the Gamer and Map modules, while my colleague A wrote an image loader called loadImage.
The code for loadImage is as follows
JavaScript
loadImage( imgAry, function(){ Map.init(); Gamer.init(); })Copy the code
Once the images are loaded, render the map and execute the game logic. Well, the program works fine. One day, IT occurred to me that we should add sound to the game. I should put a line of code in the image loader.
JavaScript
loadImage( imgAry, function(){ Map.init(); Gamer.init(); Sount.init(); })Copy the code
But my colleague A, who wrote this module, went to other places to travel. So I called him. Hello. Where is your loadImage function? Can I change it? There are no side effects after changing it. As you can imagine, all kinds of crazy things happen. What if we could have written this:
JavaScript
loadImage.listen( ''ready', function(){
Map.init();
})
loadImage.listen( ''ready', function(){
Gamer.init();
})
loadImage.listen( ''ready', function(){
Sount.init();
})Copy the code
After loadImage is done, it doesn’t care what happens in the future, because its work is done. Then all it has to do is issue a signal.
LoadImage. The trigger (” ready “);
Objects listening for the ready event of loadImage are notified. Like the last interview example. Interviewers don’t care where the candidates go for dinner when they receive the results. He is only responsible for putting together the resumes of the candidates. Follow the number listed on your resume and call each other when the interview results come back.
Said so many concepts, to a concrete implementation. The implementation process is actually quite simple. Candidates throw their resumes into a box, and interviewers call each of them at the right moment.
JavaScript
Events = function() { var listen, log, obj, one, remove, trigger, __this; obj = {}; __this = this; Listen = function(key, eventfn) {var stack, _ref; Stack = (_ref = obj[key])! = null ? _ref : obj[ key ] = []; return stack.push( eventfn ); }; one = function( key, eventfn ) { remove( key ); return listen( key, eventfn ); }; remove = function( key ) { var _ref; return ( _ref = obj[key] ) ! = null ? _ref.length = 0 : void 0; }; Trigger = function() {var fn, stack, _i, _len, _ref, key; key = Array.prototype.shift.call( arguments ); stack = ( _ref = obj[ key ] ) ! = null ? _ref : obj[ key ] = []; for ( _i = 0, _len = stack.length; _i < _len; _i++ ) { fn = stack[ _i ]; if ( fn.apply( __this, arguments ) === false) { return false; } } return { listen: listen, one: one, remove: remove, trigger: trigger } }Copy the code
Finally, the observer model to do a small adult TV application.
/ / subscriber
JavaScript
var adultTv = Event(); Adulttv. listen('play', function(data){alert (" whose movie is today "+ data.name); }); / / publisher adultTv. The trigger (' play ', {' name ':' Mr Bush})Copy the code
Four-adapter mode
Last year when DEV.qplus.com was being developed, there was a JS file that stored the application category ID. The structure of the category ID was originally designed to be bulky. So I decided to reconstruct it. I define it as a JSON tree, which looks something like this:
JavaScript
var category = {
music: {
id: 1,
children: [ , , , , ]
}
}Copy the code
There are probably four or five pages in dev.qplus.com that invoke this category object. I had a week’s holiday before the Spring Festival. After the Spring Festival, I found an email in my mailbox. The student who designed the database put category.. Js has also been reconstructed, and several other projects have used this category.js. When I took it over, I was stunned. It was completely different from the data structure I had determined before.
This, of course, is a bad example of communication. But the next point is that I’ve used the category.js that I ordered earlier in N files. And there’s some complicated logic involved. How do I change my old code. Rewriting it all is definitely not a good idea. So now the adapter comes in handy.
All I have to do is take my colleague’s category and use a function to make it the same as the one I defined before.
JavaScript
my.category = adapterCategory ( afu.category );Copy the code
The adapter pattern acts much like a transfer interface. Originally, the iPhone charger couldn’t be plugged into the computer case. Instead, it could be plugged into a USB port.
Therefore, the adapter pattern is often used to accommodate two interfaces in applications, such as if you are using a custom JS library. There is a method called $id() to get nodes by ID. One day you think the $implementation in jquery is cooler, but you don’t want your engineers to have to learn new libraries and syntax. That adapter will allow you to do that.
JavaScript
$id = function( id ){
return jQuery( '#' + id )[0];
}Copy the code
5. Agent Mode
The proxy pattern is defined as handing over access to one object to another proxy object.
For example, I was chasing a girl and wanted to send her a bunch of flowers, but I was shy, so I entrusted one of her best friends to deliver them.
This example is not very good, at least we don’t see much use in the agency model, because a better way to chase a woman is to give her a BMW.
For another example, if I had to write my daily work paper every day (it’s not this sad). My daily paper will be reviewed by the director at the end. If we all sent the daily paper directly to the director, he might not be able to do his job. Therefore, the usual practice is to send daily papers to my team leader, who will collect the daily papers of all team members for a week and then send them to the director.
In real programming, there are many opportunities to use proxy mode for performance reasons. For example, frequent access to DOM nodes and frequent requests for remote resources. You can save the operation to a buffer and then choose the actual firing time.
Before a detailed example, I wrote a game street fighter, address in alloyteam.github.com/StreetFight…
In the game, long needs to accept the events of the keyboard to complete the corresponding action.
So I wrote a keyManage class. Listen for keyManage changes in the main thread of the game.
JavaScript
var keyMgr = keyManage();
keyMgr.listen( ''change', function( keyCode ){
console.log( keyCode );
});Copy the code
In the picture, Ryu is lifting dragon fist. Lifting dragon fist operation is front, bottom and front. But the keyManage class fires the change function it was listening for whenever a keyboard event occurs. This means that you can never get a single keystroke event like front, back, front, and fist, but not a keystroke combination.
Well, I decided to rewrite my keyManage class to support passing key combinations as well. But if I write an HTML5 version of the double Dragon, that means I’ll have to rewrite keyManage every time. I’ve always felt that this kind of function could be abstracted into a lower-level method that could be used in any game.
So the final keyManage only maps keyboard events. Ryu receives the action after it is processed by a proxy object.
JavaScript
var keyMgr = keyManage(); keyMgr.listen( ''change', proxy( function( keyCode ){ console.log( keyCode ); // front down front + fist)});Copy the code
As for how to achieve proxy inside, you can play freely.
Another example is when an Ajax request is invoked, either by various open source libraries or by a self-made Ajax class, which sets up a proxy for an XHR object. It is not possible to frequently manipulate XHR objects to make requests, as it should be.
JavaScript
var request = Ajax.get( 'cgi.xx.com/xxx' );
request.send();
request.done(function(){
});Copy the code
Six bridge mode
The purpose of the bridge pattern is to separate the implementation part from the abstract part so that both can change independently. The bridge pattern is particularly useful when implementing apis. Take the case of Singleton from the very beginning.
JavaScript
var singleton = function( fn ){
var result;
return function(){
return result || ( result = fn .apply( this, arguments ) );
}
}
var createMask = singleton( function(){
return document.body.appendChild( document.createElement('div') );
})Copy the code
Singleton is the abstract part, while createMask is the implementation part. They can change on their own. If you need to write another singleton createScript, it’s no sweat at all.
JavaScript
var createScript = singleton( function(){
return document.body.appendChild( document.createElement('script') );
})Copy the code
Another common example is the implementation of the forEach function, which iterates over an array.
JavaScript
forEach = function( ary, fn ){ for ( var i = 0, l = ary.length; i < l; i++ ){ var c = ary[ i ]; if ( fn.call( c, i, c ) === false ){ return false; }}}Copy the code
As you can see, forEach does not care about the implementation of fn, and fn’s logic is not affected by forEach’s rewriting.
JavaScript
ForEach ([1,2], function(I, n){alert (n*2)}) forEach([1,2,3], function(I, n){alert (n*3)})Copy the code
Vii Appearance Mode
The appearance pattern (facade pattern) is a relatively simple and ubiquitous pattern. The facade pattern provides a high-level interface that makes it easier to invoke clients or subsystems. I can’t think of a simpler code
JavaScript
var getName = function(){
return ''svenzeng"
}
var getSex = function(){
return 'man'
}Copy the code
If you need to call getName and getSex respectively. That can be called using a higher level interface called getUserInfo.
JavaScript
var getUserInfo = function(){
var info = a() + b();
return info;
}Copy the code
You might ask why you didn’t write getName and getSex together in the first place, like this
JavaScript
var getNameAndSex = function(){
return 'svenzeng" + "man";
}Copy the code
The answer is obvious. The chef at the canteen doesn’t mix roast duck and cabbage in the same pot just because you ordered them. He would rather serve you a set of roast duck rice. Also in programming, we need to make sure that functions or objects are as fine-grained as possible. After all, not everyone likes roast duck and cabbage at the same time. The facade pattern also has the benefit of hiding the real implementation details from the user, who only cares about the top-level interface. For example, in the story of the roast duck rice set, you don’t care whether the chef makes the roast duck or stir-fry the cabbage first, and you don’t care where the duck was raised.
Finally, an example of appearance patterns that we’ve all used
JavaScript
Var stopEvent = function(e){var stopEvent = function(e){ e.preventDefault(); }Copy the code
8 visitor Mode
GOF official definition: Visitor pattern represents an operation that acts on elements in an object structure. It makes it possible to define new operations on elements without changing their classes. When we use some operations to process different objects, we often choose different processing methods and procedures according to different objects. In the actual code process, we can find that if all operations are scattered among objects, the whole system becomes difficult to maintain and modify. And adding new operations usually involves recompiling all classes. Therefore, to solve this problem, we can extract the related operations from each class and wrap them into a separate object, which we call a Visitor. Use the visitor, to visit the elements of some operations, just pass this object as a parameter to the current visitor, and then, the visitor will be based on the specific information of the visitor, related operations.
According to statistics, only 5% of people read the last sentence. In plain English, the Visitor pattern abstracts some reusable behavior into a function (object), which we call a Visitor. If some other object calls this function, we just pass those objects as arguments to the function. In JS we often pass this object to a Visitor function by call or apply. The Visitor pattern is also known as one of the most difficult to understand of the 23 design patterns summarized by GOF. But that’s largely because design mode was written in C++ and Smalltalk. In strongly typed languages, multiple overloads are required to match the visitor’s interface.
In js, the visitor pattern is almost a native implementation, so we can use the visitor pattern effortlessly with Apply and Call. This section is more concerned with the idea of this pattern and its implementation in THE JS engine.
Once upon a time, there was an emperor who liked to hear ducks quack, so he called on his ministers to form a choir of a thousand ducks. The minister caught all the ducks in the country, and there was still one missing. Finally one day a chicken came along and volunteered. The chicken said it could croak too, and in the context of the story, it could. As the story goes, the chicken apparently gets mixed up with a chorus of ducks. The emperor only wants to hear the quack, he doesn’t care if you are a duck or a chicken.
This is the concept of duck type, in weakly typed js language, many methods do not do object type detection, but only care about what the object can do. The Array constructor and String constructor’s Prototype methods are designed to be visitors. These methods do nothing to check the data type of this. That’s why Arguments can call the push method as array.
Take a look at the array.prototype. push code in v8:
JavaScript
function ArrayPush() { var n = TO_UINT32( this.length ); var m = %_ArgumentsLength(); for (var i = 0; i < m; i++) { this[i+n] = %_Arguments(i); } this.length = n + m; // return this. Length; }Copy the code
As you can see, the ArrayPush method does not place any display restrictions on the type of this, so in theory any object can be passed to the ArrayPush visitor.
// The length property of this object is writable. // The length property of this object is writable. Functon object, function has a read-only Length property that represents the number of parameters.
If these two rules are not met, the code will report an error at execution time. . That is to say, the Array. The prototype. Push the call (1, “first”) and Array. Prototoype. Push. The call (the function () {}, “first”) failed to reach the desired effect.
With visitors, let’s do something fun. Add a push method to an object.
JavaScript
var Visitor = {} Visitor .push = function(){ return Array.prototype.push.apply( this, arguments ); } var obj = {}; obj.push = Visitor .push; obj.push( '"first" ); alert ( obj[0] ) //"first" alert ( obj.length ); / / 1Copy the code
Ix Strategy Mode
The meaning of the policy pattern is to define a series of algorithms, encapsulate them one by one, and make them interchangeable. A small example will make it clear. Recall the animate method in jquery.
JavaScript
$( div ).animate( {"left: 200px"}, 1000, 'linear' ); Constant speed in a / / $(div). The animate ({" left: 200 px "}, 1000, "cubic"); // Slow to the third powerCopy the code
Both lines of code move div 200 pixels to the right in 1000ms. Linear and cubic are encapsulation of a strategic pattern. Another example: Dev.qplus.com, which I wrote earlier this year, had a form for instant verification on many pages. Each member of the form has some different validation rules.
For example, in the name box, you need to verify that it is not empty, sensitive words, characters are too long. Of course, you can write 3 if else to solve the problem, but this way to write code scalability and maintainability can be imagined. If you have a lot more elements in your form, and you have a lot more validation, it’s not impossible to add up hundreds of if else’s. It is better to wrap each validation rule in a separate policy pattern. You only need to provide the name of the policy when you need which authentication. Something like this:
JavaScript
nameInput.addValidata({ notNull: true, dirtyWords: true, maxLength: 30}) methods such as notNull, maxLength, etc. only need to return true or false to indicate whether validation is passed. validataList = { notNull: function( value ){ return value ! = = ' '; }, maxLength: function( value, maxLen ){ return value.length() > maxLen; }}Copy the code
As you can see, the various validation rules can be easily modified and interchangeable. If one day the product manager suggests that the maximum length of characters be changed to 60 characters. It only takes 0.5 seconds to do the job.
Ten template method pattern
The pattern approach is to define a set of algorithms in advance, first abstracting the invariant parts of the algorithm to the parent class, and then deferring the implementation of other variable steps to the child class. Sounds a bit like the factory pattern (rather than the simple factory pattern described earlier). The biggest difference is that the intention of the factory pattern is to eventually obtain an object based on the implementation of a subclass. The template method pattern focuses on parental control over subclasses.
In GOF’s description, the template approach leads to a reverse control structure sometimes referred to as the “Hollywood rule” : “Don’t come to us, we’ll come to you.” This means that a parent class calls an operation of a subclass, not the other way around. A common scenario is in a corporate project, where the architect sets up the architecture and declares abstract methods. The following programmers rewrite these abstract methods separately.
Let me digress a little before I dive into this. As an opponent of evolution, assume that God created the world in code. Then God may have used the template method to create life. See how he declares template methods in the life constructor:
JavaScript
Var Life = function(){} life.prototype.init = function(){this.dna (); This. Was born (); This growth (); This. (aging); This. (death); } this. Prototype. DNA replication = function () {$% & * & ^ % ^ & (& (& (&& (^ ^ (*) / / can't read the code} Life. The prototype. Prototype = function(){} life.prototype. Prototype = function(){} life.prototype Prototype = function(){} life.prototype. Function (){}Copy the code
Where DNA replication is a fixed part of the pre-defined algorithm. None of the subclasses can overwrite it. We can make it protected if we want. Other functions are defined as an empty function (hook) in the parent class, and then overridden by the subclass. This is the so-called mutable step in template methods. Suppose you have a subclass of mammalian class that descends from the Life class.
JavaScript
var Mammal = function(){
}
Mammal.prototype = Life.prototype; //继承LifeCopy the code
Then rewrite the birth and aging hook functions.
JavaScript
Prototope = function(){' take ()} Mammal. Growth = function(){// leave it to subclasses to implement} Mammal. Prototope. Prototype = function(){free radical peroxide ()} life.prototype Var = Dog = function(){} //Dog inherits from mammals. Dog.prototype = Mammal.prototype; var dog = new Dog(); dog.init();Copy the code
At this point, a dog’s life goes through DNA replication, birth, growth, aging and death. These steps were decided long before it was born. Fortunately, God doesn’t plan all the details of his life. He can still rewrite the growth function to make a different dog.
For a more realistic example, all games in the game hall have login, and in the game, the game ends, and the login and the pop-up after the game ends, these functions should be common. So the first thing you need is a parent class.
JavaScript
var gameCenter = function(){ } gameCenter.ptototype.init = function(){ this.login(); this.gameStart(); this.end(); } gameCenter. Prototype. The login = function () {/ / do something} gameCenter. Prototype. GameStart = function () {/ / empty function, Leave it to subclasses to override} gamecenter.prototype. end= function(){alert (" Welcome to play again "); }Copy the code
To create a new game to fight landlord, simply inherit the gameCenter and override its gameStart function.
JavaScript
Function (){} prototype = gamecenter.prototype; Prototype. GameStart = function(){//do something} (new).init();Copy the code
And so a new game began.
The mediator model
A mediator object allows objects to be loosely coupled without explicit references to each other, and can change their interactions independently.
Arms buyers and sellers, for example, use a trusted intermediary for security purposes. Buyer A gives the money to agent B and gets the arms from the agent, and seller C sells the arms to the agent and gets the money back from the agent. After A trade, A doesn’t even know if C is A monkey or A mammoth. Because of the existence of the intermediary, A may not necessarily buy THE arms of C, but also D, E, F.
A bank can also act as an intermediary between a depositor and a lender. Depositor A doesn’t care who ends up borrowing his money. And lender B doesn’t care whose deposits he’s borrowing from. This transaction is made so convenient by the presence of intermediaries.
The broker pattern is a little similar to the broker pattern. Both are third party objects that connect two objects to communicate. The specific differences can be distinguished from the following figure.
Proxy mode:
The mediator pattern
In the broker pattern, A must know everything about B, while in the mediator pattern, A,B, AND C do not care about the implementation of E,F, and G. And the mediator pattern can connect any number of objects.
Cut back to MVC in the application world, whether it’s struts Action in J2EE. The Controler in spine.js and backbone.js. Both serve as intermediaries. Take backbone as an example. The data in a mode is not determined which view will use it last. The data required by a view can also come from any mode. All binding relationships are determined in the Controler. The mediator takes a complex many-to-many relationship and turns it into two relatively simple one-to-many relationships.
A simple example of code:
JavaScript
var mode1 = Mode.create(), mode2 = Mode.create(); var view1 = View.create(), view2 = View.create(); var controler1 = Controler.create( mode1, view1, function(){ view1.el.find( ''div' ).bind( ''click', function(){ this.innerHTML = mode1.find( 'data' ); } ) }) var controler2 = Controler.create( mode2 view2, function(){ view1.el.find( ''div' ).bind( ''click', function(){ this.innerHTML = mode2.find( 'data' ); })})Copy the code
Twelve iterator patterns
The iterator pattern provides a way to access the elements of an aggregate object sequentially without exposing the internal representation in the method. In JS we often wrap an each function to implement iterators. Array iterators:
JavaScript
forEach = function( ary, fn ){ for ( var i = 0, l = ary.length; i < l; i++ ){ var c = ary[ i ]; if ( fn.call( c, i , c ) === false ){ return false; } }}
forEach( [ 1, 2, 3 ], function( i, n ){
alert ( i );
})Copy the code
Iterators for OBEjCT:
JavaScript
forEach = function( obj, fn ){ for ( var i in obj ){ var c = obj[ i ]; if ( fn.call( c, i, c ) === false ){ return false; } }}
forEach( {"a": 1,"b": 2}, function( i, n ){
alert ( i );
})Copy the code
Xiii Combination Mode
The composite pattern, also known as the partial-whole pattern, combines all objects into a tree structure. This allows the user to perform the same operation on all members only by operating on the topmost interface. A good example of this is jquery objects, as we all know that a jquery object is actually a collection of objects. In an HTML page like this
JavaScript
<div>
<span></span>
<span></span>
</div>Copy the code
We want to unbind events on all nodes
JavaScript
var allNodes = document.getElementsByTagName("*");
var len = allNodes.length;
while( len-- ){
allNodes.unbind("*");
}Copy the code
But if you use jquery, you won’t do it again. We just need $(‘ body ‘).unbind(‘ * ‘); When each element implements the unbind interface, simply calling unbind on the topmost object $(‘ body ‘) will automatically iterate over and call the unbind methods on all the combined elements. For a more concrete example, again, dev.qplus.com’s instant verification form.
Note the following button to modify the data. If any of the fields fail to pass the verification, the button to modify the data will be gray and undoable. This means that after we fill in the form again, we have to verify each field to make sure they are all OK. This code is not difficult to implement.
JavaScript
If (namefield.validata () &&idcard.validata () &&email.validata () &&phone.validata()){alert (" validate OK"); }Copy the code
It seems that we can barely solve the problem of conditional branching with a single appearance mode, but the real problem is that we can’t guarantee the number of fields in the form. Maybe tomorrow the product manager will ask you to delete one or add two. Then this kind of maintenance is obviously not acceptable. A better implementation would be to have a form.validata function that distributes the actual validata operation to each composite object. Form. validata returns false if a field fails. The pseudocode is shown below.
JavaScript
form.validata = function(){
forEach( fields, function( index, field ){
if ( field.validata() === false ){
return false;
}
})
return true;
}Copy the code
14 Memorandum Mode
Memo mode is often used for data caching in JS. A paging control, for example, retrieves a page of data from the server and stores it in the cache. When you go back to the page later, you can use the data in the cache without having to ask the server again. Implementation is relatively simple, pseudo code:
JavaScript
var Page = function(){ var page = 1, cache = {}, data; return function( page ){ if ( cache[ page ] ){ data = cache[ page ]; render( data ); }else{ Ajax.send( 'cgi.xx.com/xxx', function( data ){ cache[ page ] = data; render( data ); }}}}) ()Copy the code
In the responsibility chain mode, one object A sends A request to another object B. If B does not process the request, the request can be forwarded to C. If C does not process the request, the request can be forwarded to D. Until an object is willing to handle the request.
For example, a client asks his boss to write a PHP program. The boss definitely didn’t, and the boss handed it over to the department manager. The department manager did not want to write, and gave it to the project manager. The project manager can’t write it and hands it to the programmer. In the end, the coder does the work.
In this hypothesis, there are several characteristics of the chain of responsibility model.
The boss only deals with the department manager, the department manager only contacts the project manager, and the project manager only finds trouble with the coder. If the coder does not write, the project will be aborted. The client doesn’t know who wrote the program at last. Event bubbling in JS is implemented as a chain of responsibilities. An event is fired on a node and then passed to the root node until it is caught by the node.
16 Enjoy yuan mode
The share mode is mainly used to reduce the number of objects required by the program. For example, almost everyone on our front end has a copy of the Definitive javascript Guide. In terms of saving money, about three should be enough. Put it in the department bookcase, take it when anyone needs it, and return it. If four students need to read it at the same time, go and buy another book.
In WebQQ, when you open the QQ friends list and drop down, you will create a div for each friend (there is far more than one element if you count the child nodes in the div).
If you have 1000 QQ friends, that means 1000 divs will be created from start to finish, at which point some browsers may have faked death. And that’s just a random friend list operation.
So we came up with a solution to remove divs that were already out of sight when the scrollbar rolled. This keeps the page to a certain number of nodes. The problem is that such frequent addition and removal of nodes can also incur significant performance overhead, and it doesn’t feel right.
Now the meta mode is ready to enter. As the name suggests, the share pattern provides some shared objects for reuse. If you take a closer look at the figure above, you only need 10 divs to display your friend information, which is 10 divs that are in the user’s line of sight. These 10 divs can be written as share elements. The pseudocode is shown below.
JavaScript
var getDiv = (function(){ var created = []; var create = function(){ return document.body.appendChild( document.createElement( 'div' ) ); } var get = function(){ if ( created.length ){ return created.shift(); }else{ return create(); }} / * a hypothetical events, used to monitor the div outside just disappear in the line of sight, actually can be done by listening article roll motion position. * / userInfoContainer disappear (function (div) {created. Push (div); }) })() var div = getDiv(); div.innerHTML = "${userinfo}";Copy the code
The idea is simple: put a hidden div in an array. When a div is needed, it is taken from the array. If the array is already empty, create a new one. The divs in this array are the primitives, each of which can be used as a carrier for any user information.
Of course, this is just an example, but the actual situation is a little more complicated, such as when dragging quickly, we may have to set up a buffer for the node.
Xvii State Mode
The state pattern can be used primarily in scenarios where an object’s behavior depends on its state, and where an operation contains a large conditional branch statement
Think back to street Fighter.
There are various states of walking, attacking, defending, falling, jumping and so on, and these states are both related and mutually constrained. You can’t attack and defend when you jump, for example. You can neither attack nor defend when you fall, and you can attack or jump when you walk.
In order to do this kind of logic, the usual if else is necessary. And the number is incalculable, especially if you add a new state that might change from line 10 all the way to line 900.
JavaScript
if ( state === 'jump' ){ if ( currState === 'attack' || currState === 'defense' ){ return false; } }else if ( state === 'wait' ){ if ( currState === 'attack' || currState === 'defense' ){ return true; }}Copy the code
To eliminate the if else and make it easier to modify and maintain, we introduce a state class.
JavaScript
var StateManager = function(){ var currState = 'wait'; var states = { jump: function( state ){ }, wait: function( state ){ }, attack: function( state ){ }, crouch: function( state ){ }, defense: function( state ){ if ( currState === 'jump' ){ return false; } //do something; CurrState = 'defense'; currState = 'defense'; currState = 'defense'; }} var changeState = function(state){states[state] && states[state](); } return { changeState : changeState } } var stateManager = StateManager(); stateManager.changeState( 'defense' );Copy the code
With this state class, condition branches scattered around the world can be centrally managed into a single class, and a new state can be easily added. As the caller, you only need to switch the character’s state through the exposed changeState interface.
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * line 1 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
The 23 design patterns proposed by GOF have been written for the most part. Others are either not applicable in JS, or have native implementations in JS, so they are not explored further. Most of the examples in these two articles came from or were adapted from code at work and in school. My view on design patterns is that you don’t have to learn design patterns. A lot of the code you work with already contains some implementation of design patterns. My process was to read the source code for Prototype and jquery, then go back to the design Patterns book and realize that I had already touched six or seven out of ten.
There is also no need to deliberately use design patterns in actual coding. As with Tokyo Hot 32, there is no need to deliberately use a pose during a friendly papapa. It’s all about needs and feelings.
22 comments