如何重用和覆盖继承的javascript函数



我有一个鼠标事件类。我正在使用dojo b/c我喜欢它的OO方法

dojo.declare("MouseObject", null, {
  constructor: function(){},
  onclick : function(){...},
  _onClick : function(){...}
});

_onClick()监听窗口生成的鼠标按下/释放事件,并确定是否发生了单击。如果有,则调用onClick()onClick()执行所有可能的点击所共有的功能,因此每次用户点击时都需要调用它。

有时用户可能想要扩展onClick()的功能。有没有办法在不复制粘贴的情况下合并原始功能?我能想到的两种方式都不喜欢,那就是

dojo.declare("MouseObjectChild", [MouseObject], {
  constructor: function(){},
  onclick : function(){this.inherited(arguments);...}
});

它的缺点是我必须不断地创建我并不真正需要的新类,并且两个添加了一个中间函数

dojo.declare("MouseObject", null, {
      constructor: function(){},
      onclick : function(){this._onClick()...}, //child onClick
      _onClick : function(){...}, //parent onClick
      __onClick : function(){...} //listener
    });

但这看起来不像是好的代码


我想得到关于如何最好地处理程序用户交互的专家建议。如果程序提供了一个关键功能,比如在画布上画一个圆圈,那么用户如何最好地与之交互。如果用户想在圆后面画一个三角形怎么办?圆圈前面的正方形?然后该程序将不得不提供这样的预处理和后处理方法:

beforeDraw();
draw();
afterDraw();

这被认为是好的设计吗?我应该把函数指针放在数组中并按顺序调用它们吗?

正如"我应该把函数放在他们自己的数组中并按顺序调用它们吗"one_answers"构建你自己的事件系统"的想法所建议的那样,你可能想看看扩展标准库中捆绑的dojox.lang.aspx库。它基本上应该是"dojo.connect"解决方案的更高级版本。

dojo.require('dojox.lang.aspect');
var aop = dojox.lang.aspect;
function log(msg){ return function(){
    console.log(msg);
};}
var obj = {
   draw : log('draw circle');
};
obj.foo();
//prints
//  draw circle
aop.advise(obj, 'draw', [
    {before: log('draw triangle')},
    {after: log('draw a square')},
    {after: log('done!')}
]);
obj.foo();
//prints
//  draw a triangle
//  draw a circle
//  draw a square
//  done!

如果有人只是想在调用onclick()时运行一些代码,他可以直接连接到它。注意,你也可以使用dojo.connect()连接到函数调用,而不仅仅是事件。

dojo.connect(obj, 'onclick', function(){
  // Some extra code that runs when obj.onclick() is called
});

如果他想创建一个新的类并扩展调用this.inherited(arguments);的功能是最好的方法。

为什么不创建自己的简单事件模型呢?

// store our callbacks
var hooks = {};
// add a callback
var hook = function(name,fn){
  if(typeof hooks[name] === 'undefined'){
    hooks[name] = [];
  }
  hooks[name].push(fn);
  return true;
};
// remove a callback
var unhook = function(name,fn){
  if(typeof hooks[name] === 'undefined'){ return false }
  var i, u = hooks[name].length;
  for(i=0;i<u;i++){
    if(hooks[name][i] === fn){
      hooks[name].splice(i,1);
      return true;
    }
  }
};
// trigger a callback
var callHook = function(name, data){
   if(typeof hooks[name] === 'undefined'){ return false }
   var i, u = hooks[name].length;
   for(i=0;i<u;i++){
     hooks[name][i](data);
   }
};

稍后(示例通过上下文):

hook('beforeDraw',function(ctx){ 
  console.log('I am called prior to drawing');
});
hook('afterDraw',function(ctx){
  console.log('I am called after drawing');
});
var drawCircle = function(){
  callHook('beforeDraw', ctx);
  // drawing code here
  callHook('afterDraw', ctx);
};

或者,如果您想将范围传递给回调,可以更改callHook方法以接受范围参数,然后使用callapply并传入this或其他对象:

