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

时间:2011-05-30 16:31:49

标签: javascript function dojo reusability redefine

我有一个鼠标事件类。我正在使用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();

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

6 个答案:

答案 0 :(得分:1)

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

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

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

答案 1 :(得分:1)

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

// 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并传入{{1或其他一些对象:

this

取决于&amp;在定义事件模型函数的地方,您可以根据您希望分配,更改和触发这些回调的方式,将功能范围限定为对象的特定实例,对象的每个实例,全局等等。

答案 2 :(得分:1)

您有多种选择 1. Promise模式越来越受欢迎(虽然有竞争规格):http://wiki.commonjs.org/wiki/Promises/B。 jQuery有一个接近B的实现,并且有很好的文档记录。它具有最多的选择,它很强大,但最初并不容易包围。

  1. AOP风格的编程可让您在建议之前和之后进行操作。但是,要使其工作,您需要停止将所有内容放在匿名函数中,并决定是在实例化后应用建议还是通过将功能烘焙到类中来规划它们的使用。这需要更多的代码结构规划,但提供了大量的实用工具。
  2. 例如:

    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. 建议使用“事件队列”。这更像是一个活动对象模式,您可以使用一组对象和一个队列管理器来管理任务。这是最容易实现的,但有几个缺点,队列可能会“卡住”而不再处理。我根据我的iPhone应用程序,为这种类型的管理编写了一个名为proto-q(http://code.google.com/p/proto-q/)的库。它对我很有用,但不适合每个人的应用。

答案 3 :(得分:1)

正如“我应该将函数放在自己的数组中并按顺序调用它们”和“构建自己的事件系统”的想法所建议的那样,您可能希望查看捆绑的dojox.lang.aspect库在扩展标准库中。它基本上应该是“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!

答案 4 :(得分:0)

__ fn变得更加混乱和混乱。那个,我的朋友,是通往地狱的道路。

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

答案 5 :(得分:0)

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

覆盖模型

你的班级

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();
    }
});