¿Sabías esto de JavaScript?

JavaScript logo
JavaScript es un lenguaje de programación interpretado*, definido como orientado a objetos basado en prototipos (class-less), imperativo, débilmente tipado y dinámico.

JavaScript sigue algunos de los principios de la programación orientada a objetos, con un enfoque prototipado en donde los objetos ya existentes pueden servir de prototipo para los que se necesite crear. Además del paradigma OOP, JavaScript también permite trabajar el paradigma de programación funcional.

Características como funciones variádicas (número indefinido de parámetros en una función), tipos de datos dinámicos, hoisting, coerción, entre otros, hacen que JavaScript sea un lenguaje muy flexible y permisivo, por lo que es mejor conocer los principios del lenguaje para evitar cometer errores y caer en malas prácticas.

Como en la mayoría de los lenguajes de scripting, el tipo de dato de un objeto está asociado a su valor (duck typing). JavaScript soporta varias formas de comprobar el tipo de un objeto, y una de ellas es mediante el operador typeof.

    typeof "" === "string"
    typeof 10 === "number"
    typeof true === "boolean"
    typeof alert === "function"
    typeof window === "object"
    typeof (void 0) === "undefined"

Hoisting en JavaScript

En la mayoría de los lenguajes de programación, el ámbito/alcance/scope de una variable está en el bloque donde fue definida (block-level scope), por ejemplo, dentro de un bloque if, for, while; sin embargo en JavaScript (ECMAScript 5.1) esto no es así, puesto que el alcance de una variable está en la función en donde fue declarada (function-level scope).

El hoisting o “elevamiento” es una de las particularidades de JavaScript y ocurre cuando declaramos una variable dentro de una función (su scope), entonces el intérprete* interno mueve la declaración (creación) de la variable al inicio de la función.

    var x = 5;

    (function () {
        console.log("x:", x); //se espera 5
        var x = 10;
        console.log("x:", x); //se espera 10
    }());

😕 ¿Que es lo que ocurrió?, la primera vez que imprimimos la variable x, se esperaba 5 (el valor de la variable global), sin embargo obtuvimos undefined. Así es como lo interpreta el browser: 💡

    var x = 5;

    (function () {
        var x; //  undefined
        x = 10;
        console.log("x:", x); //-> 10
    }());

hoisting

➡ ⭐ Continúe leyendo el artículo completo… ¿Qué es hoisting?


Coerción en JavaScript

Se podría decir que coerción es la acción de forzar a que un objeto se comporte como si fuera de otro tipo. Sin lugar a dudas, éste es uno de los aspectos más interesantes de JavaScript. Al ser un lenguaje débilmente tipado y dinámico, no es necesario especificar el tipo de dato para las variables porque el tipo está asociado al valor (duck typing), esto hace que en tiempo de ejecución podamos cambiar el valor de las variables sin ningún problema.

💡 Una de las ventajas de esta característica, es que podemos comprobar la existencia de un objeto mediante su valor, por lo que JavaScript permite implementar muy fácil algunos principios OOP como la sobrecarga de métodos (overload), sobrescritura de métodos (override) y polimorfismo. Un ejemplo de ello es como jQuery implementa el patrón composite en su constructor, definiendo un método que se puede adaptar dependiendo de la cantidad de argumentos, o del tipo de los argumentos.

Por ahora veamos como funciona la coerción hacia boleanos, es decir, como se comportan otros tipos de datos si se tratan como boleanos.

    var var1, // -> undefined
        var2 = 9;

    if (v1) { } // -> undefined se evalúa como false
    if (v2) { } // -> 9 se evalúa como true

    if (!v1) { } // -> al negar undefined, se evalúa como true
    if (!v2) { } // -> al negar 9, se evalúa como false

    var v3 = !!v1; // -> convierte explícitamente undefined a false
    var v4 = !!v2; // -> convierte explícitamente 9 a true

    // asigna 0 si la coerción v1 es false
    v1 = v1 || 0;

    // ejecuta el alert si la coerción de v2 es true
    v2 && alert("Hey!");

💡 Nótese el uso del operador !(NOT), el cual toma el operando de la derecha y lo evalúa como boolean pero negado. 😎 Este truco se usa bastante para comprobar la existencia de un valor no falsy en una variable.

Así mismo, la doble negación !!(DOUBLE NOT), convierte un valor de cualquier tipo a boolean. 😎 Este truco se usa para comprobar explícitamente si un valor representa true o false.

