今天看书,了解到一个相较于类继承模式更好的一种编程思维:委托模式。
委托模式的实现原理依托于原型链。一个对象委托于另一个对象,两者共同来完成一件事情。因为有原型链的存在,因此如果将一个对象委托于另一个对象,那么,在委托者的原型链中就可以找到对应的方法和属性。在js语言中,委托模式比继承模式更加轻松便捷,易于理解。
举个例子,在新建组件的时候,如果使用类模式定义的话:
// 父类
function Widget(width,height) {
this.width = width || 50;
this.height = height || 50;
this.$elem = null;
}
Widget.prototype.render = function($where){
if (this.$elem) {
this.$elem.css( {
width: this.width + "px",
height: this.height + "px"
} ).appendTo( $where );
}
};
// 子类
function Button(width,height,label) {
// 调用“super”构造函数
Widget.call( this, width, height );
this.label = label || "Default";
this.$elem = $( "<button>" ).text( this.label );
}
// 让Button“继承”Widget
Button.prototype = Object.create( Widget.prototype );
// 重写render(..)
Button.prototype.render = function($where) {
// “super”调用,因为this是根据函数调用时的环境决定值,this指向window,所以需要显示的绑定this对象
Widget.prototype.render.call( this, $where );
this.$elem.click( this.onClick.bind( this ) );
};
Button.prototype.onClick = function(evt) {
console.log( "Button '" + this.label + "' clicked!" );
};
$( document ).ready( function(){
var $body = $( document.body );
var btn1 = new Button( 125, 30, "Hello" );
var btn2 = new Button( 150, 40, "World" );
btn1.render( $body );
btn2.render( $body );
} );
这种方法的问题是因为js中没有类的概念,因此在模拟类的时候会导致this指向问题,同时可能还会有冗余constructor和prototype问题,而且代码看上去很繁琐。
如果使用委托模式定义的话:
var Widget = {
init: function(width,height){
this.width = width || 50;
this.height = height || 50;
this.$elem = null;
},
insert: function($where){
if (this.$elem) {
this.$elem.css( {
width: this.width + "px",
height: this.height + "px"
} ).appendTo( $where );
}
}
};
// 重点在下面这句:因为新建了一个Widget对象,并把它赋值给了Button,因此Button拥有了Widget里的属性,因为原型链将Widget和Button相联系,Button的原型链里包含了Widget。
var Button = Object.create( Widget );
Button.setup = function(width,height,label){
// 委托调用
this.init( width, height );
this.label = label || "Default";
this.$elem = $( "<button>" ).text( this.label );
};
Button.build = function($where) {
// 委托调用
this.insert( $where );
this.$elem.click( this.onClick.bind( this ) );
};
Button.onClick = function(evt) {
console.log( "Button '" + this.label + "' clicked!" );
};
$( document ).ready( function(){
var $body = $( document.body );
// 采用委托模式的新建方法:
var btn1 = Object.create( Button );
// 相当于初始化了btn1,类似于var btn1 = new Button(125, 30, “hello”);
// 但这种方式的好处是不会产生多余的constructor和peototype问题
btn1.setup( 125, 30, "Hello" );
var btn2 = Object.create( Button );
btn2.setup( 150, 40, "World" );
btn1.build( $body );
btn2.build( $body );
} );
代码清晰明了,同时没有多余的constructor问题,因为没有用new来声明变量,但他也会存在问题,就是如果需要迭代调用函数的话,就不要使用匿名函数,因为可能会找不到函数或者无法调用函数,最好使用具名函数定义:
var Widget = {
init: function init (n){
while(n <10){
return init(n++);
}
return n;
}
};