Cómo reutilizar y anular la función heredada de JavaScript
function dojo (6)
Tengo una clase para eventos de mouse. Estoy usando dojo b / c Me gusta su enfoque OO
dojo.declare("MouseObject", null, {
constructor: function(){},
onclick : function(){...},
_onClick : function(){...}
});
_onClick()
escucha los eventos de presionar / soltar mouse generados por la ventana y determina si se ha producido un clic. Si lo tiene, se llama a onClick()
. onClick()
lleva a cabo la funcionalidad común a todos los clics posibles, por lo que debe llamarse cada vez que el usuario haga clic.
a veces el usuario puede querer ampliar la funcionalidad de onClick()
¿Existe alguna forma de incorporar la funcionalidad original sin copiarla pegándola? Dos maneras en que puedo pensar, ninguna de las cuales me gusta, son
dojo.declare("MouseObjectChild", [MouseObject], {
constructor: function(){},
onclick : function(){this.inherited(arguments);...}
});
que tiene el inconveniente de que tengo que seguir creando nuevas clases que realmente no necesito, y dos agregando una función intermedia
dojo.declare("MouseObject", null, {
constructor: function(){},
onclick : function(){this._onClick()...}, //child onClick
_onClick : function(){...}, //parent onClick
__onClick : function(){...} //listener
});
pero esto no se ve como un buen código
Con la recompensa, quiero un consejo experto sobre la mejor forma de abordar la interacción entre el programa y el usuario. Si el programa proporciona una función crucial, como dibujar un círculo en el lienzo, ¿cómo puede el usuario interactuar mejor con eso? ¿Qué pasa si el usuario quiere dibujar un triángulo detrás del círculo? Un cuadrado en frente del círculo? Entonces el programa tendría que proporcionar los métodos pre y post así:
beforeDraw();
draw();
afterDraw();
¿Se considera esto un buen diseño? ¿Debería poner punteros a las funciones en una matriz y llamarlos en orden?
Si alguien solo quiere ejecutar un código cuando se llama a onclick (), simplemente puede conectarse a él. Tenga en cuenta que también puede conectarse a llamadas de función no solo a eventos con dojo.connect ().
dojo.connect(obj, ''onclick'', function(){
// Some extra code that runs when obj.onclick() is called
});
Si desea crear una nueva clase y extender la característica invocando this.inherited(arguments);
es la mejor manera de hacerlo.
__fn se vuelve más desordenado y desordenado. Eso, mi amigo, es el camino al infierno.
Es sintomático utilizar el enfoque de Magic Pushbutton . Debería considerar mudarse a un modelo de clase basado en herencia real utilizando muchas de las bibliotecas OO js. Entonces, podría llamar a $ base.fn () o $ super.fn ()
¿Por qué no creas tu propio modelo de evento simple?
// 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);
}
};
Luego, más tarde (el ejemplo pasa el contexto):
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);
};
Alternativamente, si quiere pasar el alcance a las devoluciones de llamada, puede cambiar el método callHook
para aceptar un argumento de alcance, y luego usar la call
o apply
y pasar this
u otro objeto:
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);
};
Dependiendo de cómo y dónde defina las funciones del modelo de eventos, puede extender la funcionalidad a instancias específicas de un objeto, cada instancia de un objeto, globalmente, etc., dependiendo de cómo quiera asignar, cambiar y activar esas devoluciones de llamada.
Si entiendo su pregunta, quiere modificar la funcionalidad para casos específicos. Si este es el caso, y no quiere ninguna sobrecarga, entonces confiaría en los métodos generales sobre la creación de instancias.
Anular modelo
Tu clase :
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.
}
});
Ejemplo de cliente :
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();
}
});
Si sigues este modelo, hay menos código, pero no está tan limpio. La otra opción es escribir una API real, ver a continuación.
Modelo de evento
Si quieres una verdadera API, entonces proporcionaría eventos:
// 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();
}
});
Tal como lo sugieren las ideas "¿Debería poner funciones en su propia matriz y llamarlas por orden" y "crear su propio sistema de eventos", es posible que desee echarle un vistazo a la biblioteca dojox.lang.aspect que viene incluida en la lista ampliada? biblioteca estándar. Básicamente debe ser una versión más avanzada de la solución "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!
Tienes varias opciones.
1. El patrón Promesa está ganando popularidad (aunque hay especificaciones que compiten): http://wiki.commonjs.org/wiki/Promises/B . jQuery tiene una implementación cercana a B y está bien documentada. Tiene la mayor cantidad de opciones, es robusto, pero no es fácil de entender al principio.
- La programación al estilo de AOP le permite jugar con los consejos de antes y después. Sin embargo, para que eso funcione, debe dejar de poner todo en funciones anónimas y decidir si aplica consejos después de la instanciación o plan para su uso acumulando la capacidad en la clase. Esto requiere más planificación en la estructura del código, pero ofrece una tonelada de utilidad.
por ejemplo:
var MYAPP = {
onClick: function () {
//do something...
}
};
dojo.declare("MouseObject", null, {
constructor: function(){},
onclick : function(){...},
_onClick : function(){
return MYAPP.onClick.apply(this, arguments);
}
});
Como mi clase llama a MYAPP todo el tiempo (sí, hay un cargo de alcance para esto), puedo manipular la función que cuelga de MYAPP y cualquiera que la use se beneficiará.
MYAPP.after("onClick", function () {...}); //lots of ways to write .after, just google some AOP js or use a library
El otro lado:
var MYAPP = {
onClick: function () {
//do something...
}
};
dojo.declare("MouseObject", null, {
constructor: function(){},
onclick : function(){...},
_onClick : MYAPP.onClick
});
Ahora no puedo cambiar MYAPP y ver los cambios en MouseObject o sus instancias porque la función ha pasado por referencia a MouseObject. Solo puedo cambiar las instancias con AOP (o todas las instancias futuras cambiando el prototipo de la clase).
Algo como:
var m = new MouseObject();
m.after("onClick", function () {...}); //you need to add .after to your class or use an apply pattern from library code
Solo depende de cómo piense que se usaría: ciertas instancias, todas las instancias futuras (hasta que se apaguen) o todo.
- Se sugirió una "cola de eventos". Esto se parece más a un patrón de objeto activo donde utiliza una matriz de objetos y un administrador de colas para administrar tareas. Esta es la más fácil de implementar, pero tiene varios inconvenientes en los que la cola se puede "atascar" y ya no se procesa. Escribí una biblioteca para este tipo de gestión llamada proto-q (http://code.google.com/p/proto-q/), basada en una aplicación de iPhone que hice. Funcionó muy bien para mí, pero no es para la aplicación de todos.