Funciones en Node.js y JavaScript. Lo realmente importante

Funciones en Node.js y JavaScript. Lo realmente importante

Al igual que Objetos en Node.js y Javascript. Lo realmente importante. “Funciones en Node.js y JavaScript. Lo realmente importante” se refiere a los principios detrás de las funciones, lo que es realmente importante para continuar aprendiendo, mejorar el entendimiento de funciones en programación, Node.js y en Javascript.

Antes de hablar de funciones en Node.js y JavaScript, vale la pena recordar o definir lo que es una función en programación.

¿Qué son las funciones?

Las funciones son un elemento muy importante en cualquier lenguaje de programación. Sabemos que la programación funcional se inventó incluso antes de cualquier lenguaje de programación basándose en el cálculo lambda de Alonzo Church.

Sintaxis calculo lambda, programación funcional
Sintaxis calculo lambda, programación funcional. Imagen tomada de https://www.slideshare.net/FASTPresentations/introduction-to-lambda-calculus-using-smalltalk-by-facundo-javier-gelatti

En los primeros lenguajes de programación se usaban subrutinas, procedimientos y funciones. Las subrutinas, procedimientos y funciones tienen en común que agrupan un conjunto de operaciones con la finalidad de reutilizarlas muchas veces y solo escribirlas una vez.

Las funciones, a diferencia de los procedimientos y subrutinas, aún se usan en los lenguajes de programación modernos, y son la unida más pequeña de organización de código. Se usan para definir el comportamiento de los objetos, componiéndolos de funcionalidades específicas. Recordemos de otra publicación que los objetos son un grupo de funcionalidades y que contribuyen a la comunicación con otros objetos.

Sin las funciones, un objeto no serviría de mucho. Las funciones definen el comportamiento de objetos. Y también forma funciones más grandes. En conclusión, las funciones nos sirven para.

  • Organizar el código
  • Reusar código
  • Se usa para la composición de objetos, agregando comportamientos.
  • Se usa para la composición de funciones más complicadas

¿Qué son las funciones en Node.js y JavaScript?

En Node.js y JavaScript, y de hecho en cualquier ambiente donde se ejecute JavaScript, las funciones son todo lo descrito en la sección anterior. Y además son objetos.

  • Al ser objetos, pueden tratarse como cualquier otro valor:
    • Pueden ser asignadas a variables y propiedades de otros objetos
    • Crearlas dinámicamente durante la ejecución del código JavaScript
    • Tener sus propiedades y métodos
    • Ser parámetros para otra función
    • Ser el valor de retorno de una función
  • Adicionalmente el cuerpo de una función proporciona ámbito local a las variables y parámetros

Aunque no es el tema de esta publicación, todas las características listadas hacen que JavaScript pueda usarse como un lenguaje de programación funcional.

¿Cómo crear funciones en Node.js y JavaScript?

Las tres formas recomendadas para crear funciones son las siguientes.

  • Declaración normal
  • Función como expresión
  • Funciones flechas

Declaración de función

Este es el método más común, muy similar en otros lenguajes de programación. Se usa la palabra reservada function, seguida del nombre de la función, luego una lista de argumentos entre paréntesis, los cuales se separan por comas. Esta lista de argumentos es opcional.

Por último el cuerpo de la función usando llaves { }. El cuerpo de la función contiene las sentencias que necesites.

function nombre(argumento1, argumento2, ...argumentoN) {
  // sentencias
}

Ejemplo concreto:

function sumar(a, b) {
  return a + b;
}
sumar(1, 2); // 3

Para ejecutar el código de la función es necesario invocarla. La invocación se hace con un par de paréntesis y dentro los argumentos necesarios, separados por comas. Así como el ejemplo anterior.

Una función siempre regresa algún valor, incluso si no se define explícitamente. Si no defines lo que retorna una función, por defecto el valor de retorno será undefined.

function sumarSinReturn(a, b) {
  const resultado = a + b;
}
sumarSinReturn(1, 2); // undefined

Si ejecutamos la función, nos regresa undefined porque no le indicamos explícitamente que regrese algún valor

Función en forma de expresión

Esta forma de crear funciones es mucho más flexible, su definición puede aparecer donde sea que se pueda definir una expresión. Eso le da la habilidad de ser asignada a una variable o a una propiedad de un objeto.

Su sintaxis es igual que la declaración de una función que vimos anteriormente, pero al ser asignada a una variable o propiedad, su nombre es opcional.

function [nombreOpcional](argumento1, argumento2, ...argumentoN) {
  // sentencias
}

A continuación vamos a ver algunos ejemplos.

// Sin nombre, tambie  conocida como funcion anonima
const sumar = function(a, b) {
 return a + b;
};
// Con nombre
const sumar = function sumar(a, b) {
  return a + b;
};
const calculadora = {
  sumar: function(a, b) {
    return a + b;
  }
};
const persona = {
  // como propiedad de un objeto
  comer: function() {
    return 'Comiendo...';
  }
};

Funciones flecha

Las funciones flecha es la forma más nueva de crear funciones, mucho más parecido a las funciones matemáticas del álgebra. Se sienten muy convenientes porque su sintaxis es mucho más reducida. Son una alternativa a las funciones en forma de expresión, es más rápido escribirlas. Sin embargo tiene muchas limitantes en comparación a las otras dos formas de crear funciones. Aunque si las usas para programación funcional son bastante efectivas.

Para ser honesto a mí me parece que si su uso no es enfocado a la programación funcional, sí que le agrega más complejidad al uso de funciones en JavaScript, de por sí las funciones en Node.js y Javascript pueden ser muy diferentes en comparación con otros lenguajes.

Pero bueno, veamos su sintaxis.

argumento => expresión;
// Com mas de un arguento es necesario parentesis
(argumento1, argumentN) => expresión;
// Con varias lineas de sentencias, es necesario las llaves {}
argumento => {
 // sentencias
};
// Con mas de un argumento y con varias lineas se sentencias
(argumento1, argumentoN) => {
  // sentencias
};
// Sin argumentos es necesario los parentesis
() => expresión

Cuando se usan expresiones, no es necesario definir explícitamente el return. El resultado de la expresión es el valor de retorno.

const calcularCuadrado = a => a * a;
const sumar = (a, b) => a + b;
const saludar = () => 'Hola';
// invocaciones
calcularCuadrado(5); // 25
sumar(1, 2); // 3
saludar(); // 'Hola'

Cuando queremos que el cuerpo de la función tenga varias líneas de sentencias, se usan llaves. Además si queremos que la función regrese algún valor, entonces explícitamente usamos la sintaxis del return.

Ejemplos.

const calcularCuadrado = a => {
  const result = a * a;
  return result;
};
const sumar = (a, b) = > {
  const result = a + b;
  return result;
};
// invocaciones
calcularCuadrado(5); // 25
sumar(1, 2); // 3

Funciones anidadas o internas

Una función se puede definir dentro de otra función, es decir, dinámicamente podemos crear funciones internas dentro de otra función principal e invocarlas.

function saludar() {
  function saludarInterna() {
    return 'Hola';
  }
  const saludo = saludarInterna();
  console.log(saludo);
}
saludar(); // 'Hola'

En la siguiente sección veremos otras funciones anidadas.

Ámbito local de funciones

Las funciones en Node.js y JavaScript proporcionan un ámbito de valores, local al cuerpo de la función, es decir, lo que está definido en el cuerpo de la función solo puede ser referenciado dentro de la misma.

function abuelo() {
  const nombre = 'Jaime';
  const apellido = 'Cervantes'
  function padre() {
    const apellido = 'Buendía';
    function hijo() {
      const apellido = 'Pérez';
      const nombreCompleto = `${nombre} ${apellido}`;
      console.log(nombreCompleto); // Jaime Pérez
    }
    hijo();
    const nombreCompleto = `${nombre} ${apellido}`;
    console.log(nombreCompleto); // Jaime Buendía
  }
  padre();
  const nombreCompleto = `${nombre} ${apellido}`;
  console.log(nombreCompleto); // Jaime Cervantes
}
abuelo();

Las funciones internas pueden acceder a las variables de su función padre (por nombrarlas de alguna manera). En el ejemplo anterior se puede notar que la función hijo puede hacer referencia a la constante nombre de la función abuelo. Esto nos produce Jaime Pérez. En la siguiente sección lo explicamos mejor

Closures o cierres

El anidado de funciones permite que las funciones hijas tengan su propio ámbito local, oculto de las funciones padres. Al mismo tiempo estas funciones internas tienen acceso a los valores definidos en las funciones padres. Este encapsulamiento de información y al mismo tiempo acceso a información externa, se le llama closure o cierre.

Continuemos con el ejemplo de la sección anterior, las funciones abuelo, padre e hijo

function abuelo() {
  const nombre = 'Jaime';
  const apellido = 'Cervantes'
  function padre() {
    const apellido = 'Buendía';
    function hijo() {
      const apellido = 'Pérez';
      const nombreCompleto = `${nombre} ${apellido}`;
      console.log(nombreCompleto); // Jaime Pérez
    }
    hijo();
    const nombreCompleto = `${nombre} ${apellido}`;
    console.log(nombreCompleto); // Jaime Buendía
  }
  padre();
  const nombreCompleto = `${nombre} ${apellido}`;
  console.log(nombreCompleto); // Jaime Cervantes
}
abuelo();

El resultado de la invocación de la función abuelo es:

Jaime Pérez --> Desde función hijo
Jaime Buendía --> Desde función padre
Jaime Cervantes --> Desde funcion abuelo

Entre más interna la función, más alcance a todos los ámbitos de las demás funciones “externas padres” tiene. Como la imagen de abajo, es como si los ámbitos de las funciones abuelo y padre estuvieran dentro del ámbito de la función hijo.

Closure, ámbito de funciones en js
Closure, ámbito de funciones en js

Siempre una función tomará el valor de la variable que este más cerca de su propio ámbito local. Las variables dentro de su propio ámbito local son las de mayor relevancia. Esto permite que los nombres de variables y constantes no colisionen entre ámbitos anidados.

La función hijo, tiene acceso a las constantes nombre y apellido de la función abuelo. También tiene acceso a la constante apellido de la función padre. Pero la constante apellido dentro de la propia función hijo está más cerca que lo definido en padre y abuelo, tiene mayor relevancia. Entonces el nombre completo que se imprime en la consola es Jaime Pérez en lugar de Jaime Buendía o Jaime Cervantes.

La función padre si tiene acceso a las constantes nombre y apellido de la función abuelo. En su propio ámbito tiene una constante apellido igual a Buendía. Al estar más cerca este valor, no toma el apellido de la función abuelo que está más lejos. Por eso en la consola el nombre completo que se imprime es Jaime Buendía. Luego la función padre NO tiene acceso a la constante apellido de la función hijo.

Finalmente se imprime en la consola Jaime Cervantes Velasco porque las constantes nombre y apellido están definidas en el propio ámbito local de la función abuelo. La función abuelo NO tiene acceso a las constantes apellido de sus funciones internas padre e hijo.

POO surgio de funciones

Ya que vimos un poco de las funciones anidadas y closures, podemos hablar de como se descubrió la programación orientada a objetos. Esto refleja la importancia de las funciones en los lenguajes de programación.

Ole Johan Dahl y Kristen Nygaard se dieron cuenta de que la pila de llamadas de las funciones en ALGOL, podía ser movida a un Heap. Esto permite que las variables declaradas por una función puedan existir incluso después de que la función termine su ejecución y retorne algún valor.

De esta manera la función se convirtió en el constructor de la clase, las variables locales en propiedades de la instancia de clase y las funciones internas en sus métodos. Y así en 1966 la programación orientada a objetos fue descubierta.

Este comportamiento lo podemos implementar usando funciones en Node.js y JavaScript y aprovechando su habilidad de crear closures.

function crearPersona(nombre, apellido) {
  function saludar() {
    return `Hola soy ${nombre}...`;
  }
  function comer() {
    return 'Comiendo...';
  }
  function getNombre() {
    return `${nombre} ${apellido}`;
  }
  const instancia = {};
  instancia.saludar = saludar;
  instancia.comer = comer;
  instancia.getNombre = getNombre;
  return instancia
}
const jaime = crearPersona('Jaime', 'Cervantes');
jaime.comer(); // Comiendo...
jaime.saludar(); // Hola soy Jaime
jaime.getNombre(); // Jaime Cervantes

Los parámetros nombre y apellido, están dentro del ámbito local de la función crearPersona, así que funcionan igual que variables dentro del cuerpo de la función. Las funciones internas continúan teniendo acceso a esos parámetros incluso después de que la función padre regrese su valor, un objeto literal que es la instancia de una persona.

Luego cuando la instancia jaime invoca a su método getNombre, esta propiedad hace referencia a la función interna getNombre de la función crearPersona. Debido al closure de la función interna getNombre, tenemos acceso a los parámetros nombre y apellido incluso mucho después de que la función padre crearPersona haya regresado su valor.

Nombres de funciones en Node.js y Javascript

Tenemos que estar conscientes de que la programación y el desarrollo de software es una actividad social, de mucha comunicación. Y entre más eficiente sea esta comunicación, mayor es el éxito del software. Esto nos permite ahorrar el tiempo y el recurso económico de todos los involucrados. Estoy hablando de programadores y no programadores, inversionistas, clientes y usuarios.

