El ZEN de Javascript: a la búsqueda de la armonía del código

14 Jul 2016

Introducción

Hace ya algunos años, Tim Peters enunció El ZEN de Python, un conjunto de recomendaciones cuyo objetivo era conseguir un código más limpio, mantenible y, en definitiva, cómodo para los desarrolladores. No se trata de un sistema rígido e inflexible, o de unas normas de estilo que hay que seguir a raja tabla, sino de una serie de aspectos que pueden ayudarnos a la hora de conseguir un código en armonía.

No cabe duda de que Javascript comparte muchas características con Python, y es por ello que podemos adaptar el ZEN de Python a nuestro lenguaje sin muchas complicaciones. En este artículo, daremos un repaso a este manifiesto aportando ejemplos ilustrativos en JS.

Vamos a ello!

El texto original

Como no podría ser de otra forma, comencemos con el texto original redactado por el señor Peters. La traducción, es mía, por lo que cualquier otra interpretación es bienvenida en los comentarios:

Beautiful is better than ugly
Hermoso es mejor que feo

Explicit is better than implicit
Explícito es mejor que implícito

Simple is better than complex
Simple es mejor que complejo

Complex is better than complicated
Complejo es mejor que complicado

Flat is better than nested
Plano es mejor que anidado

Sparse is better than dense
Disperso es mejor que denso

Readability counts
La legibilidad cuenta

Special cases aren’t special enough to break the rules
Los casos especiales no son tan especiales como para romper las reglas

Although practicality beats purity
Aunque lo práctico gana a la pureza

Errors should never pass silently
Los errores nunca deben ser silenciosos

Unless explicitly silenced
Salvo que sean explícitamente silenciados

In the face of ambiguity, refuse the temptation to guess
Ante la ambigüedad, evita la tentación de adivinar

There should be one– and preferably only one –obvious way to do it
Debería haber una -y preferiblemente solo una- forma obvia de hacerlo

Although that way may not be obvious at first unless you’re Dutch
Aunque esa forma no obvia a simple vista a menos que seas holandés

Now is better than never
Ahora es mejor que nunca

Although never is often better than *right* now
Aunque nunca es a menudo mejor que *ahora mismo*

If the implementation is hard to explain, it’s a bad idea
Si la implementación es difícil de explicar, es una mala idea

If the implementation is easy to explain, it may be a good idea
Si la implementación es fácil de explicar, puede ser una buena idea

Namespaces are one honking great idea — let’s do more of those!
Los namespaces son una gran idea; ¡tengamos más de esas!

The ZEN of Python
Tim Peters, 2005

Una vez familiarizados con el texto original, vamos a intentar adaptarlo a nuestro trabajo diario en Javascript.

Hermoso es mejor que feo

Este punto es claro, directo y no da pie a discusiones al respecto. Lo difícil es encontrar la solución más estética entre las muchas posibles.

En Javascript, el patrón módulo ha representado sin duda uno de los ejemplos de estructuración más elegantes al que podemos recurrir cuando creamos un nuevo componente. Su belleza no es únicamente formal, sino que aúna funcionalidad y flexibilidad:

// Hermoso
var MODULE = ( () => {
    var my = {},
        privateVariable = 1;
 
    var privateMethod = () => {
        // ...
    };
 
    my.moduleMethod = () => console.info( 'Method from core...' );
 
    return my;
} ) ();

Si entendemos el código espagueti como lo ‘feo’, el módulo es su contrapartida hermosa.

Explícito es mejor que implícito

Cuando nos referimos a Javascript, la diferencia entre ambos conceptos radica en que implícito significa que delegamos un determinado comportamiento en el propio intérprete mientras que explícito supone que somos nosotros quienes lo definimos.

Por ejemplo, no declarar de forma explícita una variable, obliga al intérprete a hacerlo por nosotros:

// Implícito
function foo () {
    a = 'Hello World'; // Missing 'var'
 
    return a;
}
 
