5

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

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


wrt 赏金,我想要专家建议如何最好地解决程序-用户交互。如果程序提供了一个关键功能,例如在画布上画一个圆圈,那么用户如何最好地与之交互。如果用户想在圆圈后面画一个三角形怎么办?圆圈前面有一个正方形?然后程序必须提供 pre 和 post 方法,如下所示:

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

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

4

6 回答 6

1

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

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

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

于 2011-05-30T16:41:29.420 回答
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方法以接受范围参数,然后使用callorapply并传入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);
};

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

于 2011-10-31T15:08:26.297 回答
1

你有几个选择。
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 应用程序。它对我很有用,但并不适合每个人的应用程序。
于 2011-11-02T12:39:48.110 回答
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!
于 2011-11-02T14:07:04.753 回答
0

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

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

于 2011-10-29T06:42:34.870 回答
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();
    }
});
于 2011-11-01T17:49:07.550 回答