¿Conceptos fundamentales que todo desarrollador web necesita saber en JavaScript?

** Lea las últimas líneas para créditos **

Los dos pilares de JavaScript

Parte 1: Cómo escapar del séptimo círculo del infierno

Había una vez

Estaba atrapado en la oscuridad. Estaba ciego, arrastrando los pies, tropezando con cosas, rompiendo cosas y, en general, haciendo un lío impío de todo lo que tocaba.

En los años 90, estaba programando en C ++, Delphi y Java y escribiendo complementos 3D para el paquete de software que finalmente se convirtió en Maya (utilizado por muchos de los principales estudios de cine para hacer películas de gran éxito en verano).

Entonces sucedió: Internet despegó. Todos comenzaron a construir sitios web, y después de escribir y editar un par de revistas en línea, un amigo me convenció de que el futuro de la web serían los productos SaaS (antes de que se acuñara el término). Entonces no lo sabía, pero ese cambio sutil de curso transformó la forma en que pienso sobre la programación en un nivel fundamental, porque si quieres hacer un buen producto SaaS, tienes que aprender JavaScript.

Una vez que lo aprendí, nunca miré hacia atrás. De repente, todo fue más fácil. El software que hice fue más maleable. El código sobrevivió más tiempo sin ser reescrito. Inicialmente, pensé que JavaScript era principalmente pegamento para scripts de UI, pero cuando supe que las cookies y AJAX explotaron, eso también se transformó.

Me volví adicto y no podía regresar. JavaScript ofrece algo de lo que carecen otros idiomas:

¡Libertad!

JavaScript es uno de los lenguajes de programación más importantes de todos los tiempos, no solo por su popularidad, sino porque popularizó dos paradigmas que son extremadamente importantes para la evolución de la programación:

  • Herencia de prototipos (objetos sin clases y delegación de prototipos, también conocido como OLOO – Objetos que enlazan con otros objetos), y
  • Programación funcional (habilitada por lambdas con cierre)

Colectivamente, me gusta llamar a estos paradigmas los dos pilares de JavaScript, y no me da vergüenza admitir que me han mimado. No quiero programar en un idioma sin ellos.

JavaScript será recordado como uno de los lenguajes más influyentes jamás creados. Muchos otros idiomas ya han copiado uno u otro, o ambos pilares, y los pilares han transformado la forma en que escribimos las aplicaciones, incluso en otros idiomas.

Brendan Eich no inventó ninguno de los pilares, pero JavaScript les expuso las masas de programación. Ambos pilares son igualmente importantes, pero me preocupa que a un gran número de programadores de JavaScript les falte por completo una o ambas innovaciones, porque JavaScript es bastante bueno para permitirle codificar mal si no se molesta en aprenderlo correctamente.

En realidad, esta es una característica, porque hace que sea muy fácil recoger JavaScript y comenzar a hacer cosas útiles con él, pero esa fase de su desarrollo como programador de JavaScript no debería durar más de un año.

Si aún no lo ha hecho, es hora de subir de nivel.

Si está creando funciones de constructor y heredando de ellas, no ha aprendido JavaScript. No importa si lo ha estado haciendo desde 1995. No está aprovechando las capacidades más potentes de JavaScript.

Estás trabajando en la versión falsa de JavaScript que solo existe para vestir el lenguaje como Java.

Estás codificando en este increíble lenguaje de programación seminal que cambia el juego y te estás perdiendo por completo lo que lo hace tan genial e interesante.

Estamos construyendo un desastre.

“Los que no son conscientes de que están caminando en la oscuridad nunca buscarán la luz”. ~ Bruce Lee

Los constructores violan el principio abierto / cerrado porque acoplan a todas las personas que llaman con los detalles de cómo se instancia su objeto. ¿Hacer un juego HTML5? ¿Desea cambiar de nuevas instancias de objetos para usar agrupaciones de objetos para poder reciclar objetos y evitar que el recolector de basura destruya su velocidad de cuadros? Demasiado. O bien interrumpirá todas las llamadas, o terminará con una función de fábrica colapsada.

Si devuelve un objeto arbitrario de una función de constructor, romperá sus enlaces prototipo, y la palabra clave `this` ya no estará vinculada a la nueva instancia de objeto en el constructor. También es menos flexible que una función de fábrica real porque no se puede usar ‘this’ en absoluto en la fábrica; simplemente se tira a la basura.

Los constructores que no se ejecutan en modo estricto también pueden ser francamente peligrosos. Si una persona que llama olvida `nuevo` y no está utilizando el modo estricto o las clases ES6 [suspiro] , cualquier cosa que asigne a` esto` contaminará el espacio de nombres global. Eso es feo.