console.info( foo() ); // Hello World
console.info( a ); // Hello World

Dejar que sea el propio Javascript el que defina variables no es una buena idea: como vemos en el ejemplo anterior, ésta se ha creado fuera de la función (en el contexto global), lo cual no es seguramente lo que esperábamos ni lo más deseable para nuestra aplicación.

// Explícito
function foo () {
    var a = 'Hello World';
 
    return a;
}
 
console.info( foo() ); // Hello World
console.info( a ); // ReferenceError: a is not defined

Esta distinción entre implícito y explícito puede aplicarse también a la coerción de tipos, a las colas de recursión, al recolector de basura, al valor contextual de this y a otros comportamientos del lenguaje que, de forma ‘nativa’, pueden provocar comportamientos inesperados en un código.

Simple es mejor que complejo

Sobre este punto podríamos extendernos tanto como quisiéramos pero la idea base es mantener los principios KISS.

Un ejemplo recurrente es el uso correcto de los métodos nativos para trabajar sobre arrays: no dominarlos implica terminar con funciones complejas que bien pueden resolverse con un par de líneas de ‘código preciso’:

// Complejo
function arrayFlat ( arrays ) {
    var count = arrays.length,
        merged = [],
        c = 0;
 
    for ( var i = 0; i < count; ++i ) {
        for ( var j = 0, jlen = arrays[ i ].length; j < jlen; ++j ) {
            merged[ c++ ] = arrays[ i ][ j ];
        }
    }
 
    return merged;
}
// Simple
var arrayFlat = ( arrays ) =>
    arrays.reduce( ( p, n ) => p.concat( n ) );

Ambas funciones son idénticas en cuanto a entrada/salida: fusionar un array de dos dimensiones en uno con una sola dimensión:

console.info( arrayFlat( [ [ 'a', 'b' ], [ 'c', 'd' ], 'e' ] ) );
// ["a", "b", "c", "d", "e"]

Complejo es mejor que complicado

Un algoritmo puede ser complejo y al mismo tiempo elegante y legible. El famoso Algoritmo de Euclides para calcular el máximo común divisor de dos números dados (mcd/gcd) resulta evidente para cualquier aficionado a la matemática cuando está bien expresado:

// Complejo pero no complicado
var gcd = ( x, y ) => {
    while ( y ) [ x, y ] = [ y, x % y ];
 
    return x;
};

Plano es mejor que anidado

Los desarrolladores en Node.js, pueden haberse encontrado en más de una ocasión con la temida Pirámide de la Muerte:

// Anidación; Pyramid of Doom
mainWindow.menu( 'File', function ( err, file ) {
    if ( err ) throw err;
    file.openMenu( function( err, menu ) {
        if ( err ) throw err;
        menu.item( 'Open', function ( err, item ) {
            if ( err ) throw err;
            item.click( function ( err ) {
                if ( err ) throw err;
                mainWindow.getChild( type( 'Window' ), function ( err, dialog ) {
                    if ( err ) throw err;
                    //...
                } );
            } );
        } );
    } );
} );

Su contrapartida, en formato de código plano (cadena), resulta mucho más legible y, a la larga, mantenible:

// Plano
Seq()
    .seq( function () { mainWindow.menu( "File", this ); } )
    .seq( function ( file ) { file.openMenu( this ); } )
    .seq( function ( menu ) { menu.item( "Open", this ); } )
    .seq( function ( open ) { open.click(); } )
    .seq( function () { mainWindow.getChild( type( 'Window' ), this ); } )
    .seq( function ( dialog ) { /* ... */ } );

NOTA: Ejemplo tomado de un famoso hilo sobre Node.js en Google Group

Disperso es mejor que denso

En relación con lo anterior, plano no significa una larguísima línea de código. Cada lenguaje nos permite indentar de una u otra forma para ganar legibilidad:

// Denso
var getAvg = ( ...values ) => values.reduce( ( p, c ) => p + c ) / values.length;
// Disperso
var getAvg = ( ...values ) =>
    values.reduce(
        ( p, c ) => p + c
    ) / values.length;

NOTA: Ejemplos tomados del artículo sobre Funciones Flecha en este mismo blog.

La legibilidad cuenta

En este aspecto podríamos mostrar muchos ejemplos, pero nos quedamos con las temidas expresiones regulares. Hay veces en que trabajar con ellas, sobre todo si nos vienen heredadas desde otro proyecto, puede ser francamente terrible:

// Ilegible
var emailCheker = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF
\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|
[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))
?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|
[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|
[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?
(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])
|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|
[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-
\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])
|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|
[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF
\uFDF0-\uFFEF])))$/i;

El fragmento anterior es código real (he forzado saltos de línea para que cupiera en la pantalla) y está tomado de Parsley, una conocida bibilioteca para validar datos en JS: http://parsleyjs.org/

Los casos especiales no son tan especiales como para romper las reglas

Poco que comentar aquí; un caso que podamos entender como ‘especial’, no justifica saltarse las conveciones. Si tenemos unas reglas de estilo y una filosofía de código, tenemos que respetarla y mantenerla durante todo el proyecto, independientemente de eventualidades.

No obstante, pueden existir ciertas excepciones a esta regla, y es lo que trata el siguiente punto…

Aunque lo práctico gana a la pureza

En ocasiones, la pureza, entendida como abstracción, puede ser reemplazada por una solución práctica y directa.

// Pureza
var urlBase = function () {
    return location.protocol + '//' + location.hostname;
}
// Practico
var urlBase = function () {
    return 'https://openlibra.com';
}

Es en estos casos donde una solución directa puede reemplazar las conveciones que usamos en un proyecto. Esta recomendación, junto con la anterior, requieren del sentido común a la hora de aplicarse y complementarse.

Los errores nunca deben ser silenciosos

Javascript, al ejecutarse en un entorno cliente, es propenso a errores. Cuando, además, lo usamos en modo asíncrono para enviar y recibir peticiones desde un servidor, la cosa entra en terreno delicado: a todo lo que puede ocurrir desde el lado del usuario, hay que sumar la incertidumbre de las redes, latencias, disponibilidad del servidor, etc.

Es por esto que los errores no deberían ocurrir sin provocar una alerta clara. Ya sea para el desarrollador bajo su entorno, o para el usuario final, cualquier problema tiene que ser notificado sin lugar a dudas. El feedback que esto nos puede reportar es valioso y, además, podemos evitar la frustración de quien cree haber realizado alguna acción en nuestra aplicación que realmente ha sido detenida por un error.

En un entorno asíncrono, con peticiones XHR, esto es especialmente importante.

// Silencioso
function saveUserData ( data ) {
    var $this = $( this ),
        ajaxParams = {
            url: 'http://myserver.com',
            method: 'post',
            data: data,
            success: function () {
                $this.data( 'running', false );
            }
        };
 
    if ( ! $this.data( 'running' ) ) {
        $this.data( 'running', 'true' );
        $.ajax( ajaxParams );
    }
}
// Alerta
function saveUserData ( data ) {
    var $this = $( this ),
        ajaxParams = {
            url: 'http://myserver.com',
            method: 'post',
            data: data,
            success: function () {
                $this.data( 'running', false );
            },
            error: function ( xhr, ajaxOptions, thrownError ) {
                // Show any visual alert
                alert( 'Something went wrong. Please, try again later...' );
 
                // Logging error
                MyLibraryLog.write( xhr, ajaxOptions, thrownError );
 
                // Release the current state and show more alerts
                $this
                    .data( 'running', false )
                    .addClass( 'error' );
            }
        };
 
    if ( ! $this.data( 'running' ) ) {
        $this.data( 'running', 'true' );
        $.ajax( ajaxParams );
    }
}

