今天看书,了解到一个相较于类继承模式更好的一种编程思维:委托模式。
委托模式的实现原理依托于原型链。一个对象委托于另一个对象,两者共同来完成一件事情。因为有原型链的存在,因此如果将一个对象委托于另一个对象,那么,在委托者的原型链中就可以找到对应的方法和属性。在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( { 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( { 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; } };