Antes del modo estricto, esta falla del lenguaje causaba errores difíciles de encontrar en dos startups diferentes para las que trabajé, durante los períodos críticos de crecimiento, cuando no teníamos mucho tiempo extra para perseguir errores difíciles de encontrar.

En JavaScript, las funciones de fábrica son simplemente funciones de constructor menos el requisito `nuevo` , el peligro de contaminación global y las limitaciones incómodas (incluida esa molesta convención inicial de letras mayúsculas).

JavaScript no necesita funciones de constructor porque cualquier función puede devolver un nuevo objeto. Con la extensión dinámica de objetos, los literales de objetos y ` Object.create ()`, tenemos todo lo que necesitamos, sin ningún problema. Y `this` se comporta igual que en cualquier otra función. ¡Hurra!

Bienvenido al Séptimo Círculo del Infierno.

“Muy a menudo no soy tan miserable como sería sabio”. ~ TH White

Todos han escuchado la analogía de la rana hirviendo: si pones una rana en agua hirviendo, saltará. Si pones a la rana en agua fría y aumentas gradualmente el calor, la rana morirá de un hervor porque no siente el peligro. En esta historia, somos las ranas.

Si el comportamiento del constructor es la sartén , la herencia clásica no es el fuego; Es el fuego del séptimo círculo del infierno de Dante.

El problema del gorila / plátano:

“El problema con los lenguajes orientados a objetos es que tienen todo este entorno implícito que llevan consigo. Querías un plátano, pero lo que obtuviste fue un gorila sosteniendo el plátano y toda la jungla. ”~ Joe Armstrong

La herencia clásica generalmente le permite heredar solo de un solo antepasado, lo que lo obliga a tomar taxonomías incómodas. Digo incómodo porque, sin falta, cada taxonomía de diseño OO que he visto en una aplicación grande finalmente se equivocó.

Digamos que comienzas con dos clases: Herramienta y Arma . Ya te equivocaste: no puedes hacer que el juego sea “Clue”.

El apretado problema de acoplamiento

El acoplamiento entre una clase secundaria y su padre es la forma más estrecha de acoplamiento en el diseño OO. Eso es lo opuesto al código modular reutilizable.

Hacer pequeños cambios en una clase crea ondulantes efectos secundarios que rompen cosas que no deberían estar relacionadas.

El problema de la duplicación por necesidad

La solución obvia a los problemas de taxonomía es retroceder en el tiempo, construir nuevas clases con diferencias sutiles cambiando lo que hereda de qué, pero está demasiado estrechamente acoplado para extraerlo y refactorizarlo adecuadamente. Terminas duplicando el código en lugar de reutilizarlo. Viola el principio SECO (no se repita).

Como consecuencia, sigue creciendo su selva de clases sutilmente diferentes, y a medida que agrega niveles de herencia, sus clases se vuelven cada vez más artríticas y frágiles. Cuando encuentras un error, no lo arreglas en un solo lugar. Lo arreglas por todas partes.

“Vaya. Perdió uno. ”- Cada programador de OO clásico, siempre.

Esto se conoce como el problema de duplicación por necesidad en los círculos de diseño OO.

Las clases de ES6 no solucionan ninguno de estos problemas. ES6 los empeora, porque estas malas ideas serán oficialmente bendecidas por la especificación, y se escribirán en miles de libros y publicaciones de blog.

La palabra clave `class` probablemente será la característica más dañina en JavaScript. Respeto enormemente a las personas brillantes y trabajadoras que han participado en el esfuerzo de estandarización, pero incluso las personas brillantes ocasionalmente hacen algo incorrecto. Intente agregar .1 + .2 en la consola de su navegador, por ejemplo. Todavía creo que Brendan Eich ha contribuido enormemente a la web, a los lenguajes de programación y a la informática en general.

PD: No use `super` a menos que disfrute de pasar por el depurador en múltiples capas de abstracción de herencia.

Las consecuencias

Estos problemas tienen un efecto multiplicador a medida que su aplicación crece, y eventualmente, la única solución es reescribir la aplicación desde cero o descartarla por completo, a veces la empresa solo necesita reducir sus pérdidas.

He visto este proceso jugar una y otra vez , trabajo tras trabajo , proyecto tras proyecto . ¿Alguna vez aprenderemos?

En una empresa para la que trabajé, la fecha de lanzamiento del software pasó un año entero para una reescritura. Creo en las actualizaciones, no en las reescrituras. En otra compañía que consulté, casi causó que toda la compañía se estrellara y se quemara.

Estos problemas no son solo una cuestión de gusto o estilo. Esta elección puede hacer o deshacer su producto.