En este caso, la idea es añadir una capa que pueda manejar los errores, ya sea con excepciones o alertas visuales y que, además, los registre en nuestro sistema. De este modo, tanto usuario como desarrollador, pueden estar al tanto de lo que ocurre en la aplicación.

Salvo que sean explícitamente silenciados

En algunos casos, ciertos errores pueden ser silenciados porque no suponen un problema potencial para la aplicación.

De nuevo, en un entorno asíncrono, puede que una determinada petición tenga como fin refrescar algún dato en pantalla no crítico. Si está acción se realiza de forma periódica, no es necesario advertir al usuario de que en un determinado momento, ésta ha podido fallar por saturación del servidor. En ese caso, el error puede silenciarse esperando a que la siguiente petición se resuelva con éxito.

// Silenciado
function refreshStats () {
    var ajaxParams = {
        url: 'http://myserver.com/api/getStats/',
        method: 'get'
        success: function ( data ) {
            mySystemChartsPrinter.print( data );
        }
    };
 
    $.ajax( ajaxParams );
}
 
setTimeout( refreshStats, 3000 );

Ante la ambigüedad, evita la tentación de adivinar

Los condicionales pueden ser peligrosos cuando se intenta reunir varias cláusulas en una sola instrucción. En ciertas ocasiones podemos tenerlo muy claro a la hora de escribirlos, pero quizá al volver sobre ellos más adelante, la cosa no esté igual de clara.

// Confuso
if ( ! a && b || c > 0 ) {
    /* ??? */
}

La propiedad distributiva hace que podamos dudar en un momento dado qué condiciones tienen que cumplirse en el fragmento de arriba para entrar:

// ? => ( ! a && b ) || ( c > 0 )
// ? => ! ( a && b || c > 0 )
// ? => ! a && ( b || c > 0 )

Este tema depende mucho de nuestra experiencia como programadores: podemos verlo muy claro de entrada porque entendemos cómo Javascript evalúa las condiciones dadas, pero el transfondo es evitar el análisis lógico de toda la estructura para entender qué hace o cuándo se cumple. Nos movemos aquí en una variante de la filosofía Don’t Make Me Think.

Debería haber una -y preferiblemente solo una- forma obvia de hacerlo

Esto en Javascript no suele cumplirse al pie de la letra. Dada la flexibilidad del lenguaje, es habitual alcanzar un mismo resultado desde varios planteamientos diferentes. Sin embargo, la idea es buscar cuál de esos caminos es el más indicado.

Aquí pocos ejemplos pueden ponerse dado que es un tema muy subjetivo: una solución que personalmente me pueda resultar elegante, a otro desarrollador le puede parecer compleja o ilegible.

Del mismo modo, un código muy limpio en sintaxis puede resultar pesado para el intérprete requiriendo una optimización para determinados escenarios… Y viceversa: un código extremadamente eficiente, puede ser completamente opaco y quizá sea interesante sacrificar rendimiento por legibilidad.

Sea como fuere, aquí prima el sentido común: si tenemos varias formas de hacer algo, busquemos aquella con un mejor compromiso entre rendimiento, mantenimiento y legibilidad.

Aunque esa forma no resulte obvia a simple vista a menos que seas holandés

Pero por supuesto, esa solución balanceada no tiene porque ser evidente… Toca analizar:

  • Rendimiento: tenemos varias herramientas a nuestra disposición extremadamente útiles para medir el rendimiento de un código. Desde la propia consola del navegador, a servicios online como JSPerf, o analizadores para Grunt/Gulp. Informaros sobre todas ellas y tenedlas en cuenta cuando se trata de medir la complejidad de un algoritmo o el rendimiento puntual de un método o función.
  • Mantenimiento: tu solución tiene que ser testeable. El respaldo de pruebas unitarias te garantiza que el código funciona, al menos bajo el SUT escogido para las mismas. Si surge algún error más adelante, siempre puede añadirse dicha excepción a las pruebas para, a partir de ahí, corregir el código principal.
  • Legibilidad: comparte tu solución con otros colegas. El pair programming o cualquier otra metodología que te permita una revisión externa es siempre deseable. Si un compañero no entiende cómo funciona lo que has hecho, es una señal de alerta. Aquí puede argumentarse que no todo el que revisa el código tienen porqué tener la misma experiencia y bagaje técnico que el desarrollador, pero no por ello se pierde la idea de fondo: si tu código es completamente opaco a los ojos de otro programador, quizá esté incorporando alguna capa de complejidad innecesaria que bien podría ahorrarse.

