委托模式

今天看书,了解到一个相较于类继承模式更好的一种编程思维:委托模式。

委托模式的实现原理依托于原型链。一个对象委托于另一个对象,两者共同来完成一件事情。因为有原型链的存在,因此如果将一个对象委托于另一个对象,那么,在委托者的原型链中就可以找到对应的方法和属性。在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;
       }
};
posted @ 2016-05-12 00:10  兔昵  阅读(339)  评论(0编辑  收藏  举报