Las grandes empresas generalmente pueden avanzar como si nada estuviera mal, pero las nuevas empresas no pueden darse el lujo de hacer girar sus ruedas en problemas como estos mientras luchan por encontrar su producto / mercado adecuado en una pista limitada.

Nunca he visto ninguno de los problemas anteriores en una base de código moderna que evita la herencia clásica por completo.

Sal a la luz.

“La perfección se alcanza no cuando no hay nada más que agregar, sino cuando no hay nada más que restar”. ~ Antoine de Saint-Exupéry

Hace un tiempo, estaba trabajando en una biblioteca para demostrar cómo usar la herencia prototípica para mi libro, “Programando aplicaciones JavaScript” (O’Reilly), cuando decidí una idea interesante: una función de fábrica que te ayuda a producir funciones de fábrica que pueden heredar y componer juntos. Llamé a las fábricas compostables “sellos” y a la biblioteca, “Stampit”. La biblioteca es muy pequeña y simple. Di una charla sobre Stampit en la O’Reilly Fluent Conference en 2013 (incluida aquí al final del artículo), y escribí una publicación de blog sobre sellos (ver más abajo).

Hay una comunidad pequeña, pero en constante crecimiento, de desarrolladores cuyos estilos de codificación han sido transformados por sellos. Stampit está en uso de producción en múltiples aplicaciones con millones de usuarios activos mensuales.

“He estado usando Stampit mucho y realmente disfruto del poder que ofrece la simplicidad en la separación de preocupaciones entre los tipos de prototipos, así como la naturaleza composable de los sellos. Los árboles clásicos de herencia profunda siempre me molestaron, especialmente en el contexto del desarrollo web donde las necesidades comerciales son a menudo un objetivo móvil. La charla mencionada anteriormente y Stampit han inspirado diferentes pensamientos y me permiten hacer cosas que parecen interfaces sin la sobrecarga, o heredar datos privados con métodos privilegiados de múltiples fuentes. Mi herencia prototípica ahora es fuerte, mis objetos se han vuelto sin forma, sin forma, como el agua. Sigan con el gran trabajo, el azúcar de la clase no tiene nada que ver con esto ”. ~ Justin Schroeder, Desarrollador Senior, Wrecking Ball Media Group

Stampit no es la única alternativa, por supuesto. Douglas Crockford no usa `new` o` this` en absoluto, sino que opta por un enfoque completamente funcional para la reutilización de código.

Todos sus objetos son simplemente bolsas de funciones sin estado, u objetos solo de datos como matrices asociativas sin métodos. Esto funciona bien a menos que esté creando cientos de miles de objetos y necesite que su aplicación funcione sin problemas en o en tiempo real (piense en motores de juegos, procesadores de señal en tiempo real, etc.). En esas situaciones, delegar llamadas a métodos puede salvarlo de una gran cantidad de administración manual de memoria.

Otras buenas alternativas incluyen hacer un mejor uso de los módulos JavaScript como alternativa a la herencia (recomiendo los módulos npm y ES6 con Browserify o WebPack), o simplemente clonar objetos copiando propiedades de un objeto fuente a un nuevo objeto (por ejemplo, `Object.assign ( ) ` , ` $ .extend () `,` _.extend () `, etc …).

El mecanismo de copia es otra forma de herencia prototípica. Las fuentes de las propiedades de clonación son un tipo específico de prototipo llamado prototipos ejemplares, y la clonación de un prototipo ejemplar se conoce como herencia concatenativa.

Incluso si sigues los consejos de Douglas Crockford y dejas de usar `this`, aún puedes hacer las cosas de la manera prototípica. La herencia concatenativa es posible debido a una característica en JavaScript conocida como extensión de objeto dinámico: la capacidad de agregar a un objeto después de que se haya instanciado.

Nunca necesita clases en JavaScript, y nunca he visto una situación en la que la clase sea un mejor enfoque que las alternativas. Si puede pensar en alguno, deje un comentario, pero he estado haciendo ese desafío durante años y nadie ha presentado un buen caso de uso , solo argumentos débiles sobre micro optimizaciones o preferencias de estilo.

Cuando le digo a la gente que los constructores y la herencia clásica son malos, se ponen a la defensiva. No te estoy atacando. Estoy tratando de ayudarte

Las personas se apegan a su estilo de programación como si su estilo de codificación fuera como se expresan. Disparates.

Lo que haces con tu código es cómo te expresas.

Cómo se implementa no importa en absoluto a menos que se implemente de manera deficiente.

Lo único que importa en el desarrollo de software es que a sus usuarios les encanta el software.