Truthy and Falsy

Conocer los valores falsy que resultan de una comparación o de la coerción que hace JavaScript es muy importante y nos ayudará a evitar errores de lógica. La siguiente tabla muestra los valores falsy.

falsy coercion
false false
0 false
“” false
NaN false
null false
undefined false

➡ Todos los demás valores son evaluados como truthy (boolean: true), incluyendo las funciones, arrays vacíos, objetos vacíos y los string "0" y "false".

Coerción a números

⭐ El operador unario +(plus) se puede utilizar para convertir un string a number, si dicho string representa un número en su totalidad, de lo contrario devuelve NaN (Not A Number).

    n = +"13";  //  13, type: number
    n = +"0";   //   0, type: number
    n = +"5px"; // NaN, type: number

➡ ⭐ Continúe leyendo el artículo completo… ¿Qué es coerción?


Funciones variádicas

Esta es otra de las características que hacen flexible a este lenguaje de scripting.
En JavaScript no se necesita ningún truco especial como la sobrecarga de métodos (overload) para hacer que la misma función reciba un número diferente de argumentos, ya que disponemos de la variable local arguments, la cual está disponible dentro del body de las funciones (excepto Arrow functions) y contiene la referencia a cada uno de los argumentos enviados a la función.

Una función variádica es una función de aridad indefinida, es decir, que acepta una cantidad de argumentos variable.

    function write() {
        console.log(arguments);
    }

    write("JavaScript"); // ["JavaScript"]
    write(5, 6, "Awesome"); // [5, 6, "Awesome"]

😮 El objeto arguments es similar a un Array, pero NO es un Array, ya que no tiene ninguna de las propiedades de un Array, excepto length.

    function write() {
        for(var i = 0; i < arguments.length; i += 1) {
            console.log(arguments[i]);
        }
    }

    write("JavaScript", "is", "awesome!");
    write(1, 2, 3, 4);

➡ ⭐ Continúe leyendo el artículo completo… ¿Qué son las funciones variádicas?


POO en JavaScript

🙄 JavaScript es un lenguaje basado en objetos que en lugar de estar basado en clases, se basa en prototipos. Debido a esta diferencia, puede resultar menos evidente que JavaScript le permite crear jerarquías de objetos y herencia de propiedades.

💡 En lenguajes basados en clases los objetos pueden ser de dos tipos generales: las clases y las instancias. Una clase es una plantilla que define la funcionalidad (métodos y propiedades) de los objetos. Las instancias son objetos “utilizables” creados a partir de una clase y su función es conservar los datos dentro del objeto.

😳 En un lenguaje basado en prototipos como JavaScript no se hace esta distinción, simplemente tiene objetos, que son clase e instancia simultáneamente: son clase con respecto a los objetos que se crean como instancia suya, y son objetos en sí, tomados como instancia de una clase implícita, de la cual “heredan” su prototipo.

💡 JavaScript tiene la noción de un objeto prototipo, que es utilizado como una plantilla de la cual se obtiene las propiedades para crear un nuevo objeto. Cualquier objeto puede especificar sus propiedades, tanto al ser creado como en tiempo de ejecución. Adicionalmente, cualquier objeto puede servir de prototipo para otro objeto, permitiendo que el segundo objeto comparta las propiedades del primero. Y el prototipo puede ser modificado dinámicamente de modo que se afecten todos los objetos que pertenezcan o “hereden” ese prototipo.

Definición de una clase

En los lenguajes basados en clases, se define una clase mediante un constructor.
El constructor es un método que permite crear instancias de una clase en donde la definición de la clase está separada del constructor.

    // clase en C#
    public class Employee {
    
        // constructor
        public Employee (string nombre) {
            // implementación
        }
    }

    // instancia
    Employee david = new Employee("David");

JavaScript sigue un modelo similar, pero sin tener la definición de la clase separada del constructor… ^^’ ¿pero no habíamos dicho que JavaScript es class-less?

Bueno, efectivamente JavaScript (ES5-) es class-less, sin embargo se puede emular una clase definiendo una función como el constructor y utilizando el operador new para crear una nueva instancia.

    // clase y constructor (JS)
    function Persona (nombre) {
        // implementación
    }

    // instancia
    var david = new Persona("David");

Herencia

