Cierres anónimos autorreferenciados: ¿JavaScript está incompleto?

18

¿El hecho de que los cierres anónimos de funciones de autorreferencia sean tan frecuentes en JavaScript sugiere que JavaScript es una especificación incompleta? Vemos mucho de esto:

(function () { /* do cool stuff */ })();

y supongo que todo es cuestión de gustos, pero ¿esto no parece un error, cuando todo lo que quieres es un espacio de nombres privado? ¿No podría JavaScript implementar paquetes y clases adecuadas?

Compare con ActionScript 3, también basado en ECMAScript, donde obtiene

package com.tomauger {
  import bar;
  class Foo {
     public function Foo(){
       // etc...
     }

     public function show(){
       // show stuff
     }

     public function hide(){
       // hide stuff
     }
     // etc...
  }
}

Contraste con las convoluciones que realizamos en JavaScript (esto, de la documentación de autoría del complemento jQuery ):

(function( $ ){

  var methods = {
    init : function( options ) { // THIS },
    show : function( ) { // IS   },
    hide : function( ) { // GOOD },
    update : function( content ) { // !!! }
  };

  $.fn.tooltip = function( method ) {

    // Method calling logic
    if ( methods[method] ) {
      return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
    } else if ( typeof method === 'object' || ! method ) {
      return methods.init.apply( this, arguments );
    } else {
      $.error( 'Method ' +  method + ' does not exist on jQuery.tooltip' );
    }    

  };

})( jQuery );

Aprecio que esta pregunta pueda degenerar fácilmente en una queja sobre las preferencias y los estilos de programación, pero en realidad tengo mucha curiosidad por saber cómo se sienten los programadores experimentados al respecto y si se siente natural, como aprender diferentes idiosincrasias de un nuevo idioma o kludgy , como una solución alternativa a algunos componentes básicos del lenguaje de programación que simplemente no se implementan?

Tom Auger
fuente
22
"¿No podría JavaScript implementar ... clases adecuadas?" No. Ya tiene prototipos adecuados. Los prototipos no son inferiores a las clases. Ellos son diferentes. La gente ha intentado agregar clases a JavaScript en varias ocasiones y no han tenido éxito.
Rein Henrichs
55
@Rein: Y sin embargo, de alguna manera ActionScript lo logró ...
Mason Wheeler
8
@ Tom no hay "clases integradas". No existe una clase en un lenguaje prototípico . Sigues combinando los dos paradigmas.
Rein Henrichs
1
Personalmente, encuentro que la función del lenguaje de funciones anónimas es más flexible que las clases. Sin embargo, me gusta la programación funcional en la que este idioma es común.
dietbuddha
1
Para otros, aquí: brianodell.net/?page_id=516 es una excelente introducción a JavaScript como lenguaje prototípico.
Tom Auger

Respuestas:

9

Supongo que todo es cuestión de gustos, pero ¿esto no parece un error, cuando todo lo que quieres es un espacio de nombres privado? ¿No podría JavaScript implementar paquetes y clases adecuadas?

La mayoría de los comentarios argumentan en contra del mito de que "los prototipos son clases de hombres pobres", así que solo repetiré que la OO basada en prototipos no es inferior de ninguna manera a la OO basada en clases.

El otro punto "un kludge cuando todo lo que quieres es un espacio de nombres privado". Es posible que se sorprenda al saber que Scheme usa exactamente el mismo kludge para definir los ámbitos. Eso no impidió convertirse en el ejemplo arquetípico del alcance léxico bien hecho.

Por supuesto, en Scheme, el 'kludge' está oculto detrás de las macros ...

Javier
fuente
1
No presenta evidencia que respalde su afirmación de que Scheme es el principal ejemplo de alcance léxico bien hecho, o que esto tuvo algo que ver con cómo Scheme utilizó las funciones para definir los ámbitos.
DeadMG
No puedo hablar de que Scheme sea un ejemplo, pero un ejemplo del cocreador de JS, Brendan Eich, discutiendo que Scheme desempeña un papel en el diseño de JS aquí: readwrite.com/2011/07/22/javascript-was-no-accident
Erik Reppen
7

En primer lugar, un par de cosas:

  1. Otra forma de ver JavaScript es las 1 millón y 1 cosas que puede hacer con la función como una construcción. Todo está ahí si lo buscas. Nunca está lejos de ser una función.

  2. Esa creación de plug-in jQuery es horrible. No tengo idea de por qué defienden eso. Las extensiones $ deben ser cosas de uso general que $ ya tiene métodos bastante bien cubiertos, no build-me-a-complete-widget. Es una herramienta de normalización DOM-API. Su uso está mejor enterrado dentro de sus propios objetos. No veo el atractivo de usarlo como un repositorio de biblioteca de interfaz de usuario completo.

Los paquetes en la web del lado del cliente no tienen sentido

Lo que personalmente no me gusta de los paquetes en la web del lado del cliente es que básicamente estaríamos fingiendo que estamos haciendo algo que realmente no hacemos. En una publicación de formularios web .NET y un montón de cosas horribles que nunca se extrajeron de nuestros amigos de Java, prefiero pensar en un trozo de HTML con recursos vinculados como lo que realmente es y no tratar de apaciguar a los desarrolladores de aplicaciones de sistemas operativos resistentes al aprendizaje de nuevas cosas pretendiendo que es otra cosa En JS en la web del lado del cliente, nada se "importa" salvo hacer algo horrible con Ajax que opera en ignorancia del almacenamiento en caché del navegador, lo cual sí, muchos han intentado hacer. Todo lo que le importa al navegador es que se cargó e interpretó o no. No tenemos más código guardado en el cliente en algún lugar disponible para su uso "por si acaso" por buenas razones El n. ° 1 es que acabo de describir las dependencias de un complemento y un complemento de navegador para aplicaciones web como un fenómeno que generalmente no ha funcionado demasiado bien. Queremos web ahora. No después de que Adobe o Sun hayan terminado de actualizar por tercera vez esta semana.

El lenguaje tiene lo que necesita para estructurarse

Los objetos JS son altamente mutables. Podemos tener árboles ramificados de espacios de nombres en cualquier grado que nos resulte útil y es muy fácil hacerlo. Pero sí, para cualquier cosa reutilizable, debe pegar la raíz de cualquier biblioteca en el espacio global. Todas las dependencias están vinculadas y cargadas al mismo tiempo de todos modos, entonces, ¿qué sentido tiene hacer algo más? El punto de evitar el espacio de nombres global no es que haya nada malo. Es que demasiadas cosas son malas porque corres el riesgo de colisiones en el espacio de nombres o sobreescribiendo accidentalmente las características principales del lenguaje.

Solo porque sea popular no significa que lo estamos haciendo bien

Ahora, cuando vea esto en una aplicación web del lado del cliente:

(function(){
//lots of functions defined and fired and statement code here
})()

El problema no es que nos falten herramientas para estructurar una aplicación, sino que la gente no está valorando la estructura. En el caso de sitios temporales desechables únicos de 2-3 páginas en una agencia de diseño, realmente no tengo ningún problema con eso. Donde se pone feo es cuando tienes que construir algo que se pueda mantener y que sea legible y fácil de modificar.

Pero cuando llegue al lugar donde es hora de implementar todos los objetos y fábricas reutilizables y tal vez uno o dos nuevos vars temporales puedan ingresar en ese proceso, es una conveniencia.

Pero hay implementaciones de JS con paquetes / módulos

Tenga en cuenta que en Node.js, donde tales cosas tienen mucho más sentido, tienen módulos. JS, suponiendo que podamos evitar uber-config-hell que plaga otros idiomas, es lo único en la ecuación y cada archivo ejecutado es su propio alcance aislado. Pero en una página web, vincular un archivo js es en sí mismo la declaración de importación. Hacer más importaciones sobre la marcha es solo una pérdida de tiempo y recursos, ya que obtener los recursos requiere mucho más esfuerzo que simplemente agregar enlaces a los archivos, ya que los necesita sabiendo que se almacenarán en caché en un navegador si otra página los necesita nuevamente. Por lo tanto, está tratando de dividir el espacio global haciendo algo más que crear fábricas de objetos adaptadores como jQuery u objetos más tradicionales que cubren un gran subconjunto de tareas en un dominio dado mientras ocupan un lugar en global. Allí'http://wiki.ecmascript.org/doku.php?id=harmony:modules

Entonces, no, no hay nada de malo en que los invocadores automáticos se usen para evitar la contaminación global del espacio de nombres cuando hay una buena razón para usar tales cosas (la mayoría de las veces no lo hay). Y tenemos propiedades persistentes privadas equivalentes en nuestros objetos (solo defina una var en el constructor y no la exponga como una propiedad).

Sin embargo, el hecho de que PODEMOS hacer tales cosas es increíble. El uso intensivo es una señal de que los desarrolladores de JS todavía están madurando, pero no es un agujero en el lenguaje para cualquiera que no esté tratando de forzar un paradigma en la web del lado del cliente que simplemente no tiene sentido aquí.

Erik Reppen
fuente
Para el votante, ¿podría explicar por qué? ¡Cuando alguien escribe tanto, creo que merece una explicación!
Songo
+1 buena respuesta, no estoy seguro por qué el voto negativo antes.
favor
Impresionante redacción y gran perspectiva. También me gusta el "solo porque es un tropo no significa que sea correcto". Creo que mi problema es que me siento más cómodo con lenguajes más estrictos, lo que (IMO) ayuda a la eficiencia del desarrollo de varias maneras. JavaScript parece realmente deslucido con no muchos controles y equilibrios integrados: puede hacer lo que quiera, por lo tanto, el panorama es un desastre de expresiones idiomáticas y prácticas. Es difícil determinar la forma "correcta" de abordar su estructura de codificación. Aunque estoy de acuerdo en que para los empleos rápidos y puntuales, esto no es una gran preocupación.
Tom Auger
1
En mi opinión, hay muchas cosas que puedes hacer con mucha cuerda además de ahorcarte y aprender a escribir código robusto más rápido a partir de ahorcamientos ocasionales que ocurren con menos frecuencia a medida que desarrollas mejores hábitos, pero no voy a fingir que es para todos o un candidato ideal para cada trabajo. Sospecho que cuanto más lo aprendas, sin embargo, más tolerable lo encontrarás. Siento que me falta la mitad del cerebro cuando intento hacer cosas en idiomas sin funciones u objetos de primera clase tan flexibles / mutables como los JS.
Erik Reppen
4

La otra cosa que falta es que javscript debe ser compatible con versiones anteriores. Si intenta introducir la sintaxis del paquete, realmente podría romper la web de alguna manera loca. ¡Eso sería malo! Doug Crockford ha hablado sobre esto en varios puntos y por qué los intentos de agregarlo han fallado.

Zachary K
fuente
Ese es un buen punto. Y, sin embargo, ActionScript lo logró simplemente lanzando una nueva versión. Al definir su etiqueta de script, siempre ha podido especificar la versión de JavaScript, por lo que "romper" los sitios existentes no debería ser un problema.
Tom Auger
1
En la práctica, la mayoría de las etiquetas de script en la red no tienen un número de versión. Para ser honesto, no estoy seguro de todos los problemas en este caso, pero sí sé que las personas que piensan en estas cosas han decidido que no es factible.
Zachary K
1
@Tom: Adobe también tiene control total sobre la plataforma Flash. Ninguna entidad tiene control total sobre todas las plataformas JS que existen. Además, simplemente agregar un número de versión para un script JS en un navegador significaría que no es compatible con navegadores antiguos o que tiene que escribir dos scripts. Entonces, es un problema.
Jeremy Heiler
2

Sí, es un error.

Mucha gente dice que "los prototipos no son inferiores a las clases". No estoy de acuerdo, pero eso es una cuestión de preferencia. Pero ese ni siquiera es el verdadero problema con JavaScript: el problema es que fue originalmente diseñado como un lenguaje de script rápido y sucio para crear cosas como botones animados. A mediados de los 90, nadie pensó que se le pediría a JavaScript que hiciera algunas de las locuras que está haciendo ahora.

Mike Baranczak
fuente
66
No estoy de acuerdo, JavaScript, el lenguaje es realmente increíble. Que fue agrupado junto con un DOM especificado y mutuamente incompatible es donde comenzaron todos los problemas.
Dean Harding
2
¿Qué tiene esto que ver con los prototipos?
Erik Reppen
2

Las funciones anónimas de auto invocación son más parecidas a los módulos que a las clases. Es molesto que el valor predeterminado para javascript sea ejecutarse en el ámbito global. El comité que trabaja en JS.next está considerando seriamente agregar módulos, de modo que no deje caer sus variables locales en el ámbito global. Afortunadamente, las funciones de Javascript tienen una semántica tan conveniente que podemos usar una función anónima como un ámbito privado con relativa facilidad.

No veo cómo las clases realmente entran en la discusión en absoluto, excepto que son la construcción de alcance de nivel superior en muchos idiomas. Sería muy bueno tener una mejor construcción de módulo / paquete / please-give-me-a-local-scope-so-i-don't-leave-my-variables-in-the-global-environment.

Sean McMillan
fuente
1

Es posible que desee echar un vistazo a ExtJS 3 y 4, donde han logrado implementar espacios de nombres bastante bien.

- agregado después de -1

Mi punto aquí fue que es posible ocultar todas estas 'circunvoluciones' y aún así tener un código bastante amigable como este:

Ext.ns('com.tomauger');
Ext.Loader.load('bar.js'); //unfortunately filname needs to be used
MyNameSpace.Foo = {
   //...
}
Mchl
fuente