NOTA: En el texto original, el término holandés hace referencia a la nacionalidad del creador de Python, Guido van Rossum, y en general a todos aquellos que dieron forma a los primeros borradores del lenguaje en su foro oficial. De ahí que cualquier solución final adoptada que no pueda no resultar obvia, es posiblemente responsabilidad de un holandés.

Ahora es mejor que nunca

Poco que decir al respecto: mejor afrontar un problema a la mayor brevedad, que dejarlo relegado.

Esta regla bien puede aplicarse a multitud de aspectos complementarios al propio lenguaje:

  • Aprender de una vez ES6 en lugar de esperar a que el 100% del parque de navegadores lo implemente. El tan manido ‘no estoy al día porque en mi empresa no podemos utilizarlo’, es una escusa criminal que nunca deberiamos oir.
  • Aprender programación funcional aún con la falsa sospecha de que no vamos a utilizarlo.
  • Comenzar a escribir esos tests unitarios que hemos ido retrasando.
  • Añadir ahora esas bibliotecas de Grunt o Gulp que tanto trabajo nos van a ahorrar a la larga pese a que ahora nos lleve algo de tiempo familiarizarnos con ellas.
  • Tomarnos un tiempo en ver qué utilidades facilita nuestro editor de código o IDE para nuestro trabajo diario.

Aunque nunca es a menudo mejor que ‘ahora mismo’

Pero siempre con cabeza. No podemos volvernos locos y lanzarnos al vacío sin arnés en mitad de un proyecto crítico. La idea inicial de esta premisa era la de pensar los cambios con calma: temas como la retrocompatibilidad en una versión nueva en una API, o refactorizados masivos a horas de una entrega en producción.

Sin embargo, en el entorno de Javascript, esto aplica perfectamente a las nuevas tecnologías y bibliotecas que continuamente salen al mercado. Una nueva versión del framework de moda no puede significar tirar abajo todo un proyecto en desarrollo para re programarlo con él desde cero. Tampoco es responsable abrazar ese nuevo meta lenguaje que compila en Javascript (ya sea CoffeeScript, TypeScript, o loQueSeaScript) y portar nuestro proyecto sin mirar atrás.

Ocurre que muchas veces, una nueva bibilioteca o framework, no está aún maduro cuando lo adoptamos y eso puede llevarnos a un callejón sin salida en algún momento. Ocurrió en el pasado con Angular por ejemplo: determinadas aplicaciones que comenzamos a portar como si no hubiera un mañana, acabaron topándose con las limitaciones intrínsecas de la plataforma que nadie había advertido en un rápido análisis inicial. Finalmente, muchas veces debemos detener el desarrollo de un proyecto para contribuir a mejorar el código en el que se basa y así permitir esos malabares que de forma original no era posible.

La idea en esencia de este punto sería: si tienes que escribir más de 10 líneas de código de forma inmediata, piénsatelo bien.

Si la implementación es difícil de explicar, es una mala idea

En este punto, Tim está parafraseando a Einstein:

Si no eres capaz de explicarle algo a un niño de 6 años, es que no lo entiendes lo suficientemente bien.

