revealing practices practicas patterns pattern patrones node example diseño buenas best and javascript jquery design-patterns design plugins

javascript - practices - Patrón de diseño del complemento jQuery(¿práctica común?) para tratar con funciones privadas



pattern javascript example (4)

Desafortunadamente, los métodos "privados" (o cualquier propiedad para el caso) nunca pueden invocarse con el prefijo "this" en javascript. Todo lo que se llame como this.myFunc(myArgs) debe estar a disposición del público.

Y los métodos "privados" solo se pueden llamar desde el ámbito en el que se definieron.

Su solución original es la única que funcionará. Sí, es un dolor tener que pasar por this , pero no hay más detalles de los que habría si tu pedido imposible fuera posible:

this.fill(''green''); //or fill(this,''green'');

Como puede ver, ambos toman exactamente el mismo número de caracteres en su código.

Lamento decirlo, pero estás atascado con esto como una solución, a menos que quieras crear un nuevo espacio de nombres y hacer que no sea privado, lo que simplemente aumentará la cantidad de código que necesitas para escribir, es decir, lo que llamaste indirectamente. "no directamente expuesto":

this.myplugin.fill(''green'');

... es más detallado, por lo tanto, como que derrota el propósito.

Javascript no es como otros idiomas, no hay miembros "privados" per se, solo miembros accesibles dentro de cierres, que a veces se pueden usar de manera similar a los miembros privados, pero es más una "solución", y no el " verdaderos "miembros privados" que está buscando.

Puede ser difícil aceptar esto (a menudo me cuesta), pero no trates de convertir javascript en lo que entiendes de otros idiomas, tómalo como lo que es ...

He estado desarrollando complementos jQuery desde hace bastante tiempo, y me gusta pensar que ya sé cómo diseñar uno. Sin embargo, uno de los problemas sigue fastidiándome, y es cómo lidiar con las funciones privadas de una manera poderosa pero elegante.

Mis complementos generalmente se ven algo como esto:

(function($) { $.fn.myplugin = function(...) { ... // some shared functionality, for example: this.css(''background-color'', ''green''); ... }; $.fn.mypluginAnotherPublicMethod = function(...) { ... // some shared functionality, for example: this.css(''background-color'', ''red''); ... }; }(jQuery));

Ahora mi pregunta es: ¿cómo secar cuidadosamente esa funcionalidad compartida? Una solución obvia sería ponerlo en una función dentro del espacio de nombres del complemento:

var fill = function($obj, color) { $obj.css(''background-color'', color); };

Aunque esta solución es efectiva y está muy bien diseñada, realmente no me gusta. Por una simple razón: tengo que pasarle el objeto jQuery. Es decir, tengo que llamarlo así: fill(this, ''red''); , mientras que me gustaría llamarlo así: this.fill(''red'');

Por supuesto que podríamos lograr este resultado simplemente fill jQuery.fn . Pero eso se siente muy incómodo. Imagine tener diez complementos desarrollados en base a este enfoque y cada complemento que coloca cinco de esas funciones "privadas" en el espacio de nombres de la función jQuery. Termina en un gran desastre. Podríamos mitigar prefijando cada una de estas funciones con el nombre del complemento al que pertenecen, pero eso realmente no lo hace más atractivo. Se supone que estas funciones son privadas para el complemento, por lo que no queremos exponerlas al mundo exterior (al menos no directamente).

Entonces, mi pregunta es: ¿alguno de ustedes tiene sugerencias sobre cómo obtener lo mejor de ambos mundos? Es decir; el código de complemento puede invocar funciones de complemento ''privadas'' de forma similar a this.fill(''red'') (o this.myplugin.fill(''red'') o incluso this.myplugin().fill(''red'') etc.), al tiempo que previene la contaminación del espacio de nombres de la función jQuery. Y, por supuesto, debería ser ligero, ya que estas funciones privadas podrían ser llamadas con mucha frecuencia.

ACTUALIZACIÓN : Gracias por sus sugerencias.

Me gusta especialmente la idea de David de definir un tipo de objeto que contenga las funciones ''privadas'' y envuelve un objeto jQuery. El único problema es que todavía me impide encadenar funciones ''privadas'' y ''públicas''. Lo cual fue una gran razón para querer una sintaxis como this.fill(''red'') para empezar.

Terminé con una solución que considero no tremendamente elegante, pero que atrae a los "lo mejor de ambos mundos" porque:

$.fn.chain = function(func) { return func.apply(this, Array.prototype.slice.call(arguments, 1)); };

Lo cual permite construcciones como:

this. find(''.acertainclass''). chain(fill, ''red''). click(function() { alert("I''m red"); });

Publiqué mi pregunta en otros lugares, y también obtuve algunas respuestas interesantes:


Qué tal (dentro del alcance del plugin):

var fill = function () { (function (color) { this.css (''backgrorund-color'', color); //.. your stuff here ... }).apply (this, arguments); } $.fn.myplugin = function () { fill (''green''); }

De esa forma, Fill retendrá el contexto de jQuery en el que se encuentra, y sigue siendo privado para su complemento

Enmendado: lo anterior es un alcance wrt incorrecto, pruebe lo siguiente en su lugar:

var fill = function (color) { if (!$this) return; // break if not within correct context $this.css (''backgrorund-color'', color); //.. your stuff here ... } $.fn.myplugin = function () { var $this = $(this); // local ref to current context fill (''green''); }


Una cosa primero : si desea llamar a algo como this.fill(''red''); donde esta es una instancia de jQuery, debe extender el prototipo de jQuery y hacer que fill() "público". jQuery proporciona directrices para extender su prototipo utilizando los llamados "complementos" que se pueden agregar usando $.fn.fill , que es lo mismo que jQuery.prototype.fill .

En las devoluciones de llamada de jQuery, a menudo se trata de una referencia al elemento HTML y no se pueden agregar prototipos a esos (todavía). Ese es uno de los motivos por los cuales jQuery envuelve elementos y devuelve instancias jQuery que se pueden extender fácilmente.

Usando la (function(){})(); sintaxis, puede crear y ejecutar javascript "privado" sobre la marcha, y todo desaparece cuando termina. Con esta técnica, puedes crear tu propia sintaxis similar a jQuery que envuelve jQuery en tu propio objeto privado encadenable.

(function(){ var P = function(elem) { return new Private(elem); }; var Private = function(elem) { this.elem = jQuery(elem); } Private.prototype = { elem: null, fill: function(col) { this.elem.css(''background'',col); return this; }, color: function(col) { this.elem.css(''color'', col); return this; } } $.fn.myplugin = function() { P(this).fill(''red''); }; $.fn.myotherplugin = function() { P(this).fill(''yellow'').color(''green''); }; })(); $(''.foo'').myplugin(); $(''.bar'').myotherplugin(); console.log(typeof P === ''undefined'') // should print ''true''

De esta manera, P representa tu propia caja de herramientas de funciones "privadas". No estarán disponibles en ningún otro lugar del código ni en el espacio de nombres de jQuery, a menos que los adjunte a alguna parte. Puede agregar tantos métodos como desee en el objeto privado, y siempre que lo devuelva, también puede encadenarlos al estilo jQuery como lo hice en el ejemplo.


Es posible que desee ver cómo se implementa la fábrica de widgets de jQuery UI .

El enfoque básico es así:

(function($){ $.fn.myplugin = function(method) { if (mp[method]) // map $(''foo'').myplugin(''bar'', ''baz'') to mp.bar(''baz'') { return mp[method].apply(this, Array.prototype.slice.call(arguments, 1)); } else if (typeof method === ''object'' || ! method) { return mp.init.apply(this, arguments); // if called without arguments, init } else { $.error(''Method '' + method + '' does not exist on $.myplugin''); } }; // private methods, internally accessible via this.foo, this.bar var foo = function() { … }; var bar = function() { … }; var private = { // alternative approach to private methods, accessible via this.private.foo foo : function() { … }, bar : function() { … } } var mp = { // public methods, externally accessible via $.myplugin(''foo'', ''bar'') init : function( options ) { return this.each(function() { // do init stuff } }, foo : function() { … }, bar : function() { … } }; })(jQuery);