Remember earlier said to share with you "JavaScript design mode", the delay did not write because I lazy, really busy recently, busy work, busy travel (ah?) , the last few days free, and then the time to cash before the empty vernacular.
Before you discuss design patterns, make sure you have some scripting basics, and if you don't know it, it's a good idea to look at the next article, which I wrote a long time ago about JavaScript-oriented object-oriented programming.
When it comes to design patterns, it's important to focus on "interface design" because the interface design is too much in design mode and is larger than the pattern itself. For the sake of visualization, first describe the form of the interface definition:
Copy Code code as follows:
var interface = new Interface ("interface", [["GetName", 1],["Getage", 1]]);
You can see that the interface function must contain two parameters, and the interface method is defined in a two-dimensional array. The above example defines two interface methods: Getname,getage, both of these methods take a parameter, we look at the implementation code of the interface function in detail, so as to deepen the understanding of the interface.
Copy Code code as follows:
function Interface (name,methods) {
if (Arguments.length!=2) {
Console.log ("parameter must be two");
}
THIS.name = name;
This.methods = [];
if (methods.length<1) {
Console.log ("Second argument cannot be an empty array");
}
for (Var i=0;len=methods.length,i<len;i++) {
if (typeof methods[i][0]!== ' string ') {
Console.log ("First parameter data type must be a string");
}
if (methods[i][1] && typeof methods[i][1]!== ' number ') {
Console.log ("Second parameter data type must be integer");
}
if (methods[i].length = = 1) {
METHODS[I][1] = 0;
}
This.methods.push (Methods[i]);
}
}
It's not hard to see from the code, the definition rule of an interface function: [The 1]interface function can only contain two parameters, the first argument is the interface name, the second is a two-dimensional array [2] The second argument is not allowed to be an empty array [the first argument in the 3]methods parameter must be a string type, To define the method name, the second argument must be an integer type to define the number of parameters of the method [4] When the number of arguments for the method in methods is 0 o'clock, it can be omitted and not written.
Next we're going to build a class that inherits the interface interface defined above, so what do we do? We need to add a new method, see the following code:
Copy Code code as follows:
var ioldfish = function (name,age) {
THIS.name = name;
This.age = age;
Interface.regimplement (This,interface);
}
Ioldfish.prototype.getName = function () {
alert (this.name);
};
Ioldfish.prototype.getAge = function () {
alert (this.age);
};
var fishwl = new Ioldfish ("Old Fish", 27);
Fishwl.getname ();
Interface.regimplement is what we want to add, the role is to let the Ioldfish class code according to the interface Interface, otherwise it will throw an exception in the Firebug console.
Take a look at the specific implementation code for this method:
Copy Code code as follows:
Interface.regimplement = function (object) {
if (arguments.length<2) {
Console.log ("Interface inheritance parameters cannot be less than two");
}
for (var I=1;len = arguments.length,i<len;i++) {
var interface = Arguments[i];
if (Interface.constructor!== interface) {
Console.log ("The third argument must begin with an interface instance");
}
for (Var j=0;len=interface.methods.length,j<len;j++) {
var method = interface.methods[j][0];
if (!object[method] | | typeof Object[method]!== "function" | | object[method].getparameters (). Length!== Interface.methods[j][1]) {
Console.log ("" +method+ "Method interface Mismatch");
}
}
}
}
Read this code, you can easily find: [1]interface.regimplement inherited interface function parameters must have at least two, if there is a third parameter, then this parameter should be Interface interface Instance [2] we go through the Interface interface of the method, Again, match the method one by one in the new class, and if you find that the class that inherits the interface specification is missing a method, you throw an error prompt. [3] The interface also matches the number of parameters, and if the number of parameters in the interface method does not match the number of methods in the new class, an error is thrown.
To match the number of parameters in the method, here we use a getparameters () method, which we extend based on function, and the code is implemented as follows:
Copy Code code as follows:
Function.prototype.getParameters = function () {
var str = this.tostring ();
var paramstr = Str.slice (Str.indexof ("(") +1,str.indexof (")")). Replace (/\s*/g, ' ");
try{
Return (Paramstr.length ==0?) []: Paramstr.split (","));
}
catch (Err) {
Console.log ("illegal function");
}
}
Next, you can put the interface function that you are talking about, The Interface.regimplement function, and the Function.prototype.getParameters function, are integrated into a interface.js file, debugging the new Ioldfish class. What happens when the Getage method is missing from the class? Suggest novice, all kinds of situation imitate, strengthen understanding! If you are convinced that you have fully understood the interface design, then follow me and go down.
Single mode singleton of JavaScript design pattern
Monomer Mode Singleton: This is the most basic design mode, strictly no mode to say, but it is easy to use also very good, Alipay many components are designed through the monomer mode. In fact, in the "Talking about JavaScript Object-oriented programming" in the introduction of prototype inheritance has been used in this mode, here simply with the emphasis on inert monomer, which is not all users need, in a specific scenario will be used in the components have a very good optimization role, He can postpone the instantiation of the component until the user triggers him.
Copy Code code as follows:
var ioldfish = {
Name: ' Old Fish ',
Age:27,
Getname:function () {
alert (name);
},
Getage:function () {
alert (age);
}
}
The above example is one of the simplest monomer patterns, integrating my data into the Ioldfish object literal, forming a module, and playing a role as a namespace.
Copy Code code as follows:
var ioldfish = (function () {
var name = ' Old fish ';
var age = 27;
return{
Getname:function () {
alert (name);
},
Getage:function () {
alert (age);
}
}
})();
Make simple modifications to the first monomer, make name,age a static private variable by closure, ensure that the instantiation is always in memory only, so it is more consistent with the definition of the monomer pattern.
The following is an introduction to inert monomers, less nonsense, and a first look at how we can implement inert monomers:
Copy Code code as follows:
var ioldfish = (function () {
var uniqueinstance;
var name = ' Old fish ';
var age = 27;
function Constructor () {
return{
Getname:function () {
alert (name);
},
Getage:function () {
alert (age);
}
}
}
return{
Isinstance:function () {
if (uniqueinstance = = null) {
Uniqueinstance = Constructor ();
}
return uniqueinstance;
}
}
})();
Ioldfish.isinstance (). GetName ();
The above structure is clear and private, private variable uniqueinstance (identifies whether the class has been instantiated) and private method constructor, returns a public method Isinstance (this method can call the method defined in the private method constructor ), in the form of: Ioldfish.isinstance (). GetName (), the Isinstance () method is used to determine whether it is instantiated, and then the GetName () method is used to obtain the name of the private variable in the closure. The pattern of the application is still a lot of, is not met the page needs to load a large calendar control, but not all users to use it? Isn't it...
The factory model of JavaScript design pattern factory
Factory mode factory: First create an abstract class, then derive the subclass based on this abstract class, and create the factory method in the subclass to postpone the instantiation to the corresponding subclass, to tell the truth, the factory pattern is somewhat far-fetched to use in JavaScript, After all, JavaScript is not like Java in the hard code to bring the trap, to learn only the mode of thinking, not because of patterns and patterns.
As an example of the extreme point, for the tab switch, Drop-down list and other components to add positioning, fade, delay and other effects, we can first define an interface for these components:
var Iwidget = new Interface ("Iwidget", [["AddEffect"]]);
Defining the interface so that the derived subclass inherits, the interface defines a AddEffect method, and when the interface method is implemented, the calling classmate can not pay attention to the code implementation for the AddEffect method in each subclass.
Copy Code code as follows:
var Widget = function () {};
widget.prototype={
Fire:function (model) {
var widget = This.createwidget (model);
Some students ask why the subclass must define an interface method, because the following is called
Widget.addeffect ();
return widget;
},
Show:function () {
Show code specifically implemented
},
Hide:function () {
Hide code specifically implemented
},
Createwidget:function (model) {
Alert (' abstract class, cannot be instantiated ')
}
};
The previous example defines an abstract class widget as the parent of a derived subclass, and since both types of components involve hiding and displaying a container, the show and hide methods are predefined in the parent class to inherit the subclass.
Copy Code code as follows:
var xtab = function () {};
Extend (Xtab,widget);
XTab.prototype.createWidget = function (model) {
var widget;
Switch (model) {
Case ' position ':
Widget = new Xtabposition ();
Break
Case ' Anim ':
Widget = new Xtabanim ();
Break
Case ' delay ':
Default
Widget = new Xtabdelay ();
}
};
var dropDown = function () {};
Extend (Dropdown,widget);
DropDown.prototype.createWidget = function (model) {
var widget;
Switch (model) {
Case ' position ':
Widget = new Dropdownposition ();
Break
Case ' Anim ':
Widget = new Dropdownanim ();
Break
Case ' delay ':
Default
Widget = new Dropdowndelay ();
}
};
Subclasses Xtab and dropdown inherit the parent class and rewrite the Createwidget method, with different subclasses creating different instances based on positioning, fading, and delaying effects, as long as the classes that create these instances implement the AddEffect method agreed in the interface, as to how the method code is implemented, All the same, love how the whole.
Copy Code code as follows:
var xtabposition = function () {};
Xtabposition.prototype ={
Addeffect:function () {
Specific implementation code
}
};
var dropdownposition = function () {};
Dropdownposition.prototype ={
Addeffect:function () {
Specific implementation code
}
};
var dropdowninstance = new DropDown ();
Dropdowninstance.fire (' position ');
And so on, if you need to add these effects to the bubble component, divert, as you can see clearly, this design pattern greatly reduces the coupling between classes and classes, and can implement different auxiliary actions according to specific interaction requirements, but it also inevitably increases the complexity of code implementation. , in fact, this model is not suitable for JavaScript, after all, it is different from Java, there will be no class name hard coding problem, the purpose is to learn his design ideas, so the above examples for reference only, such as without adults, children do not follow.
For JavaScript enthusiasts, what's more valuable should be the "caching (memoization) mechanism" described in the factory model, which shows an example of creating a Xhr object to illustrate the feature, but the effect is clearly not obvious ...
memoization noun explanation: put every execution result of a function into a pair of key values (arrays can also, as the case may be, in the next execution, find out if there is already a corresponding value in the key pair, and if so, return the value directly without actually executing the evaluation part of the function body. Obviously, finding a value, especially in a key-value pair, is much faster than executing a function.
In recursive invocation, the power of the memoization can be better revealed. Here is a classic Fibonacci sequence, which fib (20) performs 21,891 times fib This method, and if it is fib (40), it executes 331,160,281 times.
Copy Code code as follows:
function fib (n) {
if (n < 2) {
return n;
}
return fib (n-1) + fib (n-2);
}
and see how to use memoization to achieve:
Copy Code code as follows:
var Itermemofib = (function () {
var cache = [1, 1];
var fib = function (n) {
if (n >= cache.length) {
Converts a recursive to a
for (var i = cache.length i <= n; i++) {
Cache[i] = Cache[i-2] + cache[i-1];
}
}
return cache[n-1];
}
return fib;
})();
Extend the prototype of a function to Memoize and Unmemoize methods so that you can implement memoize and remove memoize for any function, and, of course, this method should be cautious and not necessarily cached for functions that are not frequently executed:
Copy Code code as follows:
Function.prototype.memoize = function () {
var pad = {};
var self = this;
var obj = arguments.length > 0? Arguments[i]: null;
var memoizedfn = function () {
Save the parameter as an array, and as a key, cache the result of the function execution as a value
var args = [];
for (var i = 0; i < arguments.length; i++) {
Args[i] = arguments[i];
}
if (!) ( args in pad)) {
Pad[args] = self.apply (obj, arguments);
}
return Pad[args];
}
Memoizedfn.unmemoize = function () {
return self;
}
return MEMOIZEDFN;
}
Function.prototype.unmemoize = function () {
Alert ("Attempt to unmemoize a unmemoized function.");
return null;
}
Use method: Fib.memoize ();
The combination mode of JavaScript design pattern
Combination mode: The design pattern can be used to add properties and methods to the combination object, and to get the properties and methods of the objects by recursive batch. For example, we are now going to create a list of banks dynamically, grouped by bank type into online banking classes, cartoon banking classes, and can configure whether they are displayed. How do you implement the combination mode?
The first step is to define the interface, because to do some kind of bank or even a bank to show the configurable, then we first agreed to 2 interfaces, Showbank and Hidebank.
var icarditem = new Interface ("Icarditem", [["Showbank"],["Hidebank"]]);
Next, we define the combination object of the card and set the basic method Add,remove,getchild of the composite object, because this class inherits the Icarditem interface class, so the Showbank,hidebank interface method is also defined.
Copy Code code as follows:
var cardmain = function (ID) {
This.cards = [];
This.element = document.createelement ("div");
This.element.id = ID;
Interface.regimplement (This,icarditem);
};
Cardmain.prototype = {
Add:function (Card) {
This.cards.push (card);
This.element.appendChild (Card.getelement ());
},
Remove:function (Card) {
for (i=0;len=this.cards.length,i<len;i++) {
if (cards[i] = = Card) {
This.cards.splice (i,1);
Break
}
This.element.removeChild (Card.getelement ());
}
},
Getchild:function (i) {
return this.cards[i];
},
Getelement:function () {
return this.element;
},
Showbank:function () {
This.element.style.display = "block";
for (i=0;len=this.cards.length,i<len;i++) {
This.cards[i].showbank ();
}
},
Hidebank:function () {
This.element.style.display = "None";
for (i=0;len=this.cards.length,i<len;i++) {
This.cards[i].hidebank ();
}
}
};
Then define the Leaf object class Banklogo to create the bank logo, where the bank logo is labeled with Class A tag:
Copy Code code as follows:
var Banklogo = function (bankclassname) {
This.element = Document.createelement ("a");
This.element.className = Bankclassname;
Interface.regimplement (This,icarditem);
};
Banklogo.prototype ={
Showbank:function () {
This.element.style.display = "block";
},
Hidebank:function () {
This.element.style.display = "None";
},
Getelement:function () {
return this.element;
}
};
Finally, set up a monomer object, the operation of the bank's related information into a module to facilitate the call:
Copy Code code as follows:
var bankaction ={
Banklist:[],
Addbank:function (Card) {
This.bankList.push (card);
},
Innerbank:function (Conid) {
for (i=0;len=this.banklist.length,i<len;i++) {
var cardobj =this.banklist[i].getelement ();
}
document.getElementById (Conid). appendchild (Cardobj);
}
};
To the implementation of the link, the instantiation generated a containing all the cards of the outermost container, and then according to the card class, respectively, generate a bank card and cartoon card container, and finally generate the cards of each instance, and hierarchical relationship to form a DOM structure:
Copy Code code as follows:
var bankdivt = new Cardmain ("Paycard");//Create outermost container
var ebankcard = new Cardmain ("Ebankcard");//Create NET Bank card container
var ktcard = new Cardmain ("Ktcard");//Create a Cartoon class bank card container
var Ccbbank = new Banklogo (' EBANK-CMB ');//create bank card of CMB
var Abcbank = new Banklogo (' ebank-abc ');//create ABC bank card
var Abcktbank = new Banklogo (' kt-abc ');//create cartoon ABC Card
Ebankcard.add (Ccbbank);
Ebankcard.add (Abcbank);
Ktcard.add (Abcktbank);
Bankdivt.add (Ebankcard);
Bankdivt.add (Ktcard);
Bankaction.addbank (BANKDIVT);
Bankaction.innerbank ("Banklist");
The list of banks that will be dynamically generated, such as the DOM structure:
Copy Code code as follows:
<div id= "Paycard" >
<div id= "Ebankcard" >
<a class= "EBANK-CMB" ></a>
<a class= "EBANK-ABC" ></a>
</div>
<div id= "Ktcard" >
<a class= "KT-ABC" ></a>
</div>
</div>
The combination mode application is a very good choice when dynamically generating the user interface, which simplifies the adhesion code to a large extent and promotes maintainability. However, it is still prudent to use, after all, when the leaves of many objects, the recursive return is a problem of performance.
The decorator pattern of JavaScript design patterns
Decorator Mode: You can create new features for an object without creating a new subclass. Examples: Pay the Treasure cashier red envelopes combined with balance payment application scenarios.
var ieconomics = new Interface ("Ieconomics", [["GetPrice"]]);
First, a component class is created, and the object instantiated by the component is passed as a parameter to the adorner class so that the adorner invokes the methods in the component.
Copy Code code as follows:
var economic = function () {
Interface.regimplement (This,ieconomics);
};
economic.prototype={
Getprice:function () {
Code implementation
}
};
Then create an adorner abstract class to be the parent of the derived adorner option class:
Copy Code code as follows:
var economicsdecorator = function (economic) {
This.economic = economic;
This.regimplement (Economic,ieconomics);
};
economicsdecorator.prototype={
Getprice:function () {
return This.economic.getPrice ();
}
};
Finally, based on the above abstract class, derive an adorner option class:
Copy Code code as follows:
Red Envelope Decorator Option class
var coupon = function (economic) {
Call a constructor that is decorated with an abstract class
Economicsdecorator.call (this,economic);
};
Extend (Coupon,coupondecorator);
Coupon.prototype=function () {
Rewriting the GetPrice method
Getprice:function () {
Return This.economic.getPrice ()-this.getcoupon ();
},
Getcoupon:function () {
Achieve the total price of red envelopes
}
};
var mycoupon = new Economic ();
Mycoupon = new coupon (mycoupon);
Implementing the adorner pattern is as simple as creating an instance of a component mycoupon and then passing the object as a parameter to the Adorner option class coupon. You'll find that I've assigned values to variable mycoupon in both of the code, because they all implement the same interface class and they can be used interchangeably.
See here forestall students may find that we have a new Getcoupon method in the coupon class, and now we don't have any problems, but if we continue to create a shopping volume decorator option class, and then combine the red envelopes together?
Copy Code code as follows:
Shopping Volume Decorator option class
var voucher = function (economic) {
Economicsdecorator.call (this,economic);
};
Extend (Voucher,coupondecorator);
Voucher.prototype=function () {
Getprice:function () {
Return This.getprice ()-This.getvoucher ();
},
Getvoucher:function () {
Get the discount volume total price to realize
}
};
var mycoupon = new Economic ();
Mycoupon = new coupon (mycoupon);
Mycoupon = new voucher (mycoupon);
In this scene below Getcoupon method has been found, this is because voucher decorate Mycoupon, its parent class Economicsdecorator does not contain Getcoupon method, that nature is not to get, then how to do?
To analyze the decorator abstract class Economicsdecorator, we pass a mycoupon reference as a parameter, we can do some tricks through this parameter, get the new added method.
Copy Code code as follows:
var economicsdecorator = function (economic) {
This.economic = economic;
This.interface = Ieconomics;
For (var k in this.economic) {
if (typeof This.economic[key]!== "function") {
Continue
var i;
for (i = 0;len = This.interface.methods.length,i < Len; i++) {
Whether to include this method in the interface class by traversing, if the include returns the next
if (key = = This.interface.methods[i][0]) {
Break
}
}
if (I < this.interface.methods.length)
Continue
var decorator = this;
To define a new method using anonymous function invocation
(function (methodname) {
Decorator[methodname] = function () {
return Decorator.economic[methodname] ();
};
}) (key);
}
}
}
This.regimplement (Economic,ieconomics);
};
economicsdecorator.prototype={
Getprice:function () {
return This.economic.getPrice ();
}
};
Looking at the code above, we made some modifications to the adorner abstract class to make sure that once the new method is defined in the Adorner option class, it can be defined dynamically in the adorner abstract class. Here is only to provide a way to use the decorator mode of thinking, the specific implementation of the code far more complex than this, as the project is still in development, demo temporarily do not provide, Alipay new checkout after the release, will be with you to do a detailed design sharing.
The bridging mode of JavaScript design patterns
Bridging mode: Separates the abstraction from its implementation so that the two can change independently. In fact, it's simple to add a bridge between the API and the specific events, reducing the coupling between the API and the class and objects that use it.
In fact, for most students bridging mode is not unfamiliar, the following this.getname is a bridging method, he is an external access interface, his internal implementation is through access to internal private variables to achieve, this method played a bridge between the external and internal communication.
Copy Code code as follows:
var ioldfish = function () {
var name = ' Old fish ';
This.getname = function () {
alert (name);
}
}
The most common use of bridging mode is in the event listener callback function. The following is an API interface function to get user information:
Copy Code code as follows:
function GetUserInfo (userid,callback) {
Asyncrequest (' Get ', ' userinfo?userid= ' +userid,function (resp) {
Callback (Resp.responsetext);
});
}
What we're going to do next is to build a bridge between this API and the triggering of an event.
Addevent (element, ' click ', Bridgemethod);
function Bridgemethod (e) {
GetUserInfo (This.userid,function () {
callback function Implementation code
});
}
Here in the element object Click when the trigger function is not getiserinfo, but a new bridge method Bridgemethod, through this layer bridge makes API interface functions and click events relatively independent, so that greatly broaden the scope of the API application.
JavaScript design mode Adapter mode
Adapter mode: For example, you maintain a system, has been using the prototype framework, but now intend to introduce the Yui framework, how to make two of the framework of the smooth over?
, for example, how to convert the $ method in prototype into the Get method in Yui:
Copy Code code as follows:
function $ () {};
function Yahoo.util.dom.get=function (EL) {};
function Prototypetoyuiadapter () {
return YAHOO.util.Dom.get (arguments);
}
You want to use Yui's Get method in prototype, only need to do the following affirmation:
$ = Prototypetoyuiadapter;
In this case, in the prototype can use Yui in the Get method. I am not very advocating this model, so do not do more elaboration, in fact, I do not think that the million, we do not need to use this model, as a responsible designer, I would rather do code refactoring also do not want to use the model, can only be used as a helpless under the transition scheme.
The façade mode of JavaScript design pattern, observer mode
Façade mode: This should be used in all script framework, the most basic design pattern, just find a frame of the definition of a good way to see the line, such as the Yui SetStyle methods and so on. There is no more elaboration here.
Observer mode: The design pattern used in JavaScript seems more far-fetched, not very understanding, here is not to say, lest misleading, if the experience of the liberal enlighten.
The day is spent in this blog, as if there are a lot of want to write, it seems to be the mind to write things clearly or not easy, and then tidy up again, please look forward to!