Как повторно использовать и переопределить унаследованную функцию JavaScript - PullRequest
5 голосов
/ 30 мая 2011

У меня есть класс для событий мыши.Я использую 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 ]

1 голос
/ 02 ноября 2011

В соответствии с предложениями "Должен ли я поместить функции в их собственный массив и вызывать их по порядку" и "создать свою собственную систему событий", возможно, вы захотите взглянуть на 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!
1 голос
/ 02 ноября 2011

У вас есть несколько вариантов.
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, которое я сделал. Он отлично работал для меня, но не для всех.
1 голос
/ 31 октября 2011

Почему бы не создать свою собственную простую модель событий?

// 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, чтобы он принимал аргумент области действия, а затем использовать call или apply и передать 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 голос
/ 30 мая 2011

Если кто-то просто хочет запустить некоторый код при вызове onclick (), он может просто подключиться к нему. Обратите внимание, что вы также можете подключаться к вызовам функций, а не только к событиям с помощью dojo.connect ().

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

Если он хочет создать новый класс и расширить функцию, вызов this.inherited(arguments); - лучший способ сделать это.

0 голосов
/ 01 ноября 2011

Если я понимаю ваш вопрос, то вы хотите изменить функциональность для конкретных случаев.Если это так, и вам не нужны какие-либо издержки, то я бы положился на переопределение методов при создании экземпляра.

Переопределить модель

ВашКласс :

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();
    }
});
0 голосов
/ 29 октября 2011

__fn становится все грязнее и грязнее.Это, друг мой, дорога в ад.

Это симптоматично использовать подход Волшебная кнопка .Вы должны рассмотреть возможность перехода к реальной модели классов, основанной на наследовании, используя множество библиотек OO js.Тогда вы сможете вызывать $ base.fn () или $ super.fn ()

...