形式8-模版要领形式
模版要领形式是一种基于继续的设想形式。主要由两部分组成:
笼统父类:包括子类的算法框架和一些通用的详细要领;
详细完成的子类: 包括关于父类中笼统要领的完成,继续父类的全部算法完成要领,而且能够重写父类中的要领。
在类似于java如许的面向对象语言中,笼统类的运用在这个设想形式中异常重要。由于在编译的时刻会对继续笼统类的子类举行检测,要求必须要对笼统要领举行完成。然而在javascript中没有范例搜检,所以要保证子类完成了一切笼统父类的笼统要领,能够在运行时举行检测,即让笼统要领抛出毛病。
示例:
var Beverage = function() {}
Beverage.prototype.boilWater = function(){
console.log("boil water");
}
Beverage.prototype.brew = function (){
throw new Error("you must define function brew");
}
Beverage.prototype.pourInCup = function (){
throw new Error("you must define function pourInCup");
}
Beverage.prototype.addCondiments = function (){
throw new Error("you must define function addCondiments");
}
Beverage.prototype.customerWantsCondiments = function(){
throw new Error("you must define function customerWantsCondiments");
}
//泡饮料的递次和步骤是定的,算是一个子类通用的算法
Beverage.prototype.init = function(){
this.boilWater();
this.brew();
this.pourInCup();
if(this.customerWantsCondiments){
this.addCondiments();
}
};
var Tea = function(){};
Tea.prototype = new Beverage();
Tea.prototype.brew = function(){
console.log("brew-up");
}
Tea.prototype.pourInCup = function(){
console.log("pour tea");
}
Tea.prototype.addCondiments = function(){
console.log("add sugar and milk");
}
Tea.prototype.customerWantsCondiments = function() {
return window.confirm("Do you need condiments?");
}
var tea = new Tea();
tea.init();
var Coffee = function(){};
Coffee.prototype = new Beverage();
Coffee.prototype.brew = function(){
console.log("brew coffee");
}
Coffee.prototype.pourInCup = function(){
console.log("pour coffee");
}
Coffee.prototype.addCondiments = function(){
console.log("add lemon");
}
Coffee.prototype.customerWantsCondiments = function() {
return window.confirm("Do you need condiments?");
}
var coffee = new Coffee();
coffee.init();
这个设想形式是有利于体系的拓展的,而且相符开放-关闭准绳。别的,我们在js中不一定非要运用继续的体式格局来完成这个设想形式,也能够经由过程传入高阶函数作为参数来完成,用以替代父类中的笼统函数。
形式9-享元形式
享元形式是为了优化机能而存在的。假定体系中存在大批类似的对象而致使内存斲丧太高,享元形式就异常有用了。
享元形式包括两种状况(即属性):
内部状况:
-存储于对象内部。 -能够被一些对象同享。 -独立于详细的场景,平常不会转变。
外部状况:
取决于详细的场景,并依据场景而变化,外部状况不能被同享。
剥离了外部状况的对象成为同享对象,外部状况在必要时被传入同享对象来组装成一个完全的对象。体系中能够存在的最大享元对象个数即是差别内部状况的组合数。
示例:
//这个享元只要一个内部状况
var Model = function( sex ){
this.sex = sex;
};
Model.prototype.takePhoto = function(){
console.log( 'sex= ' + this.sex + ' underwear=' + this.underwear);
};
//离别建立一个男模特对象和一个女模特对象:
var maleModel = new Model( 'male' ),
femaleModel = new Model( 'female' );
//给男模特顺次穿上一切的男装,并举行照相:
//注:这里我们是在须要的时刻才传入外部状况
for ( var i = 1; i <= 50; i++ ){
maleModel.underwear = 'underwear' + i;
maleModel.takePhoto();
};
//一样,给女模特顺次穿上一切的女装,并举行照相:
for ( var j = 1; j <= 50; j++ ){
femaleModel.underwear = 'underwear' + j;
femaleModel.takePhoto();
};
运用:
文件上传,只依据上传组件的差别来(运用工场)新建uploader对象(只含有一个uploadType内部状况,一样uploadType的被同享),而文件信息等贮存在外部,只当须要(如删除文件)时才(经由过程uploadManager)将外部状况传入内部。
对象池是另一种机能优化的计划,其头脑是建立一个池子用来寄存余暇对象,当须要运用该对象时从池子中取,假如没有则建立,用完以后放回。不过与享元形式差别的是它不会辨别内部状况和外部状况。
形式10-职责链形式
头脑是将能够处置惩罚要求的对象连成一个链,要求者只需晓得第一个对象,然后将要求沿着链顺次通报直到碰到能够处置惩罚该要求的对象。这就使得要求发出着和要求接受者之间解耦,也就是说要求发出者没必要晓得哪一个对象能够处置惩罚要求,防止了在一个函数中运用大批的if else推断。
示例:
var handler1 = function(params){
if(params === true) { // check condition
console.log("request solved by handler1");
} else {
return false; // condition not valid
}
}
var handler2 = function(params){
if(params === true) { // check condition
console.log("request solved by handler2");
} else {
return false; // condition not valid
}
}
var handler3 = function(params){
if(params === true) { // check condition
console.log("request solved by handler3");
} else {
return false; // condition not valid
}
}
var Chain = function( fn ){
this.fn = fn;
this.successor = null;
};
Chain.prototype.setNextSuccessor = function( successor ){
return this.successor = successor;
};
Chain.prototype.passRequest = function(){
var ret = this.fn.apply( this, arguments );
if ( ret === false ){
return this.successor && this.successor.passRequest.apply( this.successor, arguments );
}
return ret;
};
//挪用next函数能够手动通报要求,用于异步处置惩罚
Chain.prototype.next= function(){
return this.successor && this.successor.passRequest.apply( this.successor, arguments );
};
var chain1 = new Chain(handler1);
var chain2 = new Chain(handler2);
var chain3 = new Chain(handler3);
//指定节点在职责链中的递次
chain1.setNextSuccessor(chain2);
chain2.setNextSuccessor(chain3);
//把要求通报给第一个节点:
chain1.passRequest(someParams);
职责链形式使得各个链结点之间能够拆分重组,便于插进去或删除结点。而且要求不一定要从第一个结点最先。同时,要防止职责链太长带来的机能题目。
别的,能够应用js的函数式特征将函数“链接”起来完成职责链形式,即为Function对象的原型增添after函数。
运用:
差别浏览器文件上传控件的挑选,DOM事宜冒泡等。
形式11-中介者形式
中介者形式是为了消除对象之间的强耦合关联,一切对象都经由过程中介者通讯而不再互相援用。
示例:
假定一个网页中几个DOM元素的值配合决议一个按钮的有用性,
比方input必需有用,select1和select2必需挑选
传统的要领中,我们须要为这三个DOM元素增添值转变监听函数,而且在每一个监听函数中都要对峙别的两个DOM的值。这时候,假如我们须要到场一个input2,就须要将一切其他DOM元素的监听函数举行修正。
假如运用中介者形式,我们能够建立一个mediator对象,而且供应一个向其发送音讯的托言。然后,我们为其他DOM建立的监听函数中只须要向中介者发送一个音讯而且把this作为参数通报通知中介者是谁发的音讯。而中介者的完成中,只须要对收到的差别音讯举行处置惩罚即可,假如须要增添新的关联DOM,也只须要稍稍修正mediator的代码而不会须要修正其他DOM的监听函数了。
总之,中介者形式使对象之间的网状援用关联变成了一对多的关联,满足一个对象尽量少地相识其他对象的准绳。然则该形式引入了中介者对象,也会构成一些内存斲丧。
形式12-装潢者形式
装潢者形式用于动态地给对象增添职责。
为了保证在实行原函数和this指向发作转变,我们须要引入代办函数。代办函数的功用是在原函数实行之前或以后,实行别的的函数。
示例:
运用AOP,转变Function对象的原型
Function.prototype.before = function(beforefn){
var _self = this;
return function(){
beforefn.apply(this, arguments); //确保this指向稳固
this.apply(this, arguments);
}
}
var func1 = function(){
alert("1");
}
func1 = func1.before(function(){
alert("0");
});
func1(); //先输出0再输出1
不转变原型的做法:
var before = function(fn, beforefn){
return function() {
beforefn.apply(this, arguments);
fn.apply(this, arguments);
}
}
func1 = before(func1, function(){alert("0")});
运用:
关于某些用户操纵(如单击按钮)数据统计上报,转变函数的arguments对象(如ajax传数据时增添属性),表单考证和提交功用的星散等。
这个设想形式使得开辟人员在开辟框架时能够只斟酌对象的稳固和基本功用,其他须要增添的特性功用能够被动态增添。它差别于代办形式的是,代办形式的目标是为了掌握对对象的接见或增添一些功用,而装潢者形式则是为了为对象动态增添功用,而且平常会构成一条长长的装潢链。
值得注意的是,装潢者形式返回的是一个新的函数,因而原函数上的属性会消逝。同时,装潢链也叠加了函数的作用域,太长则会对机能产生影响。
形式13-状况形式
状况形式使得一个对象在其状况转变时转变其行动。也就是说,在差别状况下挪用同一个名字的函数其函数功用是差别的。
在状况形式中,平常有两类对象:context和状况类。context组织函数中应当实例化一切的状况类并作为context的属性,以便context挪用状况类中的要领。而状况类的组织函数应当以context作为参数,以便能够挪用context中转变其state值的接口来对其举行赋值。
比方一个具有差别光强度的灯的掌握代码示例:
var Light = function(){
this.offLightState = new OffLightState( this ); // 持有状况对象的援用
this.weakLightState = new WeakLightState( this );
this.strongLightState = new StrongLightState( this );
this.superStrongLightState = new SuperStrongLightState( this );
this.button = null;
};
Light.prototype.init = function(){
var button = document.createElement( 'button' ),
self = this;
this.button = document.body.appendChild( button );
this.button.innerHTML = '开关';
this.currState = this.offLightState;
this.button.onclick = function(){
self.currState.buttonWasPressed();
} };
//接下来就要定义种种状况类
var OffLightState = function( light ){
this.light = light;
};
OffLightState.prototype.buttonWasPressed = function(){
console.log( '弱光' );
this.light.setState( this.light.weakLightState );
};
// 其他状况类似,都须要定义一个buttonWasPressed要领,略
该设想形式长处是将对象的状况跟对应的要领一起封装在一个类里,使得状况的治理如增添删除等更easy。状况形式与战略形式很类似,都是有context和一些战略/状况类。差别点是战略类之间是平行的,用户须要晓得他们的差别来挑选挪用,而状况类之间的状况转换关联是早就规定好的,用户也没必要晓得其内部差别点。
JavaScript版本的状况机(在js中状况类不一定须要经由过程类来建立,能够运用显式的对象):
var Light = function(){
this.currState = FSM.off; // 设置当前状况
this.button = null;
};
Light.prototype.init = function(){
var button = document.createElement( 'button' ),
self = this;
button.innerHTML = '已关灯';
this.button = document.body.appendChild( button );
this.button.onclick = function(){
self.currState.buttonWasPressed.call( self );
}
};
var FSM = {
off: {
buttonWasPressed: function(){
console.log( '关灯' );
this.button.innerHTML = '下一次按我是开灯';
this.currState = FSM.on;
}
},
on: {
buttonWasPressed: function(){
console.log( '开灯' );
this.button.innerHTML = '下一次按我是关灯';
this.currState = FSM.off;
};
var light = new Light();
light.init();
形式14-适配器形式
适配器形式的作用就是转换不兼容的接口。
示例:
var getStudents = function(){
//这个函数返回一个对象的数组
var arr = [
{"id": 0,
"name": "LiLei"
},
{"id": 1,
"name": "HanMeimei"
}
];
}
var printStudents = function(fn){
var params = fn();
for(var i = 0; i < params.length; i++){
console.log(params[i].name + ":" + params[i].id);
}
}
printStudents(getStudents);
//倘使如今供应门生信息的函数变了,返回值范例也变了:
var getStudentsObj = function () {
var stu = {
"0": "LiLei",
"1": "HanMeimei"
};
}
//adaptor
var stuAdapter = function(oldfn){
var newRes = {};
var stu = oldfn();
for(var i = 0; i < stu.length; i++) {
newRes[stu[i].id] = stu[i].name;
}
return function(){
return newRes;
}
}
printStudent(stuAdapter(oldfn));
装潢者形式与代办形式的构造与适配器形式很像,都是用一个对象来包装另一个对象,但差别点仍然是他们的企图。适配者形式只是转换接口,不须要晓得对象的详细完成。
P.s. 本文总结自《JavaScript设想形式与开辟实践》,曾探著