En lenguajes basados en clases, se crea una jerarquía de clases mediante la herencia. En una definición de clase se puede especificar que la nueva clase hereda las propiedades de alguna clase ya existente, por lo tanto es una subclase.

    // HomeController hereda de Controller (C#)
    public class HomeController : Controller {
    
    }

💡 JavaScript implementa la herencia mediante prototipos, asociando un objeto prototipo con una función constructora. 😮 La herencia realmente se basa en la clonación de un prototipo en donde las propiedades heredadas se determinan mediante la delegación automática, esto es, el orden o jerarquía en que las propiedades compartidas son resueltas en la cadena de prototipos de un objeto.


Nota: el término clase en JavaScript no tiene significado técnico, ya que no hay separación entre clase y constructor, al igual que el término instancia lo podemos utilizar informalmente para dar a entender que un objeto es creado a partir de una función constructora. Del mismo modo, los términos clase base y subclase no tienen significado técnico, pero se usan informalmente para referirse a objetos que están por encima o por debajo de la cadena de prototipos.


1. Para implementar la herencia primero definimos el constructor del objeto base con todas sus propiedades:

    function Runner () {
        this.tasks = [];
        this.add = function (describe, action) {};
        this.run = function () {};
    }

2. Luego definimos el constructor de la subclase con sus propiedades:

    function RunnerPlus () {
        this.beforeRun = function () {};
        this.afterRun = function () {};
    }

3. Y finalmente pasamos el prototipo (o instancia) del objeto base y se lo asignamos al prototipo de la subclase, de este modo heredará las propiedades del objeto base.

    RunnerPlus.prototype = new Runner();

    //reparamos el constructor original
    //que fue sobrescrito por la herencia
    RunnerPlus.prototype.constructor = RunnerPlus;

⭐ En el ejemplo anterior, cada vez que instanciamos un objeto Runner o RunnerPlus, se crean repetidamente los métodos definidos en su clase/constructor, lo cual no es recomendado ya que estamos ocupando más memoria de lo necesario. La forma más eficiente de crear métodos en una “clase” es hacerlo a través de su prototype. 😎 Ésta es una buena práctica ya que los métodos se registran sólo una vez en el prototipo de la clase/constructor y su referencia es compartida entre todas las instancias, aprovechando mejor la memoria. Veamos esto en el siguiente ejemplo.

❗ Usaremos Strict mode en el constructor para evitar que se creen propiedades en el objeto global cuando no se use el operador new al crear una instancia.

    //definimos el constructor base
    function Runner () {
        "use strict";
        this.tasks = [];
    }

    //definimos las propiedades compartidas
    Runner.prototype = {
        add: function (describe, action) {},
        run: function () {}
    };

    //definimos el constructor de la "subclase"
    function RunnerPlus () {
        "use strict";
        this.callbacks = {
            beforeRun: null
        };
    }

    //heredamos del objeto base
    RunnerPlus.prototype = new Runner;

    //corregimos el constructor
    //que fue sobrescrito por la herencia
    RunnerPlus.prototype.constructor = RunnerPlus;

    //extendemos el prototipo de la "subclase"
    RunnerPlus.prototype.clear = function () {};
    RunnerPlus.prototype.beforeRun = function (callback) {};

    //@override
    RunnerPlus.prototype.toString = function () {
        return "[object RunnerPlus]";
    };

➡ ⭐ Continúe leyendo el artículo completo… POO en JavaScript


Conclusión

JavaScript es asombroso por su flexibilidad, y rompe algunas barreras de los lenguajes estrictos, sin embargo debemos estar atentos a evitar las malas prácticas, ya que muchas veces por desconocimiento del lenguaje, escribimos código que va en contra de los principios del lenguaje.

Ésta fue una introducción a las buenas prácticas, un llamado a que estudiemos mas a fondo las reglas que definen JavaScript, de manera que podamos escribir código legible, extensible y fácil de mantener.

➡ Recomiendo leer los artículos completos de los cuales este post toma fragmentos:

😎 Happy coding!

Advertisements

6 thoughts on “¿Sabías esto de JavaScript?

  1. Excelente compilación de lo que en esencia es JavaScript y de sus fortalezas y tips para saber manejar POO con este lenguaje, gracias por el aporte ya que me ha servido para aclarar dudas y comprender mejor como funciones las “clases”.

  2. Excelente post, me servirá de base junto con otras cosas que estoy recopilando para hacer un post sobre Hoisting 😀
    Saludos desde Venezuela 😉

Comentarios

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s