var callHook = function(name, scope, data){
   if(typeof hooks[name] === 'undefined'){ return false }
   var i, u = hooks[name].length;
   for(i=0;i<u;i++){
     hooks[name][i].call(scope, data);
   }
};
// later
var drawCircle = function(){
   callHook('beforeDraw', this, ctx);
   // or
   callHook('beforeDraw', someObject, ctx);
};

取决于&在定义事件模型函数的地方,您可以将功能范围扩展到对象的特定实例、对象的每个实例、全局等等,具体取决于您希望如何分配、更改和触发这些回调。

您有几个选项
1.Promise模式越来越受欢迎(尽管有相互竞争的规格):http://wiki.commonjs.org/wiki/Promises/B.jQuery有一个接近B的实现,并且有很好的文档记录。它有最多的选择,它很坚固,但一开始不容易让你不知所措。

  1. AOP风格的编程允许您篡改之前和之后的建议。然而,要做到这一点,您需要停止将所有内容放入匿名函数中,并决定是在实例化后应用建议,还是通过将功能烘焙到类中来计划使用建议。这需要在代码结构上进行更多的规划,但提供了大量的实用性

例如:

var MYAPP = {
  onClick: function () {
    //do something...
  }
};
dojo.declare("MouseObject", null, {
  constructor: function(){},
  onclick : function(){...},
  _onClick : function(){
    return MYAPP.onClick.apply(this, arguments);
  }
});

由于我的类每次都调用MYAPP(是的,这需要范围界定费用),我可以操作挂在MYAPP上的函数,任何使用它的人都会受益。

MYAPP.after("onClick", function () {...}); //lots of ways to write .after, just google some AOP js or use a library

另一面:

var MYAPP = {
  onClick: function () {
    //do something...
  }
};
dojo.declare("MouseObject", null, {
  constructor: function(){},
  onclick : function(){...},
  _onClick : MYAPP.onClick
});

现在我无法更改MYAPP并查看MouseObject或其实例中的更改,因为该函数已通过引用传递给MouseObject。我只能用AOP更改实例(或者通过更改类上的原型来更改所有未来的实例)。

类似于:

var m = new MouseObject();
m.after("onClick", function () {...}); //you need to add .after to your class or use an apply pattern from library code

它只是取决于你认为它将如何使用:某些实例,所有未来实例(直到关闭),或者一切。

  1. 建议使用"事件队列"。这更像是一个活动对象模式,使用对象数组和队列管理器来管理任务。这是最容易实现的,但也有一些缺点,队列可能会"堵塞",不再处理。我为这种类型的管理写了一个库,叫做proto-q(http://code.google.com/p/proto-q/),基于我做的一个iPhone应用程序。它对我来说效果很好,但并不是每个人都适用

__fn变得越来越混乱。我的朋友,那是通往地狱的路。

这是使用魔术按钮方法的症状。您应该考虑使用许多OO js库转移到实际的基于继承的类模型。然后您就可以调用$base.fn()或$super.fn(

如果我理解您的问题,那么您需要针对特定情况修改功能。如果是这种情况,并且您不想要任何开销,那么我将依赖于实例化时重写方法。

覆盖模型

你的班级

dojo.declare("some.path.DrawingThing", [dijit._Widget], {
    draw: function () {
        this.inherited(arguments); // Call super class methods
        // override this method to do specialized things that're specific 
        // to an instance.
    }
});

客户端实例化

var drawer = new some.path.DrawingThing({
    draw: function () {
        beforeDraw();
        // Access the pre-existing functionality in a somewhat dirty way.
        this.constructor.prototype.draw.apply(this, arguments);
        afterDraw();
    }
});

如果你遵循这个模型,代码会更少,但也不那么干净。另一种选择是编写一个真正的API,请参见下文。

事件模型

如果你想要一个真正的API,那么我会提供事件:

// Dojo 1.6 style declaration.
dojo.declare("some.path.DrawingThing", [dijit._Widget], {
    onBeforeDraw: function(){
        // Hook into this method to do something before draw
    },
    onDraw: function(){
        // Hook into this method to do something after draw
    },
    draw: function () {
        // This method is actually called when the draw even fires
        this.onBeforeDraw();
        this.inherited(arguments); // Call super class methods
        this.onDraw();
    }
});

相关内容

  • 没有找到相关文章

最新更新