Puedo advertirte que hay un precipicio por delante, pero algunas personas no creen que haya peligro hasta que lo experimentan de primera mano. No cometas ese error; El costo puede ser enorme. Esta es su oportunidad de aprender de los errores que innumerables otros han cometido una y otra vez durante décadas. Se han escrito libros completos sobre estos problemas.

El libro seminal “Patrones de diseño” de Gang of Four se basa en dos principios fundamentales:

“Programa para una interfaz, no una implementación” y “favorece la composición de objetos sobre la herencia de clases”.

Debido a que las clases secundarias codifican la implementación de la clase principal, el segundo principio se sigue del primero, pero es útil explicarlo.

El trabajo fundamental en el diseño OO clásico es la herencia anti-clase.

Contiene una sección completa de patrones de creación de objetos que existen únicamente para evitar las limitaciones de los constructores y la herencia de clases.

Google “nuevo considerado dañino”, “herencia considerada dañina” y “súper es un olor a código”. Desenterrará docenas de artículos de publicaciones de blog y publicaciones respetadas como el Dr. Dobb’s Journal que datan de antes de que se inventara JavaScript, todo diciendo casi lo mismo: `nuevas` , taxonomías de herencia clásicas frágiles y acoplamiento entre padres e hijos (por ejemplo, ` super` ) son recetas para el desastre.

Incluso James Gosling, el creador de Java, admite que Java no hizo bien los objetos.

¿Quieres seguir con las referencias de JavaScript? Douglas Crockford obtuvo Object.create () agregado al lenguaje para que no tuviera que usar `new` .

Kyle Simpson (autor, “You Don’t Know JS”) escribió una fascinante serie de blogs de tres partes sobre el tema llamado “Objetos JS: un desastre heredado”.

Kyle argumenta que la herencia prototípica es anti-clase, eso es más simple y mejor que la clase. Incluso acuñó el término OLOO (Objetos vinculados a otros objetos) para aclarar la distinción entre delegación de prototipos y herencia de clases.

El buen código es simple.

“La simplicidad se trata de restar lo obvio y agregar lo significativo”. ~ John Maeda

A medida que elimina los constructores y la herencia clásica de JavaScript, esto:

  • Se vuelve más simple ( más fácil de leer y escribir. No más taxonomías de diseño incorrectas).
  • Se vuelve más flexible (¿Cambiar de nuevas instancias a grupos de objetos de reciclaje o proxies? No hay problema).
  • Se vuelve más poderoso y expresivo (¿Heredar de antepasados ​​múltiples? ¿Heredar estado privado? No hay problema).

La mejor opción

“Si una característica es a veces peligrosa, y hay una mejor opción, siempre use la mejor opción”. ~ Douglas Crockford

No estoy tratando de quitarte una herramienta útil. Te advierto que lo que crees que es una herramienta es en realidad un arma de pie. En el caso de los constructores y las clases, hay varias opciones mejores.

Otro argumento común que usan los programadores es que debería depender de ellos cómo se expresan, como si el estilo de código se elevara al nivel del arte o la moda. Este argumento es puramente emocional e irracional:

Su código no es el producto de su autoexpresión más de lo que el pincel de un pintor es el producto de su autoexpresión. El código es la herramienta. El programa es el producto.

Sí, algún código es arte en sí mismo, pero si no se publica solo en papel, su código no entra en esa categoría. De lo contrario, en lo que respecta a sus usuarios, el código es un cuadro negro, y lo que les gusta es el programa.

Un buen estilo de programación requiere que cuando se le presente una opción elegante, simple y flexible, u otra opción compleja, incómoda y restrictiva, elija la primera. Sé que es popular tener una mente abierta sobre las características del lenguaje, pero hay una manera correcta y una incorrecta.

Elige el camino correcto.

~ Eric Elliott

PD: No te pierdas Los dos pilares de JavaScript, Parte 2: Programación funcional (Cómo detener la microgestión de todo)

Uno de los fundamentos básicos que todo desarrollador web debe saber sobre Javascript es que es un lenguaje de script.

Solo funciona en el navegador del cliente, lo que significa que uno no necesita un servidor para ejecutar Javascript. Hay una excepción de que Javascript también funciona con backend como node.js. Esa es otra historia.

Otro fundamento de Javascript que los desarrolladores web deben conocer. Permite al desarrollador web controlar el comportamiento de la página web. Cómo la información mostrada se anima incluso de formas inimaginables.

Uno de los ejemplos más destacados fue la tabla periódica famo.us. Me impresionó la primera vez que lo vi en acción.

Visite el siguiente enlace para verlo.

famoso

PD: Muchos sitios web increíbles dependen en gran medida de Javascript para hacer su sitio web … ¡Impresionante!