Y la idea tras esta premisa es obvia: si es difícil de explicar es porque es compleja. Las ideas complejas conllevan dudas y errores potenciales durante su implementación. Bajo este escenario, siempre resulta más interesante abordar la estrategia del divide y vencerás. Descomponer un problema complejo en pequeñas partes más manejables garantiza una mayor tasa de éxito.

Si la implementación es fácil de explicar, puede ser una buena idea

Derivada directamente de la anterior, si una nueva funcionalidad es sencilla de implementar, es porque la idea subyacente será igualmente simple. Y si una idea es fácil de explicar y es captada rápidamente por nuestro equipo, público o usuarios, entonces posiblemente sea buena.

Los namespaces son una gran idea; ¡tengamos más de esas!

En programación, los espacios de nombres, o namespaces, son contenedores dentro de los cuales se organiza el código, de manera que las variables, funciones o métodos que definamos dentro no entren en conflicto con otros ya existentes fuera de ellos.

El ejemplo clásico de espacio de nombre en Javascript es el módulo que ya hemos comentado con anterioridad. Todo aquello que se defina en el interior de un módulo, queda circunscrito a su ámbito.

Dentro del ecosistema Javascript, dominado por las bibliotecas y herramientas de terceros, el uso de espacios de nombres es vital para evitar que la funcionalidad de uno, sobreescriba o entre en conflicto con las de otro. Eso permite, por ejemplo, que los métodos de una biblioteca como jQuery puedan convivir con los de otra similar como Lodash.

// Namespace
var $ = ( ( document, window, $ ) => {
    var node = Node.prototype,
        nodeList = NodeList.prototype,
        forEach = 'forEach',
        trigger = 'trigger',
        each = [][ forEach ],
        dummy = document.createElement( 'i' );
 
    nodeList[ forEach ] = each;
 
    window.on = node.on = ( event, fn ) => {
        this.addEventListener( event, fn, false );
        return this;
    };
 
    nodeList.on = ( event, fn ) => {
        this[ forEach ] ( ( el ) => {
            el.on( event, fn );
        } );
 
        return this;
    };
 
    window[ trigger ] = node[ trigger ] = ( type, data ) => {
        var event = document.createEvent( 'HTMLEvents' );
        event.initEvent( type, true, true );
        event.data = data || {};
        event.eventName = type;
        event.target = this;
        this.dispatchEvent( event );
 
        return this;
    };
 
    nodeList[ trigger ] = ( event ) => {
        this[ forEach ]( ( el ) => {
          el[ trigger ]( event );
        } );
 
        return this;
    };
 
    $ = ( s ) => {
        var r = document.querySelectorAll( s || '☺' ),
            length = r.length;
 
        return length == 1 ? r[ 0 ] : r;
    };
 
    $.on = node.on.bind( dummy );
    $[ trigger ] = node[ trigger ].bind( dummy );
 
    return $;
} )( document, this );

El código anterior es una versión reducida de jQuery que utiliza un nombre de espacio propio. Eso permite que sus métodos internos no entren en conflicto con otros similares de otras bibliotecas. Sí; son una gran idea.

Los sistemas de control de versiones

No quiero cerrar este apartado de grandes ideas sin mencionar la que considero más importante en el desarrollo a día de hoy y aplicable a todos los lenguajes de programación: la importancia que han cobrado los sistemas de control de versiones tanto para la redistribución de código como para la democratización del software. GitHub o BitBucket son los referentes ineludibles aquí y, aunque no sean parte intrínseca de un lenguaje concreto, su uso bajo cualquiera de ellos es necesario e incuestionable.

Conclusión

Hasta aquí nuestra reinterpretación del ZEN para su uso en Javascript. Como se comentó en la introducción, no se trata de un conjunto de normas que haya que seguir. Se puede estar más o menos de acuerdo con alguna de ellas, y en completo desacuerdo en otras. Y es así como hay que tomarlo: como recomendaciones que pueden ayudarnos a mantener un código limpio, sencillo y mantenible.

Queda a vuestro criterio adoptar algún punto de ahí arriba para incorporarlo a vuestro trabajo diario, o continuar con vuestros respectivos sistemas personales 🙂

Más:

{6} Comentarios.

  1. Rubén

    ¡Espectacular! ¡Un artículo increíble! Muy, muy interesante.

    Al final parece que todo queda un poco al buen o mal juicio del desarrollador. Unas prácticas son buenas en un contexto, las mismas prácticas no son tan buenas en un contexto diferente.

    Me ha encantado, y tengo una pregunta: ¿sería posible que cada uno de los puntos fuera enlazable? Quiero decir, si quiero dirigir a un amigo a un punto en concreto, ¿sería posible disponer de enlaces para cada uno de los puntos en lugar de un único enlace al artículo completo? No lo tomes como amenaza, este artículo lo guardo para poder pasárselo como referencia a muchos amigos, pero si pudiera enlazar a cada uno de los puntos individualmente… ya sería la leche.

    • Carlos Benítez

      Gracias por el feedback! Efectivamente es como dices: debería ser el buen juicio del desarrollador el que decida lo mejor en cada caso según el contexto y, sobre todo, su experiencia técnica.

      Tal y como has pedido, he añadido un enlace a cada punto para que puedas compartirlo de forma individual. Basta con que pinches encima de cada recomendación para que obtengas la URL directa.

      Saludos!

  2. Yago

    Sinceramente el estilo del código que estás usando es un poco feo. Cualquier preset de ESLint sangraría rojo por todas partes. Entre otras cosas te aconsejo cambiar:

    En ES6, está desaconsejado var en beneficio de let y const.
    La declaración simplificada de variables es fea, nada de Zen.
    Bindea las funciones en lugar del usar «$this».

    Saludos!

    • Carlos Benítez

      Buenas!
      Si por el estilo de código te refieres a la indentación y a los espacios, es la que siempre aconsejo y la que exijo cuando audito. Más aún, es la mejor cuando se redacta un tutorial porque aporta mucha mayor claridad y legibilidad. He escrito mucho al respecto 😉

      En cuanto al ‘let’ y ‘const’, no cabe duda de que es lo recomendable en ES6, de hecho si revisas el blog, verás toda la documentación que he expuesto al respecto. Sin embargo, si uso ‘var’ en los ejemplos, es para que se puedan analizar fácilmente en la consola del navegador. Como sabrás, si utilizas ahí un ‘const’ por ejemplo, te devolverá error en una segunda ejecución, obligándote a recargar la página. Es solo una cuestión de pragmatismo: era costumbre en este blog que los lectores abrieran la consola, pegaran los fragmentos de código y experimentasen con ellos. De ahí que también, todas las salidas utilicen un ‘console.info()’.

      El ‘bindeo’ de funciones (vaya vocablo más feo) no siempre es lo correcto: cuando guardamos la referencia contextual en memoria caché a través de $this, podemos aplicar tests unitarios fácilmente más adelante gracias a stubs. Sin embargo, cuando se contextualiza el valor de ‘this’ a través de bind, la cosa se vuelve compleja. Yo personalmente recomiendo alternar los métodos en función de las necesidades del código. No obstante, de nuevo tienes más información en este mismo blog al respecto, además muy reciente 😀

      Finalmente, no sé qué quieres decir con respecto a la declaración simplificada de variables. Te agradecería que lo detallases.

      Un saludo y gracias por el feedback!

  3. josejuan

    Muy buena entrada. Además, puede aplicarse directamente a muchos otros lenguajes.

  4. andy

    Hola Carlos!

    Grandísima entrada, me ha gustado mucho, ahora mismo la comparto!

    Respecto a lo que comenta Yago, de la declaración simplificada, creo que se refiere a cuando declaras varias variables en una misma expresión separadas por coma, en lugar de utilizar varias expresiones para cada variable, personalmente también prefiero la segunda.

    Un saludo,

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *