在其他语言中比如 C# 、 Java ,接口方法在定义的时候都是未实现的,而我这里模拟的 JS 接口则是可以在定义的时候实现的。 定义接口: var IC lass Manager = { hasClass:function(className){}, addClass:function(className){} } ; 定义菜单类: var Men u = function(options){ this._element = null; this.setXY = function(x,y){ if (x) this._element.style.left = x + 'px'; if (y) this._element.style.top = y + 'px'; } }; 定义菜单项类: var Men uItem = function(){ this.setText = function(){ }; } 其实菜单类和菜单项类都是基于 HTMLElement 的抽象,因此如果要让他们可以灵活的定义 HTMLElement 样式,那么都需要一组管理 className 的方法。因此 Menu 和 MenuItem 都需要实现 IClassManager 。辅助实现接口的方法: var extend = function(dest, source){ // 实现接口 dest = self || {}; for (property in source) { if (! dest[property] ) // 如果 dest 也就是类没有同名的方法,则用接口默认实现方法。 dest[property] = dest[property]; } return dest; } extend(Menu.prototype, I Class Manager); extend(MenuItem.prototype, I Class Manager); 这样 Menu 和 MenuItem 都具有了 Class 的管理能力。
通过 extend , Menu 和 MenuItem 可以实现任意的接口,并且同时实现多个接口。 在实现接口之前 Menu 和 MenuItem 还可以有一次继承的机会: Menu.prototype = new BaseClass(); // 最简单的继承方式 然后再实现接口:extend(Menu.prototype, I Class Manager);
这样就类似单根继承 + 多接口实现,很像 C# 或者 Java 吧 下面是完整的代码: (function(){ var Event = joyeach.util.Event; var extend = function(dest, source){ // 实现接口 dest = self || {}; for (property in source) { if (! dest[property] ) dest[property] = dest[property]; } return dest; } //C lass Manager Interface var I Class Manager= { hasClass:function(className){ var reg = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)') ; return reg.test(this._element['className']); }, addClass:function(className){ if (this.hasClass(el, className)) return; this._element['className'] = [this._element['className'], className].join(' '); }} /** * 菜单类 */ joyeach.controls.Menu = function(options){ this._element = null; this.setXY = function(x,y){ if (x) this._element.style.left = x + 'px'; if (y) this._element.style.top = y + 'px'; }; this.getXY = function(){ return [parseInt(this._element.style.left), parseInt(this._element.style.top)]; }; this.addItem = function(item){ this._element.appendChild(item._element); Event.fireEvent(this, 'onadditem', {sender:this, item:item}); }; this.getItemAt = function(index){ return this._element.childNodes[index]._control; }; this.removeItemAt = function(index){ var element = this._element.childNodes[i]; this._element.removeChild(element); Event.fireEvent(this, 'onremoveitem', {sender:this, item:element._control});
}; this.removeItem = function(item){ item._element.parentNode.removeChild(item._element); Event.fireEvent(this, 'onremoveitem', {sender:this, item:item});
}; this.show = function(x,y){ if (x || y) this.setXY(x,y); this._element.style.display = 'block'; Event.fireEvent(this, 'onshow', {sender:this}); }; this.hide = function(){ this._element.style.display = 'none'; Event.fireEvent(this, 'onhide', {sender:this});
}; this.close = function(){ this._element.parentNode.removeChild(this._element); Event.fireEvent(this, 'onclose', {sender:this}); }; this._init = function(options){ this._element = document.createElement('div'); options = options || {}; if (options.css) this._element.className = options.css; this.setXY(options.x,options.y); //fire event onpopup Event.fireEvent(this, 'onpopup', {sender:this}); document.body.appendChild(this._element); }; //initialize menu this._init(options); };
/** * 菜单项类. */ joyeach.controls.MenuItem = function(){ this.setText = function(){ }; this.getText = function(){ }; this.setTitle = function(){ }; this.getTitle = function(){ }; this._init = function(){ }; this._init(); }; //菜单类和菜单相类分别实现ICSSManager接口 extend(joyeach.controls.Menu.prototype, I Class Manager); extend(joyeach.controls.MenuItem.prototype, I Class Manager); })();
其中 IClassManager 接口方法实现里面的 this._element 可以在抽象成方法 this.getElement 。
此方案源于 prototype.js 和 yahoo ui library 。 |