Una de las formas de comunicación entre compañeros programadores y muchas veces uno mismo en el futuro, es a través de un código fácil de comprender, y para contribuir a esta fácil comprensión debemos de escoger con mucho cuidado el nombre de nuestras funciones.

Toma en cuenta las siguientes recomendaciones. Pero teniendo en mente que solo son ejemplos, y a la hora de escribir tus funciones reales y con el contexto adecuado, muy probable puedas idear mejores nombres de funciones a los aquí mostrados.

Dedica el tiempo suficiente para nombrar tu función.

Igual de importante que nombrar las variables, así las funciones. Las funciones son las unidades más pequeñas que nos permiten definir comportamientos en aplicaciones. El tiempo invertido en nombrar tus funciones es mucho menor que el tiempo que tienes que invertir tu mismo en el futuro y tus compañeros al tratar de descifrar lo que una función realmente hace. Es como organizar tu cuarto, entre más ordenado, más rápido encontraras las cosas que necesitas, más rápido te cambiaras, o más rápido encontraras tus calcetines, etcétera.

El nombre debe ser muy semántico, describir su objetivo

El nombre de una función debe describir con la mayor claridad posible lo que hace. Es importante que sea un verbo porque una función siempre realiza una o más operaciones enfocadas a una tarea en concreto.

Por ejemplo, si una funcion regresa el nombre completo de una persona ¿Cual de los siguientes nombres encaja mejor?

fullName()
getName();
getFullName()

El nombre que describe mejor el objetivo de la función es getFullName.

Si la función regresa un booleano, el nombre de la función debe indicar que el resultado puede ser true o false. Tal cual el resultado de una condición lógica. Es como hacer una pregunta cuyas posibles respuestas solo pueden ser si o no.

hasChildren(person) {
  return Boolean(person.childre.length);
}
if (hasChildren(currentPerson)) {
  // Haz algo
}

Si hasChildren fuera un método, quedaría de la siguiente forma.

if (currentPerson.hasChildren()) {
  // Haz algo
}

Te das cuenta como la condición se lee como una frase muy entendible. Si currentPerson tiene hijos, entonces...haz algo.

Evitar suposiciones erróneas

Si el nombre describe cosas que en realidad no hace, entonces debemos de renombrar esa función. Por ejemplo, si una función forma el nombre completo de un cliente y regresa ese nombre. ¿Qué función evita mejor las suposiciones erróneas?

function setClientName() {} // Se entiende que el nombre del cliente va a ser modificado
function getFullClientName() {} // Aquí esta claro que solo consigue el nomnbre completo del cliente

setClienteName nos indica que el nombre del cliente será modificado, es un mal nombre. Entonces el nombre que mejor evita las suposiciones erróneas es getFullClientName. No dice que forma el nombre, pero sí que va a regresar un nombre completo, para fines prácticos no nos interesa saber como forma ese nombre completo, solo que no los regrese.

Acuerdos con los programadores

Es muy importante establecer acuerdos para el nombramiento de las funciones. En los ejemplos anteriores he estado utilizando el prefijo get que indica que mando a obtener algo. Pero sería confuso que algunos programadores utilizaran la palabra fetch y otros la palabra retrieve, y otros collect o bring.

Usa el contexto adecuado

Es importante que se entienda el contexto de la función, en anteriores ejemplos utilizamos la funcion getFullClientName, pero dependiendo del contexto de la aplicación, pudiera ser mejor getFullUserName o getFullEmployeeName.

Aunque estos nombres tiene que ver con el contexto del negocio o problema, también hay términos técnicos que los programadores ya están muy acostumbrados y no se deben mezclar con el dominio del problema.

Por ejemplo, el patrón de diseño observador contiene métodos como update, subscribe, publish, notify. Si estamos trabajando con la aplicación de una revista que usa notificaciones nativas del celular, sms, y realiza publicaciones periódicas. Esto también puede crear confusiones, así que se deben de nombrar las funciones con mucho cuidado de tal manera que se distingan entre funciones o métodos del patrón de diseño y las otras relacionadas con el negocio.

El alcance de la función ayuda a la longitud del nombre

El nombre de las funciones pude ser largo o corto dependiendo del alcance que tiene en el software. Por ejemplo, una función que se usa mucho en diferentes archivos, vale la pena que su nombre se a corto. Porque si muchas personas lo utilizan, es importante que sea fácil de escribir y pronunciar.

Por otro lado, si es una función que solo se utiliza en un archivo, su nombre puede ser largo, este tipo de funciones normalmente las utilizan internamente las otras funciones con nombre corto. Entonces las funciones de nombres largos son una forma de explicar lo que hace la función de nombre corto. Esto no quiere decir que no pueda ser corta, pero si necesitas más palabras para describir mejor la función, adelante.

Como ejemplo imaginemos una función que regresa el total de tus ganancias a la fecha actual de todo tu portafolio de inversiones. Donde las ganancias son la suma de los intereses de tus inversiones más las contribuciones que has hecho a la fecha.

// funcion corta, reutilizable en otros archivos o aplicaciones
function getEarnings() {
  return calculateEarnings();
}
// funciones con nombre más largo que describen a la funcion corta
function calculateEarnings() {
 const earnings = calculateCurrentTotalInterest();
 const aditionals = calculateCurrentTotalAdditionalContributions();
  return earnings + aditionals;
}
function calculateCurrentTotalInterest() {}
function calculateCurrentAdditionalContributions() {}

No te preocupes tanto por estos ejemplos, el objetivo es que tengas una idea. En una futura publicación haremos una pequeña aplicación donde veremos como aplicar estas recomendaciones.

Conclusiones

Las funciones en Node.js y Javascript es un tema bastante amplio, es una de las cosas mejor hechas en JavaScript que revelan la potencia del lenguaje. Se nota que Javascript está influenciado por LISP y Scheme.

Así mismo no olvidemos de nombrar correctamente nuestras funciones, ellas son las unidades más pequeñas que permiten organizar el software en comportamientos bien definidos.

La programación funcional es el primer paradigma de programación inventado, de ahí la importancia de las funciones, porque es un paradigma del cual la programación orientada a objetos tomó bases.

No olvidemos que las funciones en Node.js y JavaScript son objetos y por lo tanto pueden tratarse como cualquier valor.

Aún nos faltan varios temas importantes sobre las funciones. Estos son abordados en esta publicación. Cualquier duda, no dudes en escribirla en los comentarios, ¡Estaremos contentos de ayudarte!.

Si quieres, como ejercicio, puedes traducir todos los ejemplos a funciones en forma de expresión y funciones flechas. ¡Diviértete!

Objetos en Node.js y Javascript. Lo realmente importante

Objetos en Node.js y Javascript. Lo realmente importante

Introducción

Objetos en Node.js y Javascript. Lo realmente importante” se refiere a los principios detrás de los objetos, lo que es realmente importante para continuar aprendiendo, mejorar el entendimiento de objetos en programación y en Node.js y Javascript.

Empecemos con el entendimiento de lo que es un objeto en programación.

¿Qué son los objetos en programación?

Se ha dicho mucho que la programación orientada a objetos representa el mundo real. La verdad es que la programación en general trata de representar el mundo real en algo digital. Así que la programación orientada a objetos también trata de representar digitalmente el mundo real, solo que con un poco más de énfasis en el uso de objetos. Pero como hemos establecido en esta publicación, lo importante NO son los objetos, lo importante es:

El paso de mensajes para la comunicación entre objetos

En programación un objeto es una forma digital de agrupar funcionalidades y que algunas de ellas pueden ser análogas a las de un objeto en la vida real, pero que es mucho más limitado y no se debe esperar que sean iguales, porque solo es una representación digital.

Cuando desarrollas software, y necesitas agregar una funcionalidad, modificar o arreglar un error, tu pensamiento lógico está basado en la representación de objetos digitales, sus relaciones y comunicaciones con otras entidades digitales. Nunca se debe pensar que como el objeto real se comporta de cierta manera, su representación también. Es de mucha ayuda conceptualmente, pero en la implementación muy a menudo son diferentes.

Recordemos lo que nos dice Alan Kay:

Lamento que hace tiempo haya acuñado el término Objetos para la programación porque hizo que las personas se enfocaran en la parte menos importante. La gran idea es «Envío de mensajes«

Alan Kay

También un objeto sirve para almacenar datos útiles que otros objetos pueden utilizar. Entonces un objeto nos sirve para:

  • Crear comunicaciones entre representaciones digitales de objetos.
  • Permite agrupar funcionalidades relacionadas con la representación digital del objeto.
  • Almacena datos que otro objeto puede usar. (Funciona como algunas estructuras de datos).

¿Qué son los objetos en Node.js y JavaScript?

Los objetos en Node.js y Javascript, son una colección de pares nombre: valor, parecido a los «arrays asociativos» de PHP. Estos pares de nombre/valor se les llama propiedades, una propiedad es como una variable y puede contener cualquier tipo de valor.

La clave del párrafo anterior es que puede contener cualquier tipo de valor, incluyendo funciones. Y con esto se identifican dos tipos de objetos.

  1. Objetos con funcionalidades y comunicaciones
    1. Que en su mayor parte tienen métodos para comunicarse con otros objetos y resolver problemas.
  2. Objetos con datos. También llamado estructura de datos
    1. Que en su mayor parte contiene datos y que nos sirven para almacenar y enviar información a través de mensajes. Estos objetos son utilizados por los objetos con funcionalidades y comunicaciones.

Recordemos que todo lo relacionado con objetos también funciona en cualquier lugar donde se ejecute JavaScript, node.js incluido.

¿Cómo crear objetos en Node.js y Javascript?

La forma más sencilla de crear un objeto es a través de un objeto literal. Un objeto literal es aquel que se crea con llaves {}, agregando sus propiedades y métodos. Como el siguiente ejemplo.

const jaime = {
  nombre: 'Jaime',
  apellidoPaterno: 'Cervantes',
  apellidoMaterno: 'Velasco',
  edad: 33,
  getNombre: function () {
    return 'Jaime Cervantes Velasco';
  }
};
jaime.getNombre(); // Jaime Cervantes Velasco

Tenemos un objeto con cinco propiedades, nombre, apellidoPaterno y apellidoMaterno son de tipo string, edad es de tipo number y la última propiedad, getNombre() es de tipo function. Aquí tienes más información sobre los tipos de datos.

Aunque más adelante veremos a detalle las funciones, de momento, podemos decir que las funciones nos permiten encapsular y ejecutar un conjunto de operaciones que hacemos muy a menudo. En lugar de repetir el código de estas operaciones muchas veces, mejor ponemos estas operaciones dentro de una función.

Cada vez que necesitemos ejecutar estas operaciones, simplemente ejecutamos la función, a esta ejecución de la función se le llama invocación. Ejemplo de invocación es en la última línea, donde utilizamos paréntesis, jaime.getNombre();.

¿Cómo obtengo el valor de una propiedad?

Para obtener alguna propiedad de un objeto, podemos hacer de dos maneras:

  • Notación de puntos
  • Usando corchetes, muy parecido a como se acceden a los datos de un Array

La primera forma, notación de puntos, es muy sencilla, ejemplo:

const jaime = {
  nombre: 'Jaime',
  apellidoPaterno: 'Cervantes',
  apellidoMaterno: 'Velasco',
  edad: 33,
  getNombre: function () {
    return 'Jaime Cervantes Velasco';
  }
};
jaime.nombre // 'Jaime'
jaime.apellidoPaterno; // 'Cervantes'
jaime.apellidoMaterno; // 'Velasco'
jaime.edad; // 33
jaime.getNombre // function ()  { return 'Jaime Cervantes Velasco'; }

La segunda forma, usando corchetes [], ejemplo:

const jaime = {
  nombre: 'Jaime',
  apellidoPaterno: 'Cervantes',
  apellidoMaterno: 'Velasco',
  edad: 33,
  getNombre: function () {
    return 'Jaime Cervantes Velasco';
  }
};
jaime['nombre'] // 'Jaime'
jaime['apellidoPaterno']; // 'Cervantes'
jaime['apellidoMaterno']; // 'Velasco'
jaime['edad']; // 33
jaime['getNombre'] // function ()  { return 'Jaime Cervantes Velasco'; }
jaime['getNombre']() // 'Jaime Cervantes Velasco'

Aquí accedimos a las propiedades con corchetes usando el nombre de las propiedades, la cuales son strings.

Destaca como invocamos a la función getNombre. Estas dos sentencias hacen lo mismo, invocar a la función getNombre:

jaime.getNombre();
jaime['getNombre']();

El nombre de propiedades son cadenas de caracteres

Cuando usamos la notación de corchetes, usamos una cadena de caracteres para obtener el valor. Lo que mencionamos antes sobre que Un objeto es una colección de pares nombre: valor. El nombre de todas las propiedades son cadena de caracteres (string).

Como los nombres de las propiedades son cadenas de caracteres, entonces podemos definir propiedades encerradas por comillas, como el siguiente ejemplo:

const jaime = {
  nombre: 'Jaime',
  apellidoPaterno: 'Cervantes',
  apellidoMaterno: 'Velasco',
  edad: 33,
  getNombre: function () {
    return 'Jaime Cervantes Velasco';
  },
  'nueva-propiedad-con-guion-medio': 'Mi propiedad con guión medio encerrada por comillas'
};
jaime['nueva-propiedad-con-guion-medio']; // 'Mi propiedad con guión medio encerrada por comillas'

Y obtenerlos con notación de corchetes así, jaime['nueva-propiedad-con-guion-medio'].

Una propiedad como esta, no puede ser accedida con una notación de puntos, así jaime.nueva-propoiedad-con-guion.medio. Esta sentencia te dará un error en Javascript porque no es nombre de propiedad válido.

De hecho en Javascript el nombre de variables o propiedades válidos deben:

  • Empezar con una letra, incluido $ y _.
  • No puede empezar con números o caracteres usados en el lenguaje para otros propósitos, ejemplo -, %, /, +, &.
  • Después del la letra de inicio puedes ocupar números, otras letras válidas, $ y _.

¿Objetos en Node.js y Javascript que contienen otros objetos?

Un objeto puede contener cualquier tipo de dato, así que puede contener otros objetos. Veamos un ejemplo:

const jaime = {
  nombre: 'Jaime',
  apellidoPaterno: 'Cervantes',
  apellidoMaterno: 'Velasco',
  edad: 33,
  getNombre: function () {
    return 'Jaime Cervantes Velasco';
  },
  direccion: {
    calle: 'Melchor Ocampo',
    numero: 2,
    colonia: 'Las Flores',
    municipio: 'Tezonapa',
    estado: 'Veracruz'
  }
};

Aquí agregamos un objeto direccion al objeto jaime. Simplemente usando las llaves para crear objetos literales.

¿Cómo agrego, modifico y elimino propiedades?

Una vez que creas un objeto, puedes agregar más propiedades, modificarlas y eliminarlas.

Agregar propiedades a los objetos en Node.js y Javascript

Se utiliza la asignación para agregar nuevas propiedades que no existen previamente.

const jaime = {
  nombre: 'Jaime',
};
jaime.apellidoPaterno = 'Cervantes';
jaime.appelidoMaterno = 'Velasco';
console.log(jaime ); // { nombre: 'Jaime', apellidoPaterno: 'Cervantes', apellidoMaterno: 'Velasco }

Actualizar propiedades a los objetos en Node.js y Javascript

Se utiliza también la asignación para actualizar el valor de las propiedades que ya existen.

const jaime = {
  nombre: 'Jaime',
  apellidoPaterno: 'Cervantes',
  apellidoMaterno: 'Velasco',
};
jaime.apellidoPaterno = 'Perez'
jaime.apellidoMaterno = 'Moreno';
console.log(jaime ); // { nombre: 'Jaime', apellidoPaterno: 'Perez', apellidoMaterno: 'Moreno' }

Eliminar propiedades a los objetos en Node.js y Javascript

Para eliminar propiedades se usa el operador delete. Ejemplo:

const jaime = {
  nombre: 'Jaime',
  apellidoPaterno: 'Cervantes',
  apellidoMaterno: 'Velasco',
};
delete jaime.apellidoPaterno;
// o tambien así:
delete jaime['apellidoMaterno];
console.log(jaime); // { nombre: 'Jaime' }

Las propiedades apellidoPaterno y apellidoMaterno, ya no existe en el objeto jaime.

¿Objetos en Node.js y Javascript son una referencia o un valor?

Los objetos en Node.js y JavaScript siempre serán una referencia, es decir:

const jaime = {
  nombre: 'Jaime',
  apellidoPaterno: 'Cervantes',
  apellidoMaterno: 'Velasco',
};
const pedro = jaime;
pedro.nombre = 'Pedro';
jaime.nombre === pedro.nombre; // true;
jaime.nombre; // 'Pedro'
pedro.nombre; // 'Pedro'

Las constantes jaime y pedro hacen referencia al mismo objeto en memoria.

¿Cómo recorro las propiedades de un objeto?

Existen varias formas, la más simple de recorrer las propiedades de un objeto es la siguiente.

Tenemos el objeto jaime:

const jaime = {
  nombre: 'Jaime',
  apellidoPaterno: 'Cervantes',
  apellidoMaterno: 'Velasco',
  edad: 33,
  getNombre: function () {
    return 'Jaime Cervantes Velasco';
  }
};

Lo primero es obtener el nombre de sus propiedades con el método Object.keys, el cual genera un Array con los nombres de las propiedades.

const nombresProps = Object.keys(jaime);
console.log(nombresProps); // ["nombre", "apellidoPaterno", "apellidoMaterno", "edad", "getNombre"]

Ahora recorremos el arreglo nombresProps para obtener los valores.

nombresProps.forEach(nombreProp => {
  console.log(jaime[nombreProp]); // imprime el valor del nombre de propiedad actual
});

El resultado del código anterior es algo como lo siguiente.

'Jaime'
'Cervantes'
'Velasco'
33
function () {
  return 'Jaime Cervantes Velasco';
}

¿El prototipo de un objeto?

Javascript es un lenguaje de programación multi-paradigma, en su lado orientado a objetos, no utiliza clases para la reutilización de código, más bien usa composición de objetos. Pero ¿Cómo hace esta composición de objetos?

Mejor composición de objetos sobre herencia de clases

Mejor composición de objetos sobre herencia de clases

Design Patterns: Elements of Reusable Object-Oriented Software

Este principio es bastante aplicado en Javascript y por consiguiente se aplica de igual manera en Node.js, de hecho así fue construido. Todos los objetos en Javascript tiene un enlace a un prototipo, y puede utilizar todas las propiedades y funciones de su prototipo.

Este enlace de prototipo puede ser referenciado en código, usando la propiedad especial __proto__.

En el caso de los objetos literales, como jaime, son enlazados a Object.prototype, que es el objeto base de todos los objetos en Javascript.

const jaime = {
  nombre: 'Jaime',
  apellidoPaterno: 'Cervantes',
  apellidoMaterno: 'Velasco',
  edad: 33,
  getNombre: function () {
    return 'Jaime Cervantes Velasco';
  }
};
jaime.__proto__; // { constructor: f Object(), hasOwnProperty: f hasOwnProperty, ... }
jaime.__proto__ === Object.prototype; // true
jaime.toString === Object.prototype.toString; // true
jaime.toString(); //[object Object]

Si expandimos los prototipos en un navegador web como chrome, se visualiza de esta manera.

Objeto literal tiene como prototipo a Object.prototype
Objeto literal jaime tiene como prototipo a Object.prototype

Gráficamente, el enlace de prototipos es como la imagen de abajo.

Object.prototype el prototipo de jaime
Object.prototype el prototipo de jaime

El prototipo también lo puedes ver en node.js si lo enlazas a un debugger. Pero no veremos como hacer eso hoy.

¿Qué es la cadena de prototipos?

Hemos creado el objeto jaime literalmente, pero que tal si lo creamos a través de otro objeto persona, haciendo el objeto persona su prototipo inmediato.

const persona = {
  saludar: function () {
    return 'Hola';
  },
  comer: function () {
  	return 'comiendo...';
  }
};
const jaime = Object.create(persona); // Creamos jaime en base al prototipo persona

Si hacemos ahora un console.log(jaime):

console.log(jaime);

Y expandimos el resultado en un navegador web como chrome, el resultado se visualiza de la siguiente forma.

Cadena de prototipo
Cadena de prototipo

En la imagen, podemos ver que [[prototype]] apunta al objeto persona, por lo que jaime tiene acceso a los métodos comer y saludar del objeto persona.

Y si hacemos estas comparaciones vemos que el enlace al prototipo persona existe.

jaime.__proto__; // { saludar: f (), comer: f () }
jaime.__proto__  === persona; // true

Podemos invocar los métodos comer y saludar del objeto persona como si fueran de jaime.

jaime.comer();  // 'Comiendo...'
jaime.saludar(); // 'Hola'

Ahora si expandimos el último [[prototype]] ¿Cuál crees que sea el resultado?

Cadena de prototipos Object.prototype
Cadena de prototipos Object.prototype

Vemos claramente que el último [[prototype]] es Object.prototype, el prototipo base de todos los objetos en Javascript. Y puedes comprobarlo en código comparando Object.prototype con las propiedades especiales __proto__.

jaime.__proto__.__proto__ === Object.prototype; // true

También podemos usar el método toString como si fuera de jaime.

jaime.toString(); // [object Object]

A esto se le llama cadena de prototipos, un objeto puede tener los sub prototipos necesarios para reutilizar sus propiedades y métodos. En este caso fue así, jaime → persona → Object.prototype.

jaime --> persona --> Object.prototype
jaime –> persona –> Object.prototype

Peculiaridades con la cadena de prototipos

Usa los métodos y propiedades de los sub prototipos

El objeto jaime puede utilizar directamente las propiedades y métodos de sus prototipos.

jaime.toString(); // '[object Object]' --> de Object.prototype
jaime.hasOwnProperty('nombre'); // true --> de Object.prototype
jaime.saludar(); // 'Hola' --> de persona
jaime.comer(); // 'Comiendo...' --> de persona
jaime.nombre; // 'Jaime' --> de si mismo, objeto jaime

Actualizar propiedades no afecta a la cadena de prototipos

Cuando tenemos una propiedad que existe en varios sub prototipos, javascript usa el que esté más cerca en la cadena de prototipos. Supongamos que queremos cambiar el comportamiento del método toString en jaime así que en lugar de usar Object.prototype.toString, a jaime le agregamos un método con el mismo nombre.

Se puede decir que lo que estamos haciendo es actualizarlo.

jaime.toString = function () {
  return `${this.nombre} ${this.edad}`;
};
jaime.toString(); // 'Jaime 33'

Ahora se usa el método toString de jaime, no se recorre la cadena de prototipos hasta llegar a Object.prototype.toString como se hizo en los anteriores ejemplos. Por que ya existe un metodo toString directamente en nuestro objeto jaime.

El operador delete no elimina las propiedades de los prototipos

Cuando ocupamos el operador delete, este nunca toca la cadena de prototipos, solo elimina las propiedades propias del objeto en cuestión.

Si en la cadena de prototipos existe una propiedad con el mismo nombre de la que eliminamos, entonces ahora esa propiedad en la cadena de prototipos será utilizada.

Usando el ejemplo anterior, del método toString de jaime, si lo eliminamos con delete, entonces ahora el toString() de Object.prototype es el que se usara porque el objeto persona no tiene ese método directamente.

jaime.toString = function () {
  return `${this.nombre} ${this.edad}`;
};
jaime.toString(); // 'Jaime 33'
delete jaime.toString
jaime.toString(); // '[[object Object]]' --> De Object.prototype

En la última línea notamos que ahora jaime vuelve a usar el toString() de Object.prototype.

Ejemplos de objetos predefinidos en Node.js y Javascript

Como ya hemos mencionado en otras publicaciones, a excepción de los tipos primitivos, todo lo demás en javascript son objetos. En futuras publicaciones veremos más detalle de los objetos de tipo function y Array

¿Cómo crear un objeto function?

function imprimirPersona(persona) {
  console.log(persona.nombre);
  console.log(persona.edad);
}

Como la función imprimirPersona es un objeto, podemos agregarle propiedades sin ningún problema.

imprimirPersona.miPropiedad = 'Mi propiedad de una funcion';
console.log(imprimierPersona.miPropiedad); // 'Mi propiedad de una funcion

Las funciones son muy útiles, tan versátiles que con ellas podemos generar nuevos objetos, pero ese tema no pertenece a esta publicación.

¿Cómo crear un objeto Array?

La forma recomendada es simplemente crearlos usando corchetes [].

const vacio = [];
const numeros = [1, 2, 3, 4, 5, 6];
const animales = ['perro', 'gato', 'caballo'];
const conObjetos = [
  		{
          nombre: 'Jaime',
          edad: 33
        },
        function imprimirPersona(persona) {
          console.log(persona);
        }
	];

Como un array es un objeto, igual que una función, también le puedes agregar propiedades.

const numeros = [1, 2, 3, 4, 5, 6];
numeros.miPropiedad = 'Mi propiedad de un arreglo';
console.log(numeros.miPropiedad); // 'Mi propiedad de un arreglo

El Array es el objeto más claro para usarlo como estructura de datos.

¿Cómo crear un objeto Date?

const fecha = new Date();
console.log(fecha); // Fri May 28 2021 10:46:27 GMT-0500 (hora de verano central)

Como te podrás imaginar, al objeto date también le podemos agregar propiedades porque es un objeto

Conclusión

En Javascript y en cualquier plataforma donde se ejecute, un ejemplo es en node.js, tienen la peculiaridad de siempre tener un enlace a un prototipo. Así es como el lenguaje está diseñado. Ya sé que actualmente existen las clases, pero en realidad estas son funciones que internamente hacen uso de prototipos, también lo explicaremos en otra publicación.

Con excepción de los datos primitivos, todo lo demás en Javascript son objetos, es importante saber como funcionan para no atorarnos en este aprendizaje.

Con esta información tienes lo suficiente para seguir avanzando y utilizar los objetos más sabiamente.

El tema de los objetos es bastante extenso, todavía quedan muchas cosas a tomar en cuenta, pero ya es demasiada información que tenemos que dividirla en más publicaciones. Así que muy pronto continuaremos. Cualquier duda, no dudes en escribirla en los comentarios, ¡Estaremos contentos de ayudarte!.

¿Cómo funciona React con ReactDOM y JSX?

¿Cómo funciona React con ReactDOM y JSX?

En esta publicación veremos como crear un componente, y comprender como se relaciona React con ReactDOM y JSX. Tener buenos fundamentos sobre el funcionamiento de React es muy importante para aprender más rápido esta librería. Además, al entender bien estos fundamentos, es fácil arreglar errores a la hora de crear nuestros componentes y aplicaciones.

Primero utilizaremos solo React para crear un elemento y ReactDOM para pintarlo, luego agregaremos JSX y finalmente veremos como podemos convertir ese elemento en un componente reutilizable.

Para los que no tienen idea que es React, es una librería escrita en JavaScript que facilita la creación de interfaces gráficas de usuario para aplicaciones web y móviles. Esta basada en componentes y composición en lugar de herencia.

Vamos a usar una herramienta en línea llamada condesandbox. Aquí crearemos un nuevo sandbox con la plantilla de HTML 5, tambien se le llama static.

Plantilla sandbox HTML5
Plantilla sandbox HTML5

Luego creamos un archivo api-dom.html y otro react.html, en el body de ambos archivos agregamos un div con el atributo id igual a "raiz-app", en este elemento contenedor vamos a insertar todos los elementos nuevos.

Todo lo referente a API DOM deber estar en el archivo api-dom.html y todo lo referente a React en react.html.

API DOM: Crear un elemento saludo simple

El objetivo es crear un elemento donde saludemos a nuestro usuario a través de un texto. Primero usaremos el API del DOM que se encuentra en todos los navegadores. Esto nos ayudará a comparar y visualizar los beneficios de usar React.

<body>
  <div id="raiz-app"></div>
  <script>
    const saludo = document.createElement('div');
    saludo.textContent = 'Hola, soy un elemento saludo creado con API DOM';
    const raiz = document.getElementById('raiz-app');
    raiz.appendChild(saludo);
  </script>
</body>

Creamos un elemento div y dentro agregamos el texto del saludo. Finalmente agregamos nuestro saludo a nuestro contenedor raiz-app con node.appenChild(node).

Si abres tu archivo api-dom.html usando el navegador web del lado derecho de la interfaz de condesandbox, el resultado debe ser algo así.

Resultado de document.createElement
Resultado de document.createElement

React: crear elemento saludo

Ahora vamos a crear el mismo elemento de la sección anterior con React.

Importar código de la librería

Para empezar a utilizar React es necesario descargar su código, existen varias formas de usar el código de React, por el momento simplemente descargamos la librería usando la etiqueta <script> dentro de la etiqueta <head> de nuestro archivo react.html. De la siguiente manera.

<head>
  <title>React.createElement</title>
  <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
</head>

Algo muy bueno de React es que separa las responsabilidades, lo que acabamos de agregar, descarga el código necesario para crear elementos y componentes. Para poder pintar esos elementos en nuestra página web, usamos ReactDOM. Pintar elementos en la página web desde luego que debe ser otra responsabilidad.

Para usar ReactDOM.render() necesitamos descargar su código usando de nuevo la etiqueta <script>, igual que hicimos con React.

<head>
  <title>React.createElement</title>
  <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
</head>

Crear saludo con React.createElement() y ReactDOM.render()

Ahora sí, creamos nuestro nuevo elemento usando React.createElement(component, props, ...children)

<body>
  <div id="raiz-app"></div>
  <script>
    const saludo = React.createElement(
      'div',
      null,
      'Hola, soy un elemento saludo creado con React');
	ReactDOM.render(saludo, document.getElementById('raiz-app'));
  </script>
</body>

La primera diferencia con document.createElement(), es que React.createElement() recibe como parámetros adicionales un objeto de propiedades y luego cualquier número de parámetros que se convertirán en elementos hijos.

De momento no le agregamos propiedades, pero si queremos agregar el saludo como un elemento de tipo texto, este elemento de tipo texto será un elemento hijo. Es lo que en API DOM seria node.textContent.

Finalmente pintamos nuestro saludo usando el método ReactDOM.render(). Por el momento digamos que ReactDOM.render() es como el node.appendChild().

Resultado de React.createElement
Resultado de React.createElement

Podemos agregar más elementos hijos simplemente agregando más parámetros, de la siguiente forma:

const saludo = React.createElement(
  'div',
  null,
  'Hola, soy un saludo creado con React',
  ' Y yo soy otro hijo',
  ' Y yo otro hijo más'
);
ReactDOM.render(saludo, document.getElementById('raiz-app'));

El parámetro children es un parámetro rest, el cual puede tomar cualquier número de parámetros e internamente hacer referencia a ellos a través de un array, React sabe como usarlos para pintarlos. En este ejemplo children contiene lo siguiente.

children = [
  'Hola, soy un saludo creado con React',
  ' Y yo soy otro hijo',
  ' Y yo otro hijo más'
];

API DOM: Modificamos el elemento saludo agregando propiedades y estilos

Nuestro elemento hasta ahora es demasiado simple, vamos a agregarle algunos estilos, y para eso vamos a utilizar dos propiedades, className y style.

const saludo = document.createElement('div');
saludo.textContent = 'Hola, soy un elemento saludo creado con API DOM';
saludo.className = "saludo";
saludo.style = 'font-weight:bold; text-transform:uppercase;';
const contenedor = document.getElementById('raiz-app');
contenedor.appendChild(saludo);

La propiedad className no se llama class para evitar conflictos, debido a que class es una palabra reservada en JavaScript usada para definir clases.

Ahora creamos la reglas de estilo para la clase de estilos saludo. Vamos a usar la etiqueta <style> dentro de <head>.

<head>
  <style>
    .saludo {
      background: orange;
      padding: 16px;
    }
  </style>
<head>

La propiedad style también aplica estilos, en este ejemplo pone el texto en negritas lo convierte a mayúsculas. El resultado debe ser algo como la imagen de abajo.

Resultado de document.createElement con estilos y propiedades
Resultado de document.createElement con estilos y propiedades

React: Modificamos el elemento saludo agregando propiedades y estilos

En props podemos agregar las propiedades que necesitemos, en este ejemplo solo agregamos className y style.

const saludo = React.createElement('div', {
    className: 'saludo',
    style: {
      'font-weight': 'bold',
      'text-transform': 'uppercase'
    },
  },
  'Hola, soy un elemento saludo creado con React'
);
const contenedor = document.getElementById('raiz-app'))
ReactDOM.render(saludo, container);

En la propiedad style, los estilos son agregados a través de un objeto en lugar de una cadena de caracteres, esto es más cómodo y legible, ¿Imagínate que fueran muchos estilos?, la supercadena larga que se formaría.

Tampoco no es que se recomiende esta forma de aplicar estilos a tus elementos en React, pero podemos aprovechar esta comodidad por el momento.

Otro beneficio en la propiedad style, es que en las declaraciones de estilos pueden ser escritas en camelCase, de hecho esa es la convención en Javascript y en React. Es más simple escribir en camelCase que estar encerrando todas las propiedades en comillas y con guiones medios entre palabras.

const saludo = React.createElement('div', {
    style: {
      fontWeight: 'bold',
      textTransform: 'uppercase'
    },
  },
  'Hola, soy un elemento saludo creado con React'
});

Si usas la forma, property-name, React te mostrara unos “warnings” en la consola. Estos “warnings” solo saldrán en modo develop, en producción no hay problema. Solo es una convención y no es obligatoria.

Warning: Unsupported style property font-weight. Did you mean fontWeight?
    at div
printWarning @ react-dom.development.js:61

Por último agregamos los estilos CSS que usara la clase saludo.

<head>
  <style>
    .saludo {
      border: 1px solid #eee;
      padding: 10px;
    }
  </style>
<head>

Al final debes tener un resultado como el de la imagen de abajo.

Resultado de React.createElement y definiendo propiedades
Resultado de React.createElement y definiendo propiedades

React: children en props

El contenido de nuestro saludo lo podemos agregar en el objeto props, en la propiedad children, en lugar del último parámetro de la función. children, en este caso funciona como la propiedad textContent del ejemplo usando API DOM.

const saludo = React.createElement('div', {
  className: 'saludo',
  style: {
    fontWeight: 'bold',
    textTransform': 'uppercase'
  },
  children: 'Hola, soy un elemento saludo creado con React'
});
ReactDOM.render(saludo, document.getElementById('raiz-app'));

En un ejemplo anterior comentamos que el tercer parámetro children es un parámetro rest, el cual recibe cualquier número de parámetros los almacena en un Array. Pues la propiedad children también puede recibir un Array de hijos que React puede pintar.

const saludo = React.createElement('div', {
  className: 'saludo',
  style: {
     fontWeight: 'bold',
     textTransform: 'uppercase'
  },
  chidren: [
    'Hola, soy un saludo creado con React',
    ' Y yo soy otro hijo',
    ' Y yo otro hijo más'
  ]
);
const contenedor = document.getElementById('raiz-app');
ReactDOM.render(saludo, contenedor);

A lo mejor te parezca un poco complicado, pero es mucho más sencillo que el ejemplo del API DOM, imagínate que en lugar de texto fueran otros elementos como <h1>, <p>, otros <div>. Tendrías que crear elementos, agregarles el contenido y luego insertarlos dentro del elemento saludo.

Aun con este ejemplo, aunque es más sencillo que usar el API del DOM, se empieza a complicar cuando un elemento tiene muchos elemento hijos, por eso en React se recomienda usar JSX. Lo veremos a continuación.

React con ReactDOM y JSX

Ok, ya vimos como crear un elemento en React, pero ahora vamos a simplificar su implementación, JSX internamente utiliza React.createElement(component, props, ...children), pero estar llamando una función para crear un elemento complica la legibilidad del código.

Que tal si en lugar de escribir la función para crear un elemento, simplemente lo creas como cualquier otro codigo HTML, es mucho mas facil de escribir y leer, veamos la creación del elemento saludo con React y JSX.

const saludo = <div>Hola, soy un elemento saludo creado con React y JSX</div>;
const contenedor = document.getElementById('raiz-app');
ReactDOM.render(saludo, contenedor);

Mucha más fácil, ¿Cierto?, si te das cuenta no encerramos entre comillas el contenido del elemento saludo, JSX es lo suficientemente inteligente para entenderlo, pero para ellos es necesario descargar la herramienta llamada babel.js que lo hace posible.

Babel es una herramienta que no solo se encarga de convertir codigo JSX a invocaciones de React.createElement, sino que también convierte código con las últimas características del estándar ECMAScript a código que los navegadores web puedan entender. Para más información visita su sitio oficial.

Es importante aclarar que JSX internamente sigue siendo código JS, aunque parezca HTML y el uso de sintaxis HTML sea casi idéntica.

Usar babel para convertir JSX a JS

Todo muy bonito, pero para que podamos usar código JSX tenemos que descargar la herramienta.

<head>
 <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>

Al principio nuestro código lo insertamos dentro de las etiquetas <script>, pero para que babel pueda identificar que debe convertir codigo JSX a JS, es necesario decirle que es de tipo text/babel con el atributo type.

<script type="text/babel">
  const saludo = <div>Hola, soy un elemento saludo creado con React y JSX</div>;
  const contenedor = document.getElementById('raiz-app');
  ReactDOM.render(saludo, contenedor)
</script>

Ahora vamos agregar las propieades className y style para que tenga los mismos estilos que los ejemplos anteriores.

<script type="text/babel">
  const saludo = (
    <div
	  className="saludo"
	  style={{fontWeight: 'bold', textTransform: 'uppercase'}}
	>
      Hola, soy un elemento saludo creado con React y JSX
    </div>
  );
  const contenedor = document.getElementById('raiz-app');
  ReactDOM.render(saludo, contenedor)
</script>

Cuando se tiene más de una línea de JSX, es recomendado encerrarlo entre paréntesis para mejorar el entendimiento y evitar errores de JavaScript debido a la inserción automática de punto y coma.

Expresiones JS en atributos JSX

Algo interesante es como definimos el valor del atributo styles, usamos un par de llaves {}, todo lo que está encerrado en un par de llaves se ejecuta, por qué JSX lo identifica como una expresión de Javascript. En este caso JSX identifica que estamos definiendo un objeto para ser asignado a la propiedad style.

const saludo = (
  <div
  className="saludo"
  style={{fontWeight: 'bold', textTransform: 'uppercase'}}
  >
    Hola, soy un elemento saludo creado con React y JSX
  </div>
);

También podemos definir el objeto en otra línea y luego usar esa variable para realizar la asignación.

const styles = {fontWeight: 'bold', textTransform: 'uppercase'};
const saludo = (
  <div
  className="saludo"
  style={styles}
  >
    Hola, soy un elemento saludo creado con React y JSX
  </div>
);

El resultado de utilizar React con ReactDOM y JSX debe ser el mismo que en las últimas secciones, algo como la imagen de abajo.

Resultado React con JSX
Resultado React con ReactDOM y JSX

Componente reutilizable de React con ReactDOM y JSX

Ya que comprendemos la relación que tiene React con ReactDOM y JSX, es hora de que nuestro elemento saludo se convierta en un elemento reutilizable para saludar.

En Javascript el concepto de reutilización son las funciones, en React serian los componentes. Algo muy bueno de React, es que podemos hacer un componente usando una función. ¿Qué cosa puede ser más transparente que eso?

function Saludo(props) {
  return (
    <div
      className='Saludo'
      style={props.style}
    >
      {props.children}
    </div>
  );
}

Analizando un poco el código, vemos como la función Saludo, recibe un objeto de propiedades para ser usadas en el componente, recordemos que el contenido de este ejemplo se convierten en invocaciones a React.createElement(component, props, ...children).

En un ejemplo anterior vimos como asignar valores a las propiedades encerrando expresiones con llaves {}, no solo podemos hacer eso con atributos, sino también para pintar los elementos hijos, ejecutando {props.children}.

El código anterior JSX, se convierte en código JS, así:

function Saludo(props) {
  return React.createElement("div", {
    className: "Saludo",
    style: props.style
  }, props.children);
}

Ahora que tenemos un componente función, podemos usarlo de las siguientes formas.

const styles = { fontWeight: 'bold', textTransform: 'uppercase' };
const contenido = (
  <div>
   { Saludo({ children: 'Instancia de Saludo' }) }
   {
     Saludo({
       children: 'Instancia de Saludo en negritas',
       style: { fontWeight: 'bold' }
     })
   }
   <Saludo style={styles}>Instancia de Saludo con etiqueta</Saludo>
  </div>
);
const contenedor = document.getElementById('raiz-app');
ReactDOM.render(contenido, contenedor);

Lo mismo, todo este código se convierte a algo como lo siguiente.

function Saludo(props) {
  return React.createElement("div",
    {
      className: "Saludo",
      style: props.style
    },
    props.children
 );
}
const styles = { fontWeight: 'bold', textTransform: 'uppercase' };
const contenido = React.createElement(
  "div",
  null,
  Saludo({ children: 'Instancia de Saludo' }),
  saludo({
    children: 'Instancia de Saludo en negritas',
    style: {
      fontWeight: 'bold'
    }
  }),
  React.createElement(Saludo, { style: styles }, "Instancia de Saludo con etiqueta")
);
const contenedor = document.getElementById('raiz-app');
ReactDOM.render(contenido, contenedor);

Y el resultado visual es esto.

Resultado de usar un componente reutilizable con React
Resultado de usar un componente reutilizable con React

Date cuenta de que las invocaciones a la función Saludo, así como el uso de la etiqueta Saludo, se convierten en invocaciones a React.createElement(component, props, ...children). Y como el uso del JSX con la etiqueta <Saludo> facilita la lectura y creación del componente. Veamos un ejemplo solo usando etiquetas.

const styles = { fontWeight: 'bold', textTransform: 'uppercase' };
const contenido = (
  <div>
   <Saludo>Instancia de Saludo</Saludo>
   <Saludo style={{ fontWeight: 'bold' }}>Instancia de Saludo en negritas</Saludo>
   <Saludo style={styles}>Instancia de Saludo con etiqueta</Saludo>
  </div>
);
const contenedor = document.getElementById('raiz-app');
ReactDOM.render(contenido, contenedor);

¿Qué tal? Mucho más simple ¿Verdad?

Una última cosa, cuando uses etiquetas de los componentes de React, siempre deben empezar con la primera letra Mayúscula, así es como reconoce JSX que debe invocar a una función con ese nombre. De lo contrario se confundiría con las etiquetas normales de HTML y te lanzará un error como el siguiente.

Warning: The tag <saludo> is unrecognized in this browser. If you want to render a React component, start its name with an uppercase letter

Referencias

https://reactjs.org/

https://babeljs.io/

Instalar multiples versiones de node.js con NVM en macOS, Linux y Windows

Instalar multiples versiones de node.js con NVM en macOS, Linux y Windows

Introducción

Si no sabes que es node y nvm, aquí te explicaré que son, para qué sirven y además como instalar node.js con NVM para tener múltiples versiones y ser más productivo a la hora de crear tus paquetes.

¿Qué es node.js?

Tomando la definición del sitio oficial, Node.js®, node es un entorno de ejecución para JavaScript construido con el motor de JavaScript V8 de Chrome. Node.js usa un modelo de operaciones E/S sin bloqueo y orientado a eventos, que lo hace liviano y eficiente. El ecosistema de paquetes de Node.js, npm, es el ecosistema más grande de librerías de código abierto en el mundo.

Vamos a describir la parte que nos interesa, Node.js es un programa, V8 es un motor de javascript de código abierto creado por Google, por lo que también lo hace un programa, V8 está escrito en C++, y la tarea de V8 es tomar código Javascript y convertirlo a código máquina (compilar código), pero lo que lo hace especial para nuestros fines es que puede ser embebido dentro de otros programas, lo que permite que V8 esté embebido en Node.js, V8 por así decirlo es el punto de partida para toda la funcionalidad de Node.js.

Node.js también está escrito en C++ y utiliza la API de V8 para agregarle características y funcionalidades nuevas a Javascript. Estas nuevas funcionalidades permiten tener acceso al sistema de archivos y carpetas, nos permite crear un servidor TCP y http, además de acceso a POSIX, o sea, a toda la funcionalidad del sistema operativo donde se encuentre instalado.

Node.js proporciona la sintaxis Javascript para crear programas que tengan acceso a las características del sistema operativo donde sé está ejecutando.

Cuando instalas node.js viene por default con una herramienta llamada NPM, que es el administrador de paquetes de node, con esta herramienta tú puedes instalar cualquier paquete de JavaScript que necesites.

¿Cómo lo hace? Pues cualquier desarrollador de javascript que siga los lineamientos adecuados puede publicar su librería/paquete, esta publicación genera un registro que internamente utiliza la línea de comandos de NPM para descargar e instalar ese nuevo paquete, además de que actualmente muchísimos paquetes ya registrados que puedes descargar y usar en tus proyectos.

¿Qué es NVM?

Para instalar node.js con NVM en macOS, primero debes tener git instalado para poder correr el comando de instalación de NVM. Ahora bien, ¿Qué es NVM? NVM significa Node Version Manager, y es al administrador de versiones de node, te permite tener múltiples versiones de node en tu computadora, dándote la flexibilidad de cambiar entre versiones y tener tu propio ambiente de trabajo en cada versión.

Puedes tener tú propios paquetes instalados, específicos para diferentes proyectos. Lo que tienes instalado en una versión, no choca con lo que tienes en otra.

Todo esto te da la habilidad de probar diferentes tipos de versiones de tus paquetes. Si estás creando un nuevo paquete y lo quieres probar en las últimas versiones de node.js, lo puedes hacer sin temor a romper lo que ya tienes para otra versión más estable o incluso para versiones más viejitas.

¿Cómo instalamos node.js con NVM en macOS y Linux?

Suponemos que ya tienes instalado git, si no es así, puedes ver como instalar git en macOS aqui.

Ejecuta el siguiente comando en una terminal:

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash
Instalar nvm en mac o linux
Instalar nvm en mac o linux

Este comando te clonara la última versión estable de NVM en tu directorio ~/.nvm, luego intentara escribir en tu archivo ~/.zshrc o ~/.bashrc el código de abajo. Esto para que NVM se cargue y funcione correctamente al iniciar sesión en tu computadora.

export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  # This loads nvm bash_completion

Ahora para ejecutar el código anterior, y nos funcioné desde este momento nvm, ponemos en la consola el siguiente comando. Esto te permite no cerrar y volver abrir tu shell.

source ~/.zshrc

Revisa si tu instalación de NVM quedo correcta, ejecutando:

nvm --version

Si te imprime la versión, es que todo quedo correctamente instalado.

Ahora instala la última versión estable de node.js con el comando nvm install version

nvm install node # Aqui "node" es una alias para instalar la ultima version

Y verifica la versión de node instalada con:

node --version

¿Como usar otras versiones de node.js con NVM en macOS y Linux?

Para listar todas las versiones de node.js, puedes ejecutar el siguiente comando:

nvm ls-remote

La terminal te mostrará algo así:

nvm ls-remote
nvm ls-remote

Si quieres instalar otra versión, vuelves a ejecutar el comando nvm install version, por ejemplo, vamos a instalar la versión 14:

nvm install 14

Este comando te instalará la versión 14 de node, pero para poder usarlo, debemos ejecutar el siguiente comando:

nvm use 14

Ahora que se instaló la version 14 de node, siempre que se instala una nueva versión, nvm toma como default esa versión recientemente instalada de node en la shell actual, donde se ejecutó el comando de instalación.

Para poder verificar la versión actual, puedes ejecutar el comando `nvm current`, si quieres ver todas las versiones instaladas ejecuta el comando `nvm ls`, el cual lista las versiones instaladas de node.js

nvm ls
nvm ls

¿Cómo instalar node.js con NVM en windows?

Para instalar node.js en windows existen dos opciones similares a NVM, no son lo mismo, pero cumplen con el objetivo de tener varias versiones de node.js para facilitar tu desarrollo de software.

  • NVS
  • NVM windows

Para usar estos comandos se recomienda hacerlo desde tu power shell de windows con derechos de administrador. También desde una terminal muy buena, y que la puedes descargar del siguiente enlace:

https://cmder.net/

Instalar estas dos herramientas en windows, es mucho más fácil porque tiene sus propios instaladores automáticos.

nvs windows

Primero hay que descargar el instalador del enlace de abajito. NVS de hecho fue creado con inspiracion de NVM y tambien se puede usar en Mac y Linux.

https://github.com/jasongin/nvs/releases

Instalador de NVS
Instalador de NVS

Ahora, la operación confiable de “siguiente”, “siguiente”, “siguiente”, “finalizar”. Una vez instalado, en tu terminal de preferencia ejecuta el comando nvs add [version].

nvs add 16

Y para usar una version especifica de node, simplemente en una terminal, ejecuta el comandp nvs use [version].

nvs use 16

nvm-windows

Para instalar nvm-windows es también muy fácil, solo descárgate el instalador de aquí.

https://github.com/coreybutler/nvm-windows/releases

Luego, ya sabes, la operación confiable de oprimir el botón next hasta que la instalación esté completa.

Los comandos que puedes utilizar son:

Para instalar una versión de node ocupamos el comando:

nvm install v16

Si tienes más de una versión de node, puedes elegir cuál usar, al igual que en linux y MacOS.`

nvm use v16

Listo ya sabes como instalar node.js en tu máquina, ahora sí, tienes todo lo necesario para empezar a desarrollar en javascript.

Tipos de datos en Javascript, lo que no te enseñaron

Tipos de datos en Javascript, lo que no te enseñaron

Introducción

Los tipos de datos en Javascript son un poco diferentes en comparación con otros lenguajes de programación como C o JAVA.

JavaScript es un lenguaje débilmente tipeado, esto quiere decir que no es necesario definir el tipo de dato. Pero tampoco es que no tenga tipos, pues el tipo de dato es definido en tiempo de ejecución por Javascript.

Este comportamiento y especificación de los tipos de datos aplica para cualquier lugar donde se ejecute javascript, ya sea en el navegador, en node.js, mongodb, o cualquier herramienta que use Javascript.

Ahora si analizamos un poco el concepto de tipos, cuando realizas una suma entre números en el mundo real no te importa que tipo es, solo te importa que se puedan sumar, sin importar si es un entero, o decimal. Estos subtipos de números siguen siendo parte del conjunto de números reales, y son utilizados en la vida real.

Javascript nos permite aplicar este pensamiento y con eso nos ahorramos muchas comparaciones de tipos. Habrá ocasiones en que no quede de otra que convertirlos o compararlos, pero entre menos lo hagamos, más sencillo será nuestro código, y por consiguiente más fácil de entender y más rápido de ejecutar.

Se dice que Javascript tienen un sistema de tipos de datos dinámico, esto es porque las variables que se crean pueden recibir cualquier otro tipo de dato en cualquier momento, e ir cambiando de tipo según el valor que guarden. Lo que realmente nos interesa es el valor y que podemos hacer con ese valor, no tanto el tipo.

Más adelante veremos algunas conversiones y comparaciones que pueden confundir y complicar el código, la recomendación es disminuir su uso en la medida de lo posible. Aunque no veremos muy a detalle cada tipo, sí que veremos cosas más importantes, necesarias para usar los tipos de datos en Javascript.

Es útil que se sepa definir variables y constantes, si no sabes como crear una variable o una constante, puedes revisar esta información antes de continuar.

Tipos de datos primitivos

  • Es aquel que no es un objeto
  • No tiene métodos.
  • Son inmutables
  • Tenemos siete tipos datos primitivos en Javascript.
  1. Number, números como; 1, 0, 18500, 89.95124
  2. BigInt, agregado en el 2020, para representar números enteros muy, pero muy grandes, 99999999999999n
  3. String, cadena de caracteres como 'Hola' y "Buenas noches".
  4. Boolean, solo acepta true o false, es decir, si o no.
  5. null, sirve para indicar que algo es nada, su único valor es null.
  6. undefined, sirva para indicar que algo aún no está definido.
  7. Symbol, agregado en el 2015, con EcmaScript 6

Tipos de datos Objetos

Todo lo demás tipos de datos en Javascript son objetos. En la sección Objetos está la explicación.

Number

Si hubiera escrito esto antes del 2020, te diría que solo hay un tipo de dato numérico, lamentablemente (con algunas excepciones) se agregó un nuevo valor numérico llamado BigInt. En esta sección fingiremos que no existe.

Number es un valor flotante de 64 bits, es decir, es un número double en otros lenguajes de programación, básicamente este es el único tipo de dato numérico en javascript, no existen enteros (a excepción de BigInt), decimales, flotantes o doubles. Su especificación está proporcionada por IEEE-754.

La forma de definirlos y usarlos es muy sencilla, simplemente escribes el número, no importa si es entero o decimal, punto flotante o double, como mencionamos antes, todos estos serán de tipo double para javascript. Esta es la forma recomendada de definirlos.

const edad = 33;
const PI = 3.1416;
const descuento = 0.30;
const pesoEnDolares = 0.05; // que tristeza xD!!
edad + descuento; // 33.30, no nos interesa el tipo, solo la suma

Como vemos en la última línea, podemos sumar enteros y decimales sin ningún problema, no nos interesa el tipo, nos interesa la habilidad de sumarlos.

Se sabe que los valores flotantes tienen cierto error de precisión al realizar operaciones aritméticas, la operación más obvia es la división.

0.2 + 0.1; // es igual a 0.30000000000000004

Como vemos no nos da un valor exacto de 0.3, para mitigar esto puedes multiplicar los valores por 100 y luego dividir el resultado entre 100.

(0.2 * 100 + 0.1 * 100)/100; // es igual a 0.3

NaN

Existe un número especial llamado NaN. Este valor es el error de una operación con números.

25 * undefined; // es igual a NaN
25 * {}; // es igual a NaN

Todo operación que tenga NaN como uno de sus operandos, dará como resultado NaN.

25 * NaN; // es igua a NaN
25 + NaN; // es igual a NaN

NaN ni siquiera es igual a NaN

NaN === NaN; // es igual a false
NaN == NaN; // es igual a false

Para mitigar este extraño comportamiento, se recomienda usar el método Number.isNaN(), también existe una función global llamada isNaN(), pero debido a la funcionalidad implícita de Javascript en querer convertir entre tipos automáticamente, nos puede dar resultados muy extraños. Así que como recomendación siempre usa Number.isNaN(). Esta última función revisa si el tipo es Number e igual a NaN, cualquier otro valor nos regresara false.

// Usando funcion global
isNaN('jaime'); // true --> Esto esta mal, deberia seguir regresando false
// lo que realmente hace es una conversion automatica
const result = Number('jaime'); // NaN
isNaN(result); // true
Number.isNaN('343434'); // false
Number.isNaN('jaime'); //false
Number.isNaN(1 + undefined); // true

Podemos ver en el primer ejemplo que la función global regresa true cuando debería ser false, esto es por la conversión automática de tipos que hace javascript.

Función Number(valor)

Puedes convertir un valor a número usando la función Number(valor), solo ten cuidado porque esta función puede regresar 0 para muchos valores, así como también NaN.

Number('5'); // 5
Number(''); // 0
Number(undefined); // NaN
Number(null); // 0
Number('8778jaime'); // NaN
Number(false); // 0
Number(true); // 1;
Number({}); // NaN
Number([]); // 0

Number.parseInt y Number.parseFloat

Por último tenemos las funciones Number.parseInt(valor) y Number.parseFloat(valor), si el valor no es un string, entonces lo convierten a string utilizando el método .toString() antes de convertirlo a Number.

Number.parseInt(''); // NaN
Number.parseInt('78.5jaime'); // 78
Number.parseInt(78.58977); // 78
Number.parseFloat(''); // NaN
Number.parseFloat('78.5jaime'); // 78.5

BigInt

Este tipo de valor es nuevo, agregado al lenguaje en el 2020, se utiliza para representar valores enteros muy grandes, especialmente para cálculos matemáticos con cantidades muy largas.

Para usar este tipo de dato al número se le agrega la letra n al final. Esta es la forma recomendada.

const enteroMuyGrante = 999999999999999999999999999999999999999999999999999n;
const otroEnteroMuyGrande = 55555555555555555555555555555555555555555555555n;
const result = enteroMuyGrande * otroEnteroMuyGrande; //

Se pueden realizar operaciones entre el tipo Number y el BigInt, pero debido a que la conversión entre tipos puede provocar perdida de precisión, se recomienda solo usar valores BigInt cuando se estén ocupando valores mayores a 253 y solo realizar operaciones con valores del mismo tipo BigInt.

Otra mala noticia sobre BigInt es que no puedes utilizar sobre un valor BigInt los métodos del objeto Math, por ejemplo no puedes utilizat Math.sqrt().

Math.sqrt(99); // 9.9498743710662
Math.sqrt(99999999999999999n); // Uncaught TypeError: Cannot convert a BigInt value to a number

Como recomendación final, no te compliques la vida, no uses BigInt a menos que el problema que quieras solucionar necesite forzosamente números enteros muy grandes.

Función BigInt(valor)

Puedes convertir y crear un BigInt con la función BigInt(valor)

BigInt('9999999999'); // 9999999999n

Boolean

Boolean es un tipo de dato lógico, el cual es representado únicamente por dos valores true y false. Muy útil para tomar decisiones en nuestros programas. Nombrado así por el matemático George Boole.

Se pueden definir valores tipo Booleanos de la siguiente manera. Esta es la forma recomendada

const tieneHijos = true;
const esEstudiante = false;

Se usan mucho en operaciones lógicas, por ejemplo:

if (tieneHijos) {
  // Si "si" tiene hijos, preguntar cuantos hijos tiene
} else {
  // si "no"
}
if (esEstudiante) {
  // Si "si" es estuduante, aplicar descuento
} else {
  // si "no"
}

Función Boolean(valor)

Los Booleanos también tiene una función Boolean(valor), es adecuado para crear booleanos y convertir explicitamente un valor a Booleano. Más detalles abajo en la sección Valores que pueden generar Booleanos.

null

Este tipo de dato en Javascript es muy fácil, es un dato que significa que no es nada en comparación con los demás tipos de datos. En otros lenguajes null hace referencia a un apuntador o una referencia de memoria que está vacía, pero en javascript es diferente, simplemente es un valor que significa “nada”.

const nada = null;

undefined

Este tipo de dato representa algo que aún no está definido. Es el valor automático de las variables, parámetros y propiedades de objetos que no se les ha definido un valor.

let nombre; // undefined
let edad; // undefined
nombre === undefined; // true
edad === undefined; // true

Symbol

Este tipo de dato se utiliza principalmente para garantizar que su valor sea único e inmutable. Se puede usar como propiedades de objetos para reducir accesos a esas propiedades y con eso evitar modificaciones indeseadas. Cuando veamos más a detalle a los objetos veremos en la práctica su uso.

Para crear este tipo de dato primitivo se usa la función Symbol(). Es la única forma de crear un symbol y es la recomendada.

const nombre = Symbol('Jaime');
const nombreIgual = Symbol('Jaime');
nombre === nombreIgual; // false

String o cadena de caracteres

Los string nos permiten guardar información en forma de texto. Son una secuencia de uno o más caracteres de 16 bits, UTF-16. No existe el tipo `char` como en otros lenguajes, si deseas un carácter, pues simplemente creas un string con un solo carácter. ¿Sencillo no?

Tenemos tres formas para crear un string.

En realidad se puede también crear strings con la función String(valor) o en forma de constructor new String(valor), pero no nos compliquemos la existencia y ocupes solo las tres de abajo por simplicidad:

  1. Con comillas simples, ''. Recomendada si no necesitas evaluar expresiones dentro.
  2. Comillas dobles, ""
  3. backticks, `` . Recomendada si necesitas evaluar expresiones dentro.
const nombre = 'Jaime';
const apellidoPaterno = "Cervantes";
const apellidoMaterno = `Velasco`;

La tercera forma de definir Strings, nos permite usar expresiones dentro de la cadena de caracteres, por lo que es muy útil. Por ejemplo:

const nombre = 'Jaime';
const apellidoPaterno = "Cervantes";
const apellidoMaterno = `Velasco`;
const nombreCompleto = `${nombre} ${apellidoPaterno} ${apellidoMaterno}`; // Jaime Cervantes Velasco

Los strings son inmutables, cuando modificas o extiendes un string realmente se genera uno nuevo.

Dos cadenas de caracteres pueden ser iguales

Puedes comprobar que dos cadenas de caracteres son iguales con ===.

"jaime" === "jaime"; // true
'jaime' === "jaime"; // true
`jaime` === 'jaime'; // true

Función String(valor)

Para convertir un valor a cadena de caracteres puedes usar la función String(valor).

String(1); // '1'
String(null); // 'null'
String(undefined); // 'undefined'

String.length

La propiedad length de un string se usa para saber la longitud de una cadena de caracteres, verás con el tiempo que esto es útil cuando trabajas con cadenas de caracteres.

const nombre = 'Jaime Cervantes Velasco';
nombre.length // 23

Objetos

Todo lo demás tipos de datos son objetos, una función es un objeto. Un objeto es una colección de pares nombre: valor, parecido a los “arrays asociativos” de PHP. Estos pares de nombre/valor se les llama propiedades, una propiedad es como una variable y puede contener cualquier tipo de valor.

Algunos objetos en Javascript:

  • Object
  • Function
  • Array
  • Date
  • RegExp
  • Error

Objetos envolventes de primitivos

Incluso los tipos de datos primitivos String, Boolean, Number, BigInt y Symbol tiene su correspondiente representación en Objeto, llamados objetos envolventes. De hecho Javascript implícitamente convierte estos datos primitivos a objetos para poder ocupar métodos y propiedades útiles. Por ejemplo la propiedad length de una cadena de caracteres.

¿Cómo pensabas obtener la longitud de una cadena de caracteres? Entendiendo que una cadena de caracteres es un dato primitivo y no tiene métodos

'Jaime Cervantes Velasco'.length; // 23, el pri
// A groso modo, lo que sucede implictamente al querer obtener lenght:
const nuevoString = new String('Jaime Cervantes Velasco');
nuevoString.length; // 23
(5).toFixed(2); // 5.00
// Igual a groso modo pasa algo asi:
const numero = new Number(5);
numero.toFixed(2); // 5.00
false.toString(); // 'false'
// Igual a groso modo pasa algo asi:
const booleano = new Boolean(false);
numero.toString(2); // false
// Estos dos tipos de datos primitivos no permiten el uso de new
(9n).toLocaleString(); // '9'
Symbol('Hi').toString(); // 'Symbol(Hi)'

Aunque los tipos de datos BigInt y Symbol no permiten el uso de new no quiere decir que por detrás no se creen objetos, está claro que javascript los envuelve en un objeto porque nos permite utilizar los métodos del ejemplo, toLocaleString y toString.

¿Cómo crear un objeto?

Para crear un objeto no necesitas crear una clase, tú simplemente creas el objeto y lo empiezas a utilizar. La forma más fácil de crear un objeto es usando la definición que llaman objeto literal, donde simplemente abres y cierras llaves {}. Esta es la forma recomendada.

const vacio = {};
const persona = {
  nombre: 'Jaime',
  apellidos: 'Cervantes Velasco',
  edad: 33,
  getNombre: function () {
    return `${this.nombre} ${this.apellidos}`;
  },
  hablar: function () {
    return `Hola, soy ${this.getNombre()}, tengo ${this.edad} años.`;
  }
};
const frase = persona.hablar();
console.log(frase); // 'Hola soy Jaime Cervantes Velasco, tengo 33 años.'

En el ejemplo tenemos un objeto persona, tiene varias propiedades de diferentes tipos, su nombre es un String, su edad es un NumbergetNombre y hablar son de tipo Function, las funciones que son miembros de un objeto se les llama métodos.

Más abajo hay ejemplos de objetos.

Accediendo a propiedades con corchetes, ['key']

En el ejemplo anterior vimos como acceder al método hablar(), este método sigue siendo una propiedad y es de tipo function. Pero existe otra notación para acceder a las propiedades, usando corchetes. Muy parecido a como se acceden a los elementos de un array.

persona.edad; // regresa 33
persona['edad']; // regresa 33
// obtenemos la funciónn con corcheteds y la invocamos con parentesis
const frase = persona['hablar']();
console.log(frase); // 'Hola soy Jaime Cervantes Velasco, tengo 29 años.'

¿Cómo crear un objeto function?

function imprimirPersona(persona) {
  console.log(persona.nombre);
  console.log(persona.edad);
}

Como la función imprimirPersona es un objeto, podemos agregarle propiedades sin ningún problema.

imprimirPersona.miPropiedad = 'Mi propiedad de una funcion';
console.log(imprimierPersona.miPropiedad); // 'Mi propiedad de una funcion

¿Cómo crear un objeto Array?

La forma recomendada es simplemente crearlos usando corchetes [].

const vacio = [];
const numeros = [1, 2, 3, 4, 5, 6];
const animales = ['perro', 'gato', 'caballo'];
const conObjetos = [
  		{
          nombre: 'Jaime',
          edad: 33
        },
        function imprimierPersona(persona) {
          console.log(persona);
        }
	];

Como un array es un objeto, igual que una función, también le puedes agregar propiedades.

const numeros = [1, 2, 3, 4, 5, 6];
numeros.miPropiedad = 'Mi propiedad de un arreglo';
console.log(numeros.miPropiedad); // 'Mi propiedad de un arreglo

¿Cómo crear un objeto Date?

const fecha = new Date();
console.log(fecha); // Fri May 28 2021 10:46:27 GMT-0500 (hora de verano central)

Como te podrás imaginar, al objeto date también le podemos agregar propiedades porque es un objeto

De momento espero que esto te dé una idea de la importancia de los objetos en Javascript y por eso se merece una explicación más extensa en una futura publicación.

Valores que pueden generar un boleano false

Existen valores verdaderos y valores falsos, que aplicados en una condición se comportan como Booleanos. Esto es debido a que Javascript hace una conversión de tipos implícita, que nos puede sorprender si no tenemos cuidado, por eso aquí te dejo la lista de valores que pueden regresar un Boleano false.

  • false
  • null
  • undefined
  • 0
  • 0n
  • NaN
  • ”, “”, “ (cadena de caracteres vacía)

Todos los demás valores, incluyendo los objetos, en una condición o pasados a la función Boolean(valor) regresan el Boleano true. Un string con un espacio " ", regresa true, y un string con valor "false" también regresa true.

Función Boolean(valor)

Existe una función global para crear valores Booleanos, Boolean(valor). Donde si valor es uno verdadero, regresa true, de lo contrario, regresa false.

Boolean(false); // false
Boolean(null); // false
Boolean(undefined); // false
Boolean(0); // false
Boolean(0n); // false
Boolean(NaN); // false
Boolean(''); // false
// Todos los demas valores van a regresar true
Boolean(' '); // true
Boolean('false'); // true
Boolean('jaime cervantes'); // true
Boolean({}); // true
Boolean([]); // true;

En condiciones lógicas

Las condiciones lógicas en javascript verifican sin una expresión regresa true o false, ejecutan un conversión implicita.

if (false) {
} else {
  console.log(false);
}
if (null) {
} else {
  console.log(false);
}
if (undefined) {
} else {
  console.log(false);
}
if (0) {
} else {
  console.log(false);
}
if (0n) {
} else {
  console.log(false);
}
if (NaN) {
} else {
  console.log(false);
}
if ('') {
} else {
  console.log(false);
}
if (' ') {
  console.log(true);
}
if ('false') {
  console.log(true);
}
if ({}) {
  console.log(true);
}
if ([]) {
  console.log(true);
}

Verificar tipos con el operador typeof

Este operador nos permite identificar el tipo de dato con el que estamos trabajando, útil en caso de que no sepamos de donde viene la información, o queremos tratar los datos con base en su tipo. El operador regresa el nombre en minúsculas del tipo de dato.

typeof 5; // 'number
typeof NaN; // 'number'
typeof 99999999999999999999999999999999999999999999999n; // 'bigint'
typeof true; // 'boolean'
typeof null; // 'object', esto es un error que existe desde la primera versión de JS
typeof undefined; // 'undefined'
typeof Symbol('simbolo'); // 'symbol'
typeof 'cadena de caracteres'; // 'string'
typeof {}; // 'object'
typeof []; // 'object
typeof new Date(); // 'object'
typeof new String('Jaime'); // Object
typeof function() {} // 'function'
typeof class A {} // 'function'

Existe un error conocido en javascript, cuando se ejecuta el operador typeof sobre null, este regresa ‘object'. Este error existe desde la primera versión de Javascript, cosas malas pasan cuando escribes código a las carreras.

Si todos los demás valores que no son primitivos son objetos, cuando usamos el operador typeof sobre ellos, ¿Qué tipo de datos nos indicara que son?, exacto, 'object'. Tal como en el ejemplo de arriba cuando pasamos un objeto literal, un array vacío, un objeto tipo fecha y cuando ocupamos los objetos envolventes de primitivos como new String('Jaime').

Existe una excepción en typeof, cuando el valor es una función o una clase, regresara function. Recuerda, una función en javascript, también es un objeto.

Verificar tipos con el método Object.prototype.toString

Como vimos, es algo confuso utilizar el operador typeof, yo prefiero utilizar un método más seguro, es algo raro, pero como todo en Javascript es un objeto y el objeto base del cual parten todos los tipos de datos en javascript es Object, con excepción de los datos primitivos ( Aunque estos a su vez tienen su versión objeto), entonces podemos hacer algo como lo siguiente.

Object.prototype.toString.call(5); // '[object Number]'
Object.prototype.toString.call(NaN); // '[object Number]'
Object.prototype.toString.call(99999999999999999999999999999999999999999999999n); // '[object BigInt]'
Object.prototype.toString.call(true); // '[object Boolean]'
Object.prototype.toString.call(null); // '[object Null]',
Object.prototype.toString.call(undefined); // '[object Undefined]'
Object.prototype.toString.call(Symbol('simbolo')); // '[object Symbol]'
Object.prototype.toString.call('cadena de caracteres'); // '[object String]'
Object.prototype.toString.call({}); // '[object Object]'
Object.prototype.toString.call([]); // '[object Array]'
Object.prototype.toString.call(new Date()); // '[object Date]'
Object.prototype.toString.call(new String('Jaime')); // '[object String]'

A partir de este método, se puede crear una función que verifique el tipo de dato.

function getTypeOf(valor) {
  const texto = Object.prototype.toString.call(valor); // '[object Number]', '[object Boolean]', etc
  const sinPalabraObject = texto.slice(8, -1); // Number, Boolean, Null, Undefined, Symbol, String, etc.
  const tipo = sinPalabraObject.toLowerCase(); // number, boolean, null, undefined, symbol, string, etc.
  return tipo;
}
getTypeOf(1); // 'number'
getTypeOf(new Date()); // date
getTypeOf([]); // array

La propiedad especial prototype es muy importante en Javascript, si has leído más de una de mis publicaciones, te habrás percatado que he mencionado que Javascript es un lenguaje de programación multiparadigma y orientado a objetos basado en prototipos, en otra publicación explicaremos a detalle la funcionalidad de prototipos en Javascript.

Conclusión

Todo en Javascript es un objeto, con la excepción de los tipos primitivos, pero como ya vimos, al querer usarlos como objetos invocando algún método, es ahí donde javascript los envuelve (en la practica todo es un objeto).

Cualquier lenguaje de programación tiene buenas cosas que podemos aprovechar, pero también puede tener cosas extrañas y confusas (comparando con la mayoria de los lenguajes de programación modernos), como el operador typeof y el método Object.prototype.toString, el valor NaN, los valores que pueden generar un boleano false, y muchas otras cosas que no vimos por razones de simplicidad. Queda del lado del programador tomar las mejores partes y eliminar o mitigar las que no nos ayudan a comunicarnos mejor con nuestro equipo de trabajo, siempre con un enfoque a simplificar las cosas.

En el caso de la creación de algún tipo de dato en Javascript, la manera más simple y fácil de usar, es simplemente creando los valores que vamos a utilizar. Tal como se recomendó en secciones anteriores, a esta forma de crearlos se les llama literales.

const numero = 9999.54;
const enteroGrande = 99999999999999n;
const boleano = true;
const nulo = null;
let noDefinido;
const simbolo = Symbol('identificador');
const cadenaDeCaracteres = 'Jaime Cervantes Velasco';
const cadenaDeCaractresConExpresion = `El numero ${numero} es "Number"`;
const objeto = {};
const arreglo = [];

Existen aún más detalles que cubrir sobre los tipos de datos en javascript, pero sobre la marcha iremos viendo más temas necesarios, de momento me parece que esta información es suficiente.

Paradigma de programación en JavaScript

Paradigma de programación en JavaScript

¿Qué es un paradigma de programación?

El modelo o mapa de como vemos el mundo real, eso es un paradigma, es una manera de ver y hacer las cosas. Siguiendo esta lógica, un paradigma de programación no es más que una forma de ver y crear código de programación.

Los paradigmas son poderosos porque crean los cristales o las lentes a través de los cuales vemos el mundo.

Stephen R. Covey

Existe tres principales paradigmas de programación utilizados en la actualidad y que en JavaScript siempre han existido desde su primera versión.

  • Paradigma de programación Estructurada
  • Orientada a objetos
  • Funcional

Ahora bien, si nos apegamos a la frase de Stephen R. Covey, sobre que los paradigmas crean las lentes a través de los cuales vemos el mundo. Imaginemos que los tres paradigmas anteriores, cada uno, son un par de lentes que nos ponemos a la hora de programar y que podemos cambiar esos lentes según nuestras necesidades visuales (de programación o solución de problemas).

Paradigma de programación estructurada

Origen

En el año 1954 apareció el lenguaje de programación FORTRAN, luego en 1958 ALGOL y en 1959 COBOL. Tiempo después Edsger Wybe Dijkstra en 1968 descubre el paradigma de programación estructurada, decimos que “descubre” porque en realidad no lo inventaron. Aunque este paradigma de programación se formalizó tiempo después de la aparición de estos lenguajes de programación, era posible hacer programación estructurada en ellos.

Dijkstra reconoció que la programación era difícil, y los programadores no lo hacemos muy bien, de lo cual estoy totalmente de acuerdo. Un programa de cualquier complejidad tiene muchos detalles para el cerebro humano. De hecho la neurociencia nos indica que la mente enfocada solo puede trabajar con cuatro datos a la vez, cuanto mucho. Por esta razón, si se descuida un pequeño detalle creamos programas que parecen funcionar bien, pero que fallan en formas que uno nunca se imagina.

Pruebas

La solución de Dijkstra fue utilizar pruebas, solo que estas pruebas usaban mucho las matemáticas, lo cual era bastante difícil de implementar. Durante su investigación encontró que ciertos usos de GOTO hacían difícil descomponer un programa grande en piezas más pequeñas, no se podía aplicar “divide y vencerás”, necesario para crear pruebas razonables.

Secuencia, selección e iteración

Patrones de la programación estructurada
Patrones de la programación estructurada

Böhm y Jacopini probaron dos años antes que todos los programas pueden ser construidos por solo tres estructuras: secuencia, selección e iteración. Dijkstra ya conocía estas tres estructuras y descubrió que estas mismas son las necesarias para hacer que cualquier parte del programa sea probado. Es aquí donde se formaliza el paradigma de programación estructurada.

Edsger nos indica que es una mala práctica usar GOTO y sugiere usar mejor las siguientes estructuras de control:

  • If, then, else (selección)
  • do, while, until (iteración o repetición)

Descomposición de pequeñas unidades fáciles de probar

Hoy en día la mayoría de los lenguajes de programación utilizan la programación estructurada, JavaScript utiliza este tipo de programación. El ejemplo más sencillo son las condiciones if.

const EDAD_MINIMA = 18;
if (edad >= EDAD_MINIMA) {
	// hacer algo porque es mayor que 18
} else {
	// Hacer otra cosa en caso contraio
}

Y si lo aislamos en una función, tenemos una descomposición de funcionalidades o unidades que pueden ser probadas con mayor facilidad. En la siguiente sección veremos la importancia de tener una unidad completamente probable

const EDAD_MINIMA = 18;
function esMayorEdad(edad) {
  if (edad >= EDAD_MINIMA) {
     return true;
  } else {
     return false;
  }
}

Con esta condición tenemos un control directo de lo que puede suceder si la edad es mayor o igual a 18, y también en el caso de que la edad sea menor.

Tampoco seamos tan estrictos, seguro que la sentencia GOTO tiene sus méritos y tal vez era muy eficiente en algunos casos, pero pienso que para evitar malos usos que probablemente causaban desastres, Dijkstra recomendó dejar de usar GOTO.

Pruebas, divide y vencerás

La programación estructurada nos permite aplicar la filosofía de “divide y vencerás”, porque nos permite crear pruebas de pequeñas unidades, hasta tener todo el programa cubierto. La solución matemática de Wybe Dijkstra nunca se construyo, precisamente por su dificulta de implementación. Y lamentablemente en nuestros días aún existen programadores que no creen que las pruebas formales sirven para crear software de alta calidad.

Y digo crear, porque verificar el correcto funcionamiento después de crear, no es más que una simple medición y se pierde la oportunidad de reducir tiempo y dinero.

Método científico, experimentos e impirismo

Método científico, experimentos y pruebas
Método científico, experimentos y pruebas, por ThisIsEngineering Raeng en unsplash

La buena noticia es que el método matemático no es la única forma de verificar nuestro código, también tenemos el método científico. El cual no puede probar que las cosas sean absolutamente correctas desde el punto de vista matemático. Lo que si se puede hacer es crear experimentos y obtener resultados suficientes para verificar que nuestras teorías funcionan. Dado que nuestras teorías funcionan sobre estos experimentos, entonces concluimos que son lo suficientemente “correctas” para nuestros propósitos de validación.

Si la teoría es fácilmente validada como falsa, entonces es momento de modificarla o cambiarla. Así es el método científico y así es como actualmente se hacen las pruebas:

Primero establecemos nuestra teoría (escribimos la prueba), después construimos los experimentos (escribimos código de producción) y finalmente verificamos (corremos la prueba) y repetimos este ciclo hasta tener evidencia suficiente.

¿Esto no te suena a una práctica ágil actual llamada TDD?

Las pruebas muestran la presencia, no la ausencia, de defectos

Edsger Wybe Dijkstra dijo “Las pruebas muestran la presencia, no la ausencia, de defectos”. Ósea, un programa puede ser probado incorrecto mediante pruebas, pero no puede ser probado correcto. Todo lo que podemos hacer con nuestras pruebas es validar que nuestro programa funciona lo suficiente bien para nuestros objetivos. Pero nunca podemos estar cien por ciento seguros de la ausencia de defectos.

Si nunca podemos estar cien por ciento seguros que nuestro código es correcto, aun con sus pruebas, ¿Qué nos hace pensar que sin pruebas entregamos un programa con la suficiente validez para afirmar que no se comete conscientemente alguna negligencia?

El control directo

La programación estructurada nos enseña sobre el control directo, es decir, controlar el flujo secuencial de las operaciones a través de las estructuras de control, sin descuidar la recomendación de Wybe Dijkstra sobre GOTO. Esta recomendación también es aplicable a sentencias que cambian drásticamente el flujo normal de las operaciones, por ejemplo un break dentro de un ciclo for anidado, ese break rompe el control directo porque complica abruptamente el entendimiento del algoritmo. Tampoco seamos puristas, seguro habrá casos que necesites de estas sentencias, pero de primera instancia, trata de evitarlas.

En conclusión, el paradigma de programación estructurada nos enseña:

El control directo, claro y explicito de lo que hace un pedazo de código

Paradigma de programación orientada a objetos

¿Surgido de funciones?

El paradigma de programación orientada a objetos se descubrió en 1966 por Ole Johan Dahl y Kristen Nygaard cuando desarrollaban simula 67. Dos años antes de la programación estructurada y su adopción por la comunidad de programadores fue tiempo después. En 1967 se lanzó Simula 67, Simula fue el primer lenguaje de programación orientado a objetos,  básicamente le agregó clases y objetos a ALGOL, inicialmente Simula iba a ser una especie de extensión de ALGOL.

Durante el desarrollo de Simula, se observó que la ejecución de una función de ALGOL necesita de ciertos datos, los cuales se podían mover a una estructura de árbol. Esta estructura de árbol es un Heap.

El heap permitía declarar variables locales de la función que pueden existir incluso después de que la función regrese un valor. ¿Esto último no les suena a lo que es closure en JavaScript?

La función padre se convierte en un constructor, las variables locales se convierten en las propiedades y las funciones hijas (anidadas) en sus métodos. De hecho este patrón es aún muy utilizado por Javacript para crear módulos y usar herencia funcional. También esto describe como funcionan las clases en Javascript, por detrás son funciones.

Esparcimiento del polimorfismo

Todo esto ayudó mucho a que se implementará el polimorfismo en los lenguajes de programación orientados a objetos. Ole Johan Dahl y Kristen Nygaard inventaron la notación:

objeto.funcion(parametro)

La ejecución de funcion que pertenece a objeto, pero más importante, le pasamos un mensaje a través de parámetros desde la primera parte del código a la segunda parte localizada en un objeto diferente.

La biología molecular y el paso de mensajes

Paso de mensajes entre células
Paso de mensajes entre células, Imagen modificada de “Signaling molecules and cellular receptors: Figure 1,” por OpenStax College, Biology (CC BY 3.0).

Tiempo después se creó Smalltalk, un lenguaje de programación orientado a objetos mucho más sofisticado y moderno, a cargo de este proyecto estaba Alan Kay. A esta persona se le atribuye la definición formal de la programación orientada a objetos.

La principal influencia en la programación orientada objetos de Alan Kay fue la biología, él es licenciado en biología. Aunque es obvio que fue influenciado por Simula, la influencia de LISP (lenguaje de programación funcional) no es tan obvia.

Desde un inicio él pensaba en los objetos como células interconectadas en una red, de la cual se podrían comunicar mediante mensajes.

Alan Kay comentó lo siguiente:

Lamento que hace tiempo haya acuñado el término Objetos para la programación porque hizo que las personas se enfocaran en la parte menos importante. La gran idea es “Envío de mensajes“.

Alan Kay

Mi Ingles es malo así que pueden revisar el texto original aquí.

Mejor composición de objetos sobre herencia de clases

Smalltalk permitió también la creación de Self, creado en Xerox Parc y luego migrado a Sun Microsystems Labs. Se dice que Self es una evolución de smalltalk.

En este lenguaje de programación se introdujo la idea de prototipos, eliminando la utilización de clases para crear objetos, este lenguaje utiliza los objetos mismos para permitir que un objeto reutilice las funcionalidades de otro.

Self es un lenguaje rápido y en general se le conoce por su gran rendimiento, self hizo un buen trabajo en sistemas de recolección de basura y también utilizaba una maquina virtual para administrar su ejecución y memoria.

La máquina virtual Java HotSpot se pudo crear gracias a Self. Lars Bak uno de los últimos contribuidores de Self, se encargó de la creación del motor de Javascript V8. Debido a la influencia de Self tenemos hoy en día el motor de JavaScript V8, el cual lo utiliza node.js, mongoDB y Google Chrome internamente.

Self seguía una de las recomendaciones del libro Design Patterns: Elements of Reusable Object-Oriented Software, mucho antes de que saliera publica esta recomendación:

Mejor composición de objetos a herencia de clases.

Design Patterns: Elements of Reusable Object-Oriented Software

Ejemplo

Actualmente Javascript utiliza prototipos para la reutilización de código, se podría decir que usa composición, en lugar de herencia. Veamos un ejemplo:

const persona = {
    saludar () {
      return 'Hola'
    }
};
// se crea un programador con el prototipo igual al objeto persona
const programador = Object.create(persona);
programador.programar  = () => 'if true';
const saludo = programador.saludar();
const codigo = programador.programar();
console.log(saludo); // 'Hola'
console.log(codigo); // 'if true'

El objeto programador se basa en el prototipo de `persona`. No hereda, solo reutiliza directamente la funcionalidad de `persona`.

JavaScript surge poquito después de JAVA, en el mismo año. JavaScript tomo influencias de Self, y a su vez Self fue influido por smalltalk.

La sintaxis de clases actuales en Javascript no es más que una fachada, bueno un poco más exacto son una evolución de las funciones constructoras, pero internamente, su base, son las funciones y los prototipos.

La comunicación con el paso de mensajes

Como ya vimos, el origen de la programación orientada objetos tiene su origen en las funciones, y la idea principal siempre ha sido la comunicación entre objetos a través del paso de mensajes. Podemos decir que La programación orientada a objetos nos habla del paso de mensajes como pieza clave para la comunicación de objetos, por ejemplo, cuando un método de un objeto invoca el método de otro.

objeto.funcion(parametro);

En conclusión, el paradigma de programación orientado a objetos, nos enseña:

El paso de mensajes para la comunicación entre objetos

Paradigma de programación funcional

Cálculo lambda

Imagen tomada de https://www.slideshare.net/FASTPresentations/introduction-to-lambda-calculus-using-smalltalk-by-facundo-javier-gelatti

La programación funcional es el paradigma de programación más viejo, de hecho su descubrimiento fue mucho antes de la programación para computadoras, fue descubierto en 1936 por Alonzo Church, cuando invento el cálculo lambda, base del mismo problema a resolver de su alumno Alan Turing.

Como dato curioso el símbolo del cálculo lambda es:

λ

El primer lenguaje basado en programación funcional fue LISP, creado por John McCarthy. Como vimos anteriormente, Alan Kay también tomo influencias de LISP para crear Smalltalk, con esto podemos comprobar que los paradigmas de programación pueden estar unidos y su relación entre ellos surge de la idea de como resolver problemas de manera más eficiente, no están peleados, ni separados, buscan el mismo objetivo y de alguna manera evolucionan en conjunto.

La base del cálculo lambda es la inmutabilidad, más adelante revisaremos este concepto.

Similitudes con los paradigmas OO y estructurada

Como vimos en el paradigma de programación estructurada, podemos descomponer nuestros programas en unidades más pequeñas llamadas procedimientos o funciones. Dado que la programación funcional es el paradigma más viejo, podemos decir que el paradigma de programación estructurada también se apoya en la programación funcional.

Ahora bien, si analizamos un poco la notación objeto.funcion(x) que vimos en la sección del paradigma de programación orientada a objetos, no es muy diferente de funcion(objeto, parametros), estas dos lineas de abajo son iguales, la idea fundamental es el paso de mensajes, como nos indica Alan Kay.

objeto.funcion(parametro);
funcion(objeto, parametro);

Smalltalk usa el “El Modelo Actor”, el cual dice que los actores se comunican entre ellos a través de mensajes.

Por otro lado tenemos a LISP, el cual tiene un “Modelo despachador de funciones” que actualmente le llamamos lenguaje funcional, estos modelos son idénticos, porque las funciones y métodos lo que hacen es enviar mensajes entre actores.

Un ejemplo de esto es cuando una función llama a otra función o cuando se invoca el método de un objeto, lo que sucede es que existen actores y se comunican entre ellos a través de mensajes.

De nuevo el paso de mensajes

Entonces podemos recalcar que la idea principal de la POO es el envío de mensajes y que no es muy diferente de la programación funcional, de hecho según lo que ya establecimos en las secciones anteriores, POO nació de una base funcional. Y aquí es importante remarcar lo que dijo Alan Kay Co-creador de SmallTalk:

Lamento que hace tiempo haya acuñado el término Objetos para la programación porque hizo que las personas se enfocaran en la parte menos importante. La gran idea es “Envío de mensajes“.

Alan Kay

A partir de los modelos de comunicación entre mensajes de Smalltalk y LISP, se creó Scheme.

Scheme tiene la bondad de usar Tail recursion y closure para obtener un lenguaje funcional, closure permite tener acceso a las variables de una función externa aun cuando esta ya haya regresado algún valor. Esto es el mismo principio que origino a la programación orientada a objetos a cargo de Ole Johan Dahl y Kristen Nygaard. ¿Recuerdan la pregunta de closure en Javascript?

Javascript usa mucho closure en su paradigma de programación funcional. JavaScript tomó influencias de Scheme, a su vez Scheme fue influido por LISP.

Sin efectos colaterales

La base del cálculo lambda es la inmutabilidad, por consiguiente, la inmutabilidad también es la base del paradigma de programación funcional.

Nosotros como programadores funcionales debemos seguir este principio y limitar las mutaciones de objetos lo más que se pueda para evitar efectos colaterales.

Repito, la idea principal de la programación funcional es la inmutabilidad, la cual te permite crear tus programas con menos errores al no producir efectos colaterales difíciles de controlar.

Como ejemplo, supongamos que tenemos un objeto persona y queremos “cambiar” el nombre, lo más obvio seria modificar la propiedad nombre directamente, pero que pasa si ese objeto es utilizado en otra parte y se espera que nombre sea “Jaime”, es por eso que en lugar de cambiar la propiedad nombre, solo creamos un nuevo objeto persona con diferente nombre, sin modificar el original.

function cambiarNombre(nombre, persona) {
  return {
    nombre: nombre,
    edad: persona.edad
  }
}
const jaime = { nombre: 'Jaime', edad: 30 };
const juan = cambiarNombre('Juan', jaime);
console.log(jaime); // { nombre: 'Jaime', edad: 30 }
console.log(juan); // { nombre: 'Juan', edad: 30 }

Aquí encontrarás más detalles sobre los principios fundamentales de programación funcional:

Introducción a programación funcional

Por último, el paradigma de programación funcional nos enseña:

Sin efectos colaterales, para una expresión clara y menos propensa a errores

Conclusión

Es importante destacar que si los tres paradigmas de programación fueron implementados entre los años 1958 con LISP (programación funcional) y Simula (programación Orientado a objetos) en 1966, y el descubrimiento del paradigma de programación estructurado en 1968, en solo un lapso de 10 años existió la innovación de los paradigmas de programación, y realmente nuevos paradigmas no han surgido, al menos que no sean basados en estos tres principales.

Ahora mismo ya han pasado más de 60 años, lo que nos dice de la importancia y la firmeza que tienen a pesar del tiempo transcurrido.

  • Paradigma de programación funcional (1958, ya implementado en un lenguaje de programación para computadoras, recordemos que fue descubierto en 1936)
  • Orientado objetos (1966)
  • Estructurada (1968)

La implementación de estos tres paradigmas desde el inicio en el interior de JavaScript han hecho que este lenguaje de programación tenga el éxito global de hoy en día. Y nos comprueba lo valioso de combinar los paradigmas al escribir nuestros programas.

JavaScript es en lenguaje de programación multiparadigma, por lo que se puede combinar los paradigmas para crear un código mucha más eficiente y expresivo usando:

  • El control directo, claro y explicito de lo que hace un pedazo de código
  • El paso de mensajes para la comunicación entre objetos
  • Sin efectos colaterales, para una expresión clara y menos propensa a errores

Referencias

https://en.wikipedia.org/wiki/Structured_programming

https://en.wikipedia.org/wiki/Structured_program_theorem

http://wiki.c2.com/?AlanKayOnMessaging

https://www.slideshare.net/FASTPresentations/introduction-to-lambda-calculus-using-smalltalk-by-facundo-javier-gelatti

Libro Clean architecture

es_MXES_MX