Descargar como docx, pdf o txt
Descargar como docx, pdf o txt
Está en la página 1de 291

JavaScript (Semana 4)

1. ¿Qué es JavaScript?

JavaScript se creó inicialmente para "dar vida a las páginas web".

Los programas en este idioma se llaman scripts . Se pueden escribir


directamente en el HTML de una página web y ejecutarse automáticamente a
medida que se carga la página.

Los scripts se proporcionan y ejecutan como texto sin formato. No necesitan


preparación especial o compilación para ejecutarse.

En este aspecto, JavaScript es muy diferente a otro lenguaje llamado Java .

¿Por qué se llama Java Script?

Cuando se creó JavaScript, inicialmente tenía otro nombre: "LiveScript". Pero


Java era muy popular en ese momento, por lo que se decidió que ayudaría
posicionar un nuevo lenguaje como un "hermano menor" de Java.

Pero a medida que evolucionó, JavaScript se convirtió en un lenguaje


completamente independiente con su propia especificación
llamada ECMAScript , y ahora no tiene ninguna relación con Java.

Hoy en día, JavaScript puede ejecutarse no solo en el navegador, sino también


en el servidor, o en cualquier dispositivo que tenga un programa especial
llamado motor JavaScript .

El navegador tiene un motor integrado a veces llamado "máquina virtual


JavaScript".
Es bueno recordar los términos anteriores porque se utilizan en artículos para
desarrolladores en Internet. Los usaremos también. Por ejemplo, si "una función
X es compatible con V8", probablemente funcione en Chrome y Opera.

¿Cómo funcionan los motores?

Los motores son complicados. Pero lo básico es sencillo.

1. El motor (incrustado si es un navegador) lee ("analiza") el script.

2. Luego convierte ("compila") el script al lenguaje de máquina.

3. Y luego el código de la máquina se ejecuta bastante rápido.

El motor aplica optimizaciones en cada paso del proceso. Incluso observa el


script compilado mientras se ejecuta, analiza los datos que fluyen a través de él
y optimiza aún más el código de la máquina en función de ese conocimiento.

¿Qué puede hacer JavaScript en el navegador?

JavaScript moderno es un lenguaje de programación "seguro". No proporciona


acceso de bajo nivel a la memoria o la CPU, ya que fue creado inicialmente para
navegadores que no lo requieren.

Las capacidades de JavaScript dependen en gran medida del entorno en el que


se ejecuta. Por ejemplo, Node.js admite funciones que permiten a JavaScript
leer / escribir archivos arbitrarios, realizar solicitudes de red, etc.

JavaScript en el navegador puede hacer todo lo relacionado con la


manipulación de la página web, la interacción con el usuario y el servidor web.

Por ejemplo, JavaScript en el navegador puede:


 Agregue nuevo HTML a la página, cambie el contenido existente,
modifique los estilos.

 Reaccione a las acciones del usuario, ejecute clics del mouse,


movimientos del puntero, pulsaciones de teclas.

 Envíe solicitudes a través de la red a servidores remotos, descargue y


cargue archivos (las denominadas tecnologías AJAX y COMET ).

 Obtenga y configure cookies, haga preguntas al visitante, muestre


mensajes.

 Recuerde los datos del lado del cliente ("almacenamiento local").

¿Qué NO PUEDE hacer JavaScript en el navegador?

Las capacidades de JavaScript en el navegador están limitadas por el bien de la


seguridad del usuario. El objetivo es evitar que una página web malintencionada
acceda a información privada o dañe los datos del usuario.

Ejemplos de tales restricciones incluyen:

 JavaScript en una página web no puede leer / escribir archivos arbitrarios


en el disco duro, copiarlos o ejecutar programas. No tiene acceso directo a las
funciones del sistema operativo.

Los navegadores modernos le permiten trabajar con archivos, pero el acceso es


limitado y solo se proporciona si el usuario realiza ciertas acciones, como
"soltar" un archivo en una ventana del navegador o seleccionarlo mediante

una <input>etiqueta.

Hay formas de interactuar con la cámara / micrófono y otros dispositivos, pero


requieren el permiso explícito del usuario. Por lo tanto, una página habilitada
para JavaScript no puede habilitar furtivamente una cámara web, observar los
alrededores y enviar la información a la NSA .

 Las diferentes pestañas / ventanas generalmente no se conocen entre


sí. A veces lo hacen, por ejemplo, cuando una ventana usa JavaScript para abrir
la otra. Pero incluso en este caso, es posible que JavaScript de una página no
acceda a la otra si provienen de diferentes sitios (de un dominio, protocolo o
puerto diferente).

Esto se denomina "Política del mismo origen". Para solucionarlo, ambas


páginas deben aceptar el intercambio de datos y contener un código JavaScript
especial que lo maneje. Cubriremos eso en el tutorial.

Esta limitación es, nuevamente, para la seguridad del usuario. Una página desde

la http://anysite.comque un usuario ha abierto no debe poder acceder a

otra pestaña del navegador con la URL http://gmail.comy robar información

desde allí.

 JavaScript puede comunicarse fácilmente a través de la red con el


servidor de donde proviene la página actual. Pero su capacidad para recibir
datos de otros sitios / dominios está paralizada. Aunque es posible, requiere un
acuerdo explícito (expresado en encabezados HTTP) desde el lado remoto. Una
vez más, esa es una limitación de seguridad.

Estos límites no existen si JavaScript se utiliza fuera del navegador, por ejemplo,
en un servidor. Los navegadores modernos también permiten complementos /
extensiones que pueden solicitar permisos extendidos.

¿Qué hace que JavaScript sea único?

Hay al menos tres cosas buenas sobre JavaScript:

 Integración completa con HTML / CSS.


 Las cosas simples se hacen simplemente.

 Compatible con los principales navegadores y habilitado de forma


predeterminada.

JavaScript es la única tecnología de navegador que combina estas tres cosas.

Eso es lo que hace que JavaScript sea único. Por eso es la herramienta más
extendida para crear interfaces de navegador.

Dicho esto, JavaScript también permite crear servidores, aplicaciones móviles,


etc.

Idiomas "sobre" JavaScript

La sintaxis de JavaScript no se adapta a las necesidades de todos. Diferentes


personas quieren diferentes características.

Eso es de esperar, porque los proyectos y los requisitos son diferentes para
todos.

Recientemente apareció una plétora de nuevos lenguajes, que


se transpilan (convierten) a JavaScript antes de que se ejecuten en el navegador.

Las herramientas modernas hacen que la transpilación sea muy rápida y


transparente, lo que permite a los desarrolladores codificar en otro idioma y
convertirlo automáticamente "bajo el capó".

Ejemplos de tales lenguajes:

 CoffeeScript es un "azúcar sintáctico" para JavaScript. Introduce una


sintaxis más corta, lo que nos permite escribir código más claro y preciso. Por lo
general, a los desarrolladores de Ruby les gusta.
 TypeScript se concentra en agregar "tipificación de datos estricta" para
simplificar el desarrollo y el soporte de sistemas complejos. Está desarrollado
por Microsoft.

 Flow también agrega la escritura de datos, pero de una manera


diferente. Desarrollado por Facebook.

 Dart es un lenguaje independiente que tiene su propio motor que se


ejecuta en entornos que no son de navegador (como aplicaciones móviles),
pero que también se puede transpilar a JavaScript. Desarrollado por Google.

 Brython es un transpilador de Python a JavaScript que permite escribir


aplicaciones en Python puro sin JavaScript.

Hay mas. Por supuesto, incluso si usamos uno de los lenguajes transpilados,


también deberíamos saber JavaScript para comprender realmente lo que
estamos haciendo.

Resumen

 JavaScript se creó inicialmente como un lenguaje solo para navegador,


pero ahora también se usa en muchos otros entornos.

 Hoy en día, JavaScript tiene una posición única como el lenguaje de


navegador más ampliamente adoptado con integración completa con HTML /
CSS.

 Hay muchos lenguajes que se "transpilan" a JavaScript y proporcionan


ciertas características. Se recomienda echarles un vistazo, al menos brevemente,
después de dominar JavaScript.
2. Consola de desarrollador

El código es propenso a errores. Es muy probable que cometa errores ... Oh, ¿de
qué estoy hablando? Usted está absolutamente va a cometer errores, al menos
si eres un humano y no un robot .

Pero en el navegador, los usuarios no ven errores de forma


predeterminada. Entonces, si algo sale mal en el script, no veremos qué está
roto y no podremos arreglarlo.

Para ver los errores y obtener mucha más información útil sobre los scripts, se
han integrado "herramientas de desarrollo" en los navegadores.

La mayoría de los desarrolladores se inclinan por Chrome o Firefox para el


desarrollo porque esos navegadores tienen las mejores herramientas de
desarrollo. Otros navegadores también proporcionan herramientas de
desarrollo, a veces con funciones especiales, pero normalmente se ponen al día
con Chrome o Firefox. Por lo tanto, la mayoría de los desarrolladores tienen un
navegador "favorito" y cambian a otros si el problema es específico del
navegador.

Las herramientas de desarrollo son potentes; tienen muchas características. Para


comenzar, aprenderemos cómo abrirlos, ver errores y ejecutar comandos
JavaScript.

Google Chrome

Abra la página bug.html .

Hay un error en el código JavaScript. Está oculto a los ojos de un visitante


habitual, así que abramos las herramientas de desarrollo para verlo.

Presione  F12 o, si está en Mac, entonces . Cmd+Opt+J


Las herramientas de desarrollador se abrirán en la pestaña Consola de forma
predeterminada.

Se parece a esto:

El aspecto exacto de las herramientas para desarrolladores depende de su


versión de Chrome. Cambia de vez en cuando, pero debería ser similar.

 Aquí podemos ver el mensaje de error de color rojo. En este caso, el


script contiene un comando "lalala" desconocido.

 A la derecha, hay un enlace en el que se puede hacer clic a la


fuente bug.html:12con el número de línea donde se produjo el error.

Debajo del mensaje de error, hay un >símbolo azul . Marca una "línea de

comandos" donde podemos escribir comandos JavaScript. Presione  Enter para

ejecutarlos.

Ahora podemos ver errores, y eso es suficiente para empezar. Regresaremos a


las herramientas para desarrolladores más adelante y cubriremos la depuración
con más profundidad en el capítulo Depuración en Chrome .

Entrada multilínea

Por lo general, cuando colocamos una línea de código en la consola y luego

presionamos  Enter , se ejecuta.


Para insertar varias líneas, presione . De esta forma, se pueden ingresar

fragmentos largos de código JavaScript. Shift+Enter

Resumen

 Las herramientas de desarrollo nos permiten ver errores, ejecutar


comandos, examinar variables y mucho más.

 Se pueden abrir con la  F12 mayoría de los navegadores en

Windows. Chrome para Mac necesita , Safari: (primero debe

habilitarlo). Cmd+Opt+JCmd+Opt+C
3. La etiqueta "script"

Los programas JavaScript se pueden insertar en cualquier parte de un


documento HTML con la ayuda de la <script>etiqueta.

La <script>etiqueta contiene código JavaScript que se ejecuta

automáticamente cuando el navegador procesa la etiqueta.

Marcado moderno

La <script>etiqueta tiene algunos atributos que rara vez se usan hoy en día,

pero que aún se pueden encontrar en el código antiguo:

El typeatributo:<script type=…>

El antiguo estándar HTML, HTML4, requería que un script tuviera una

extensión type. Normalmente lo era type="text/javascript". Ya no

es necesario. Además, el estándar HTML moderno cambió totalmente el


significado de este atributo. Ahora, se puede utilizar para módulos
JavaScript. Pero ese es un tema avanzado, hablaremos de módulos en
otra parte del tutorial.

El languageatributo:<script language=…>

Este atributo estaba destinado a mostrar el idioma del guión. Este


atributo ya no tiene sentido porque JavaScript es el idioma
predeterminado. No es necesario utilizarlo.

Comentarios antes y después de los guiones.

En libros y guías realmente antiguos, puede encontrar comentarios

dentro de <script>etiquetas, como esta:

<script type="text/javascript"><!--
...

//--></script>

Este truco no se usa en JavaScript moderno. Estos comentarios ocultan el


código JavaScript de los navegadores antiguos que no sabían cómo

procesar la <script>etiqueta. Dado que los navegadores lanzados en los

últimos 15 años no tienen este problema, este tipo de comentario puede


ayudarlo a identificar el código realmente antiguo.

Guiones externos

Si tenemos mucho código JavaScript, podemos ponerlo en un archivo separado.

Los archivos de secuencia de comandos se adjuntan a HTML con el srcatributo:

<script src="/path/to/script.js"></script>

Aquí, /path/to/script.jshay una ruta absoluta al script desde la raíz del

sitio. También se puede proporcionar una ruta relativa desde la página


actual. Por ejemplo, src="script.js"significaría un archivo "script.js"en la

carpeta actual.

También podemos proporcionar una URL completa. Por ejemplo:

<script
src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lo
dash.js"></script>

Para adjuntar varios scripts, use varias etiquetas:

<script src="/js/script1.js"></script>

<script src="/js/script2.js"></script>


Tenga en cuenta:

Como regla general, solo los scripts más simples se colocan en HTML. Los más
complejos residen en archivos separados.

El beneficio de un archivo separado es que el navegador lo descargará y lo


almacenará en su caché .

Otras páginas que hacen referencia al mismo script lo tomarán de la caché en


lugar de descargarlo, por lo que el archivo se descarga solo una vez.

Eso reduce el tráfico y acelera las páginas.

Si srcestá configurado, se ignora el contenido del script.

Una sola <script>etiqueta no puede contener tanto el srcatributo como el

código.

Esto no funcionará:

<script src="file.js">

alert(1); // the content is ignored, because src is set

</script>

Debemos elegir entre un código externo <script src="…">o uno

regular <script>.

El ejemplo anterior se puede dividir en dos scripts para que funcione:

<script src="file.js"></script>

<script>

alert(1);

</script>
Resumen

 Podemos usar una <script>etiqueta para agregar código JavaScript a

una página.

 Los atributos typey languageno son obligatorios.

 Se puede insertar un script en un archivo externo <script

src="path/to/script.js"></script>.

Hay mucho más que aprender sobre los scripts del navegador y su interacción
con la página web. Pero tengamos en cuenta que esta parte del tutorial está
dedicada al lenguaje JavaScript, por lo que no debemos distraernos con
implementaciones específicas del navegador. Usaremos el navegador como una
forma de ejecutar JavaScript, que es muy conveniente para la lectura en línea,
pero solo uno de muchos.
4. Estructura de código

Lo primero que estudiaremos son los componentes básicos del código.

Declaraciones

Las declaraciones son construcciones de sintaxis y comandos que realizan


acciones.

Ya hemos visto una declaración, alert('Hello, world!')que muestra el

mensaje "¡Hola, mundo!".

Podemos tener tantas declaraciones en nuestro código como queramos. Las


declaraciones se pueden separar con punto y coma.

Por ejemplo, aquí dividimos "Hola mundo" en dos alertas:

alert('Hello'); alert('World');

Por lo general, las declaraciones se escriben en líneas separadas para que el


código sea más legible:

alert('Hello');

alert('World');

Punto y coma

Se puede omitir un punto y coma en la mayoría de los casos cuando existe un


salto de línea.

Esto también funcionaría:

alert('Hello')

alert('World')
Aquí, JavaScript interpreta el salto de línea como un punto y coma
"implícito". Esto se denomina inserción automática de punto y coma .

En la mayoría de los casos, una nueva línea implica un punto y coma. ¡Pero "en
la mayoría de los casos" no significa "siempre"!

Hay casos en los que una nueva línea no significa un punto y coma. Por
ejemplo:

alert(3 +

+ 2);

El código se genera 6porque JavaScript no inserta punto y coma aquí. Es

intuitivamente obvio que si la línea termina con un signo más "+", entonces es

una "expresión incompleta", por lo que no se requiere el punto y coma. Y en


este caso, funciona según lo previsto.

Pero hay situaciones en las que JavaScript "falla" al asumir un punto y coma
donde realmente se necesita.

Los errores que ocurren en tales casos son bastante difíciles de encontrar y
corregir.

Un ejemplo de error

Si tiene curiosidad por ver un ejemplo concreto de tal error, consulte este
código:

[1, 2].forEach(alert)

No es necesario pensar en el significado de los


corchetes []y forEachtodavía. Los estudiaremos más tarde. Por ahora,

recuerde el resultado del código: se muestra 1entonces 2.


Ahora, agreguemos un alertantes del código y no terminemos con un punto y

coma:

alert("There will be an error")

[1, 2].forEach(alert)

Ahora, si ejecutamos el código, ¡solo alertse muestra el primero y luego

tenemos un error!

Pero todo vuelve a estar bien si agregamos un punto y coma después de alert:

alert("All fine now");

[1, 2].forEach(alert)

Ahora tenemos el mensaje "Todo bien ahora" seguido de 1y 2.

El error en la variante sin punto y coma se produce porque JavaScript no asume

un punto y coma antes de los corchetes [...].

Entonces, debido a que el punto y coma no se inserta automáticamente, el


código del primer ejemplo se trata como una sola declaración. Así es como lo
ve el motor:

alert("There will be an error")[1, 2].forEach(alert)

Pero deberían ser dos declaraciones separadas, no una. Tal fusión en este caso
es simplemente incorrecta, de ahí el error. Esto puede suceder en otras
situaciones.

Recomendamos poner punto y coma entre las declaraciones incluso si están


separadas por líneas nuevas. Esta regla es ampliamente adoptada por la
comunidad. Observemos una vez más: es posible omitir el punto y coma la
mayor parte del tiempo. Pero es más seguro, especialmente para un
principiante, usarlos.

Comentarios

A medida que pasa el tiempo, los programas se vuelven cada vez más
complejos. Es necesario agregar comentarios que describan qué hace el código
y por qué.

Los comentarios se pueden colocar en cualquier lugar de un guión. No afectan


su ejecución porque el motor simplemente los ignora.

Los comentarios de una línea comienzan con dos caracteres de barra

diagonal //.

El resto de la línea es un comentario. Puede ocupar una línea completa propia o


seguir una declaración.

Como aquí:

// This comment occupies a line of its own

alert('Hello');

alert('World'); // This comment follows the statement

Los comentarios de varias líneas comienzan con una barra diagonal y un

asterisco /*y terminan con un asterisco y una barra inclinada */.

Me gusta esto:

/* An example with two messages.

This is a multiline comment.


*/

alert('Hello');

alert('World');

El contenido de los comentarios se ignora, por lo que si ponemos código


dentro /* … */, no se ejecutará.

A veces puede resultar útil deshabilitar temporalmente una parte del código:

/* Commenting out the code

alert('Hello');

*/

alert('World');

¡Usa accesos rapidos del teclado!

En la mayoría de los editores, una línea de código se puede comentar


presionando la tecla de acceso rápido para un comentario de una sola línea y
algo como - para comentarios de varias líneas (seleccione un fragmento de
código y presione la tecla de acceso rápido). Para Mac, intente en lugar de y

en lugar de . Ctrl+/Ctrl+Shift+/CmdCtrlOptionShift

¡Los comentarios anidados no son compatibles!

Puede que no haya /*...*/dentro de otro /*...*/.

Dicho código morirá con un error:

/*

/* nested comment ?!? */

*/
alert( 'World' );

Por favor, no dudes en comentar tu código.

Los comentarios aumentan la huella general del código, pero eso no es un


problema en absoluto. Hay muchas herramientas que minimizan el código antes
de publicarlo en un servidor de producción. Quitan los comentarios para que no
aparezcan en los guiones de trabajo. Por tanto, los comentarios no tienen
ningún efecto negativo sobre la producción.

El modo moderno, "uso estricto"

Durante mucho tiempo, JavaScript evolucionó sin problemas de


compatibilidad. Se agregaron nuevas funciones al idioma, mientras que la
funcionalidad anterior no cambió.

Eso tenía la ventaja de no romper el código existente. Pero la desventaja era


que cualquier error o una decisión imperfecta de los creadores de JavaScript se
quedaba estancada en el lenguaje para siempre.

Este fue el caso hasta 2009 cuando apareció ECMAScript 5 (ES5). Añadió nuevas
funciones al lenguaje y modificó algunas de las existentes. Para que el código
antiguo siga funcionando, la mayoría de estas modificaciones están
desactivadas de forma predeterminada. Es necesario que les permita
explícitamente de una directiva especial: "use strict".

"Usar estricto"

La directiva se parece a una cadena: "use strict"o 'use strict'. Cuando se

encuentra en la parte superior de un guión, todo el guión funciona de manera


“moderna”.

Por ejemplo:
"use strict";

// this code works the modern way

...

Muy pronto aprenderemos funciones (una forma de agrupar comandos), así que

observemos de antemano que "use strict"se pueden poner al principio de

una función. Hacer eso habilita el modo estricto solo en esa función. Pero


generalmente la gente lo usa para todo el guión.

Asegúrese de que "use estricto" esté en la parte superior

Asegúrese de que "use strict"esté en la parte superior de sus scripts; de lo

contrario, es posible que el modo estricto no esté habilitado.

El modo estricto no está habilitado aquí:

alert("some code");

// "use strict" below is ignored--it must be at the top

"use strict";

// strict mode is not activated

Solo pueden aparecer comentarios arriba "use strict".

No hay forma de cancelar use strict

No existe una directiva como la "no use strict"que revierte el motor a su

antiguo comportamiento.
Una vez que ingresamos al modo estricto, no hay vuelta atrás.

Consola del navegador

Cuando usa una consola de desarrollador para ejecutar código, tenga en cuenta

que no lo hace use strictde forma predeterminada.

A veces, cuando use strictmarca la diferencia, obtendrá resultados

incorrectos.

Entonces, ¿cómo en realidad use stricten la consola?

Primero, puede intentar presionar para ingresar varias líneas y colocarlas en la

parte superior, así: Shift+Enter use strict

'use strict'; <Shift+Enter for a newline>

// ...your code

<Enter to run>

Funciona en la mayoría de los navegadores, a saber, Firefox y Chrome.

Si no es así, por ejemplo, en un navegador antiguo, hay una forma fea pero
confiable de asegurarse use strict. Ponlo dentro de este tipo de envoltorio:

(function() {

'use strict';

// ...your code here...

})()

¿Deberíamos "usar estricto"?


La pregunta puede parecer obvia, pero no lo es.

Se podría recomendar comenzar los guiones con "use strict"… ¿Pero sabes

qué es genial?

El JavaScript moderno admite "clases" y "módulos", estructuras de lenguaje

avanzadas (seguramente llegaremos a ellas), que se habilitan use

strictautomáticamente. Por lo tanto, no necesitamos agregar la "use

strict"directiva, si las usamos.

Entonces, por ahora "use strict";es un invitado bienvenido en la parte

superior de sus guiones. Más tarde, cuando su código esté en clases y módulos,


puede omitirlo.

A partir de ahora, tenemos que conocerlo use stricten general.

Variables

La mayoría de las veces, una aplicación JavaScript necesita trabajar con


información. A continuación se muestran dos ejemplos:

1. Una tienda en línea: la información puede incluir productos que se


venden y un carrito de compras.

2. Una aplicación de chat: la información puede incluir usuarios, mensajes y


mucho más.

Las variables se utilizan para almacenar esta información.

Una variable

Una variable es un "almacenamiento con nombre" para datos. Podemos usar


variables para almacenar golosinas, visitantes y otros datos.

Para crear una variable en JavaScript, use la letpalabra clave.


La siguiente declaración crea (en otras palabras: declara ) una variable con el
nombre "mensaje":

let message;

Ahora, podemos poner algunos datos en él usando el operador de asignación =:

let message;

message = 'Hello'; // store the string

La cadena ahora se guarda en el área de memoria asociada con la


variable. Podemos acceder a él usando el nombre de la variable:

let message;

message = 'Hello!';

alert(message); // shows the variable content

Para ser concisos, podemos combinar la declaración de variable y la asignación


en una sola línea:

let message = 'Hello!'; // define the variable and assign the


value

alert(message); // Hello!

También podemos declarar múltiples variables en una línea:

let user = 'John', age = 25, message = 'Hello';


Puede parecer más corto, pero no lo recomendamos. En aras de una mejor
legibilidad, utilice una sola línea por variable.

La variante multilínea es un poco más larga, pero más fácil de leer:

let user = 'John';

let age = 25;

let message = 'Hello';

Algunas personas también definen múltiples variables en este estilo de líneas


múltiples:

let user = 'John',

age = 25,

message = 'Hello';

... O incluso en el estilo de "coma primero":

let user = 'John'

, age = 25

, message = 'Hello';

Técnicamente, todas estas variantes hacen lo mismo. Entonces, es una cuestión


de gusto y estética personal.

var en vez de let

En scripts más antiguos, también puede encontrar otra palabra clave:

en varlugar de let:

var message = 'Hello';


La varpalabra clave es casi la misma que let. También declara una variable,

pero de una manera ligeramente diferente, de la "vieja escuela".

Hay diferencias sutiles entre lety var, pero todavía no nos importan. Los

cubriremos en detalle en el capítulo La vieja "var" .

Una analogía de la vida real

Podemos captar fácilmente el concepto de "variable" si lo imaginamos como


una "caja" de datos, con una etiqueta con un nombre exclusivo.

Por ejemplo, la variable messagese puede imaginar como un cuadro

etiquetado "message"con el valor "Hello!"en él:

Podemos poner cualquier valor en la casilla.

También podemos cambiarlo tantas veces como queramos:

let message;

message = 'Hello!';

message = 'World!'; // value changed

alert(message);

Cuando se cambia el valor, los datos antiguos se eliminan de la variable:


También podemos declarar dos variables y copiar datos de una a otra.

let hello = 'Hello world!';

let message;

// copy 'Hello world' from hello into message

message = hello;

// now two variables hold the same data

alert(hello); // Hello world!

alert(message); // Hello world!

Declarar dos veces provoca un error

Una variable debe declararse solo una vez.

Una declaración repetida de la misma variable es un error:

let message = "This";

// repeated 'let' leads to an error

let message = "That"; // SyntaxError: 'message' has already been


declared

Por lo tanto, deberíamos declarar una variable una vez y luego referirnos a ella
sin ella let.
Lenguajes funcionales

Es interesante notar que existen lenguajes de programación funcionales ,


como Scala o Erlang, que prohíben cambiar los valores de las variables.

En dichos lenguajes, una vez que el valor se almacena "en la caja", está ahí para
siempre. Si necesitamos almacenar algo más, el lenguaje nos obliga a crear una
nueva caja (declarar una nueva variable). No podemos reutilizar el anterior.

Aunque pueda parecer un poco extraño a primera vista, estos lenguajes son
bastante capaces de desarrollarse seriamente. Más que eso, hay áreas como los
cálculos paralelos donde esta limitación confiere ciertos beneficios. Se
recomienda estudiar ese idioma (incluso si no planea usarlo pronto) para
ampliar la mente.

Denominación variable

Hay dos limitaciones en los nombres de variables en JavaScript:

1. El nombre debe contener solo letras, dígitos o los símbolos $y _.

2. El primer carácter no debe ser un dígito.

Ejemplos de nombres válidos:

let userName;

let test123;

Cuando el nombre contiene varias palabras, se usa comúnmente camelCase . Es


decir: las palabras van uno tras otro, cada palabra excepto la primera partida
con una letra mayúscula: myVeryLongName.
Lo que es interesante: el signo de dólar '$'y el guión bajo '_'también se

pueden usar en los nombres. Son símbolos regulares, como letras, sin ningún
significado especial.

Estos nombres son válidos:

let $ = 1; // declared a variable with the name "$"

let _ = 2; // and now a variable with the name "_"

alert($ + _); // 3

Ejemplos de nombres de variables incorrectos:

let 1a; // cannot start with a digit

let my-name; // hyphens '-' aren't allowed in the name

El caso importa

Las variables nombradas appley AppLEson dos variables diferentes.

Se permiten letras no latinas, pero no se recomiendan

Es posible utilizar cualquier idioma, incluidas letras cirílicas o incluso jeroglíficos,


como este:

let имя = '...';

let 我 = '...';

Técnicamente, no hay ningún error aquí. Estos nombres están permitidos, pero


existe una convención internacional para usar el inglés en los nombres de
variables. Incluso si estamos escribiendo un guión pequeño, es posible que
tenga una larga vida por delante. Es posible que las personas de otros países
necesiten leerlo en algún momento.

Nombres reservados

Existe una lista de palabras reservadas , que no se pueden utilizar como


nombres de variables porque son utilizadas por el propio idioma.

Por ejemplo: let, class, return, y functionestán reservados.

El siguiente código da un error de sintaxis:

let let = 5; // can't name a variable "let", error!

let return = 5; // also can't name it "return", error!

Una tarea sin use strict

Normalmente, necesitamos definir una variable antes de usarla. Pero en los


viejos tiempos, era técnicamente posible crear una variable mediante una mera

asignación del valor sin usar let. Esto todavía funciona ahora si no

ponemos use strictnuestros scripts para mantener la compatibilidad con los

scripts antiguos.

// note: no "use strict" in this example

num = 5; // the variable "num" is created if it didn't exist

alert(num); // 5

Esta es una mala práctica y causaría un error en modo estricto:

"use strict";
num = 5; // error: num is not defined

Constantes

Para declarar una variable constante (que no cambia), use en constlugar

de let:

const myBirthday = '18.04.1982';

Las variables declaradas mediante constse denominan “constantes”. No se

pueden reasignar. Un intento de hacerlo provocaría un error:

const myBirthday = '18.04.1982';

myBirthday = '01.01.2001'; // error, can't reassign the


constant!

Cuando un programador está seguro de que una variable nunca cambiará,


puede declararla con constpara garantizar y comunicar claramente ese hecho a

todos.

Constantes mayúsculas

Existe una práctica generalizada de utilizar constantes como alias para valores
difíciles de recordar que se conocen antes de la ejecución.

Estas constantes se nombran con letras mayúsculas y guiones bajos.

Por ejemplo, creemos constantes para los colores en el llamado formato "web"
(hexadecimal):

const COLOR_RED = "#F00";


const COLOR_GREEN = "#0F0";

const COLOR_BLUE = "#00F";

const COLOR_ORANGE = "#FF7F00";

// ...when we need to pick a color

let color = COLOR_ORANGE;

alert(color); // #FF7F00

Beneficios:

 COLOR_ORANGEes mucho más fácil de recordar que "#FF7F00".

 Es mucho más fácil escribir mal "#FF7F00"que COLOR_ORANGE.

 Al leer el código, COLOR_ORANGEes mucho más significativo que #FF7F00.

¿Cuándo deberíamos usar mayúsculas para una constante y cuándo deberíamos


nombrarla normalmente? Dejémoslo claro.

Ser una "constante" simplemente significa que el valor de una variable nunca
cambia. Pero hay constantes que se conocen antes de la ejecución (como un
valor hexadecimal para rojo) y hay constantes que se calculan en tiempo de
ejecución, durante la ejecución, pero que no cambian después de su asignación
inicial.

Por ejemplo:

const pageLoadTime = /* time taken by a webpage to load */;

El valor de pageLoadTimeno se conoce antes de la carga de la página, por lo

que se denomina normalmente. Pero sigue siendo una constante porque no


cambia después de la asignación.
En otras palabras, las constantes con nombre en mayúsculas solo se utilizan
como alias para valores "codificados".

Nombra las cosas bien

Hablando de variables, hay una cosa más extremadamente importante.

El nombre de una variable debe tener un significado claro y obvio, que describa
los datos que almacena.

La denominación de variables es una de las habilidades más importantes y


complejas de la programación. Un vistazo rápido a los nombres de las variables
puede revelar qué código fue escrito por un principiante versus un
desarrollador experimentado.

En un proyecto real, la mayor parte del tiempo se dedica a modificar y ampliar


una base de código existente en lugar de escribir algo completamente separado
desde cero. Cuando volvemos a algún código después de hacer otra cosa por
un tiempo, es mucho más fácil encontrar información que esté bien
etiquetada. O, en otras palabras, cuando las variables tienen buen nombre.

Dedique tiempo a pensar en el nombre correcto para una variable antes de


declararla. Hacerlo le compensará generosamente.

Algunas reglas que conviene seguir son:

 Utilice nombres legibles por humanos como userNameo shoppingCart.

 Manténgase alejado de las abreviaturas o nombres cortos como a, b, c, a

menos que realmente sepa lo que está haciendo.

 Haga nombres lo más descriptivos y concisos. Ejemplos de malos


nombres son datay value. Tales nombres no dicen nada. Solo está bien usarlos
si el contexto del código hace que sea excepcionalmente obvio a qué datos o
valor hace referencia la variable.

 Acuerde los términos dentro de su equipo y en su propia mente. Si un


visitante del sitio se llama "usuario", entonces debemos nombrar las variables

relacionadas currentUsero en newUserlugar

de currentVisitoro newManInTown.

¿Suena simple? De hecho lo es, pero crear nombres de variables descriptivos y


concisos en la práctica no lo es. Ve a por ello.

¿Reutilizar o crear?

Y la última nota. Hay algunos programadores perezosos que, en lugar de


declarar nuevas variables, tienden a reutilizar las existentes.

Como resultado, sus variables son como cajas en las que la gente arroja
diferentes cosas sin cambiar sus pegatinas. ¿Qué hay dentro de la caja
ahora? ¿Quién sabe? Tenemos que acercarnos y comprobarlo.

Estos programadores ahorran un poco en la declaración de variables pero


pierden diez veces más en la depuración.

Una variable adicional es buena, no mala.

Los navegadores y minificadores de JavaScript modernos optimizan el código lo


suficientemente bien, por lo que no crearán problemas de rendimiento. El uso
de diferentes variables para diferentes valores puede incluso ayudar al motor a
optimizar su código.

Resumen

Podemos declarar variables para almacenar datos mediante el uso de


los var, leto constpalabras clave.
 let - es una declaración de variable moderna.

 var- es una declaración de variable de la vieja escuela. Normalmente no

lo usamos en absoluto, pero cubriremos las diferencias sutiles leten el

capítulo La vieja "var" , por si las necesita.

 const- es como let, pero el valor de la variable no se puede cambiar.

Las variables deben nombrarse de manera que nos permitan comprender


fácilmente su contenido.
5. Tipos de datos

Un valor en JavaScript es siempre de un tipo determinado. Por ejemplo, una


cadena o un número.

Hay ocho tipos de datos básicos en JavaScript. Aquí los cubriremos en general y


en los próximos capítulos hablaremos de cada uno de ellos en detalle.

Podemos poner cualquier tipo en una variable. Por ejemplo, una variable puede
ser en un momento una cadena y luego almacenar un número:

// no error

let message = "hello";

message = 123456;

Los lenguajes de programación que permiten tales cosas, como JavaScript, se


denominan "tipados dinámicamente", lo que significa que existen tipos de
datos, pero las variables no están vinculadas a ninguno de ellos.

Número

let n = 123;

n = 12.345;

El tipo de número representa números enteros y de coma flotante.

Hay muchas operaciones para números, por ejemplo, multiplicación *,

división /, suma +, resta -, etc.

Además de los números regulares, no son los llamados “valores numéricos


especiales”, que también pertenecen a este tipo de datos: Infinity, -

Infinityy NaN.
 Infinityrepresenta el infinito matemático ∞. Es un valor especial que es

mayor que cualquier número.

Podemos obtenerlo como resultado de la división por cero:

alert( 1 / 0 ); // Infinity

O simplemente haga referencia a él directamente:

alert( Infinity ); // Infinity

 NaNrepresenta un error de cálculo. Es el resultado de una operación

matemática incorrecta o indefinida, por ejemplo:

alert( "not a number" / 2 ); // NaN, such division is erroneous

NaNes pegajoso. Cualquier otra operación sobre NaNdevoluciones NaN:

alert( "not a number" / 2 + 5 ); // NaN

Entonces, si hay un NaNlugar en una expresión matemática, se propaga al

resultado completo.

Las operaciones matemáticas son seguras

Hacer matemáticas es "seguro" en JavaScript. Podemos hacer cualquier cosa:


dividir por cero, tratar cadenas no numéricas como números, etc.

El guión nunca se detendrá con un error fatal ("morir"). En el peor de los casos,
obtendremos NaNel resultado.

Los valores numéricos especiales pertenecen formalmente al tipo "número". Por


supuesto, no son números en el sentido común de esta palabra.

Veremos más sobre cómo trabajar con números en el capítulo Números .

Empezando
En JavaScript, el tipo "número" no puede representar valores enteros mayores
que (eso es ) o menores que para los negativos. Es una limitación técnica

provocada por su representación interna.(253-1)9007199254740991-(253-1)

Para la mayoría de los propósitos, eso es suficiente, pero a veces necesitamos


números realmente grandes, por ejemplo, para criptografía o marcas de tiempo
con precisión de microsegundos.

BigInt type se agregó recientemente al lenguaje para representar números

enteros de longitud arbitraria.

Un BigIntvalor se crea agregando nal final de un número entero:

// the "n" at the end means it's a BigInt

const bigInt = 1234567890123456789012345678901234567890n;

Como los BigIntnúmeros rara vez se necesitan, no los cubrimos aquí, pero les

dedicamos un capítulo separado BigInt . Léalo cuando necesite números tan


grandes.

Problemas de compatibilidad

En este momento, BigIntse admite en Firefox / Chrome / Edge, pero no en

Safari / IE.

Cuerda

Una cadena en JavaScript debe estar entre comillas.

let str = "Hello";

let str2 = 'Single quotes are ok too';

let phrase = `can embed another ${str}`;

En JavaScript, hay 3 tipos de citas.


1. Las comillas dobles: "Hello".

2. Las comillas simples: 'Hello'.

3. Acentos abiertos: `Hello`.

Las comillas simples y dobles son comillas "simples". Prácticamente no hay


diferencia entre ellos en JavaScript.

Las comillas inversas son comillas de "funcionalidad ampliada". Nos permiten


incrustar variables y expresiones en una cadena envolviéndolas ${…}, por

ejemplo:

let name = "John";

// embed a variable

alert( `Hello, ${name}!` ); // Hello, John!

// embed an expression

alert( `the result is ${1 + 2}` ); // the result is 3

La expresión dentro ${…}se evalúa y el resultado se convierte en parte de la

cadena. Podemos poner cualquier cosa allí: una variable como nameo una

expresión aritmética como 1 + 2o algo más complejo.

Tenga en cuenta que esto solo se puede hacer con comillas invertidas. ¡Otras
citas no tienen esta función de incrustación!

alert( "the result is ${1 + 2}" ); // the result is ${1 + 2}


(double quotes do nothing)

Cubriremos las cadenas más a fondo en el capítulo Cuerdas .


No hay ningún tipo de carácter .

En algunos idiomas, hay un tipo de "carácter" especial para un solo carácter. Por


ejemplo, en el lenguaje C y en Java se llama "char".

En JavaScript, no existe tal tipo. Sólo hay un tipo de: string. Una cadena puede

constar de cero caracteres (estar vacía), un carácter o muchos de ellos.

Booleano (tipo lógico)

El tipo booleano tiene solo dos valores: truey false.

Este tipo se usa comúnmente para almacenar valores de sí / no: truesignifica

"sí, correcto" y falsesignifica "no, incorrecto".

Por ejemplo:

let nameFieldChecked = true; // yes, name field is checked

let ageFieldChecked = false; // no, age field is not checked

Los valores booleanos también se obtienen como resultado de comparaciones:

let isGreater = 4 > 1;

alert( isGreater ); // true (the comparison result is "yes")

Cubriremos los valores booleanos con más profundidad en el


capítulo Operadores lógicos .

El valor "nulo"

El nullvalor especial no pertenece a ninguno de los tipos descritos

anteriormente.

Forma un tipo separado propio que contiene solo el nullvalor:


let age = null;

En JavaScript, nullno es una "referencia a un objeto inexistente" o un "puntero

nulo" como en algunos otros idiomas.

Es solo un valor especial que representa "nada", "vacío" o "valor desconocido".

El código anterior establece que agese desconoce.

El valor "indefinido"

El valor especial undefinedtambién se distingue. Hace un tipo propio,

como null.

El significado de undefinedes "valor no asignado".

Si se declara una variable, pero no se asigna, entonces su valor es undefined:

let age;

alert(age); // shows "undefined"

Técnicamente, es posible asignar explícitamente undefineda una variable:

let age = 100;

// change the value to undefined

age = undefined;

alert(age); // "undefined"
… Pero no recomendamos hacer eso. Normalmente, se utiliza nullpara asignar

un valor "vacío" o "desconocido" a una variable, mientras que undefinedse

reserva como valor inicial predeterminado para cosas no asignadas.

Objetos y símbolos

El objecttipo es especial.

Todos los demás tipos se denominan "primitivos" porque sus valores pueden
contener solo una cosa (ya sea una cadena, un número o lo que sea). Por el
contrario, los objetos se utilizan para almacenar colecciones de datos y
entidades más complejas.

Siendo así de importantes, los objetos merecen un tratamiento especial. Nos


ocuparemos de ellos más adelante en el capítulo Objetos , después de que
aprendamos más sobre las primitivas.

El symboltipo se utiliza para crear identificadores únicos para objetos. Tenemos

que mencionarlo aquí en aras de la integridad, pero también posponer los


detalles hasta que sepamos los objetos.

El tipo de operador

El typeofoperador devuelve el tipo de argumento. Es útil cuando queremos

procesar valores de diferentes tipos de manera diferente o simplemente


queremos hacer una verificación rápida.

Admite dos formas de sintaxis:

1. Como operador: typeof x.

2. En función: typeof(x).

En otras palabras, funciona con paréntesis o sin ellos. El resultado es el mismo.


La llamada a typeof xdevuelve una cadena con el nombre del tipo:

typeof undefined // "undefined"

typeof 0 // "number"

typeof 10n // "bigint"

typeof true // "boolean"

typeof "foo" // "string"

typeof Symbol("id") // "symbol"

typeof Math // "object" (1)

typeof null // "object" (2)

typeof alert // "function" (3)

Las últimas tres líneas pueden necesitar una explicación adicional:

1. Mathes un objeto integrado que proporciona operaciones

matemáticas. Lo aprenderemos en el capítulo Números . Aquí, sirve solo como


un ejemplo de un objeto.
2. El resultado de typeof nulles "object". Ese es un error

de typeofcomportamiento reconocido oficialmente , que proviene de los

primeros días de JavaScript y se mantiene por


compatibilidad. Definitivamente, nullno es un objeto. Es un valor especial con

un tipo separado propio.

3. El resultado de typeof alertes "function", porque alertes una

función. Estudiaremos las funciones en los siguientes capítulos, donde también


veremos que no hay un tipo de "función" especial en JavaScript. Las funciones

pertenecen al tipo de objeto. Pero los typeoftrata de manera diferente,

regresando "function". Eso también proviene de los primeros días de

JavaScript. Técnicamente, tal comportamiento no es correcto, pero puede ser


conveniente en la práctica.

Resumen

Hay 8 tipos de datos básicos en JavaScript.

 numberpara números de cualquier tipo: entero o de punto flotante, los

enteros están limitados por .±(253-1)

 bigint es para números enteros de longitud arbitraria.

 stringpara cuerdas. Una cadena puede tener cero o más caracteres, no

hay un tipo de carácter único independiente.

 booleanpara true/ false.

 nullpara valores desconocidos: un tipo independiente que tiene un solo

valor null.

 undefinedpara valores no asignados: un tipo independiente que tiene

un solo valor undefined.
 object para estructuras de datos más complejas.

 symbol para identificadores únicos.

El typeofoperador nos permite ver qué tipo está almacenado en una variable.

 Dos formas: typeof xo typeof(x).

 Devuelve una cadena con el nombre del tipo, como "string".

 Para nulldevoluciones "object": este es un error en el idioma, en

realidad no es un objeto.
6. Interacción: alerta, aviso, confirmación

Como vamos a estar utilizando el navegador como nuestro entorno de


demostración, vamos a ver un par de funciones para interactuar con el

usuario: alert, prompty confirm.

alerta

Este ya lo hemos visto. Muestra un mensaje y espera a que el usuario presione


“OK”.

Por ejemplo:

alert("Hello");

La mini ventana con el mensaje se llama ventana modal . La palabra “modal”


significa que el visitante no puede interactuar con el resto de la página,
presionar otros botones, etc., hasta que no se haya ocupado de la ventana. En
este caso, hasta que presionen "Aceptar".

rápido

La función promptacepta dos argumentos:

result = prompt(title, [default]);

Muestra una ventana modal con un mensaje de texto, un campo de entrada


para el visitante y los botones Aceptar / Cancelar.

title

El texto que se mostrará al visitante.

default
Un segundo parámetro opcional, el valor inicial para el campo de
entrada.

Los corchetes en la sintaxis [...]

Los corchetes defaulten la sintaxis anterior indican que el parámetro es

opcional, no obligatorio.

El visitante puede escribir algo en el campo de entrada del mensaje y presionar


OK. Luego obtenemos ese texto en formato result. O pueden cancelar la

entrada presionando Cancelar o presionando la  Esc tecla, luego

obtenemos nullcomo result.

La llamada a promptdevuelve el texto del campo de entrada o nullsi se canceló

la entrada.

Por ejemplo:

let age = prompt('How old are you?', 100);

alert(`You are ${age} years old!`); // You are 100 years old!

En IE: proporcione siempre un default

El segundo parámetro es opcional, pero si no lo proporcionamos, Internet

Explorer insertará el texto "undefined"en el mensaje.

Ejecute este código en Internet Explorer para ver:

let test = prompt("Test");

Por lo tanto, para que las indicaciones se vean bien en IE, recomendamos
proporcionar siempre el segundo argumento:
let test = prompt("Test", ''); // <-- for IE

confirmar

La sintaxis:

result = confirm(question);

La función confirmmuestra una ventana modal con questionay dos botones:

Aceptar y Cancelar.

El resultado es truesi se presiona OK y en falsecaso contrario.

Por ejemplo:

let isBoss = confirm("Are you the boss?");

alert( isBoss ); // true if OK is pressed

Resumen

Cubrimos 3 funciones específicas del navegador para interactuar con los


visitantes:

alert

muestra un mensaje.

prompt

muestra un mensaje pidiendo al usuario que ingrese texto. Devuelve el

texto o, si el botón Cancelar o  Esc se hace clic en, null.

confirm
muestra un mensaje y espera a que el usuario presione “Aceptar” o

“Cancelar”. Vuelve truepara Aceptar y falseCancelar /  Esc .

Todos estos métodos son modales: pausan la ejecución del script y no permiten
que el visitante interactúe con el resto de la página hasta que se cierre la
ventana.

Hay dos limitaciones compartidas por todos los métodos anteriores:

1. La ubicación exacta de la ventana modal la determina el navegador. Por


lo general, está en el centro.

2. El aspecto exacto de la ventana también depende del navegador. No


podemos modificarlo.

Ese es el precio de la sencillez. Hay otras formas de mostrar ventanas más


agradables y una interacción más rica con el visitante, pero si las “campanas y
silbidos” no importan mucho, estos métodos funcionan bien.
7. Conversiones de tipo

La mayoría de las veces, los operadores y las funciones convierten


automáticamente los valores que se les asignan al tipo correcto.

Por ejemplo, alertconvierte automáticamente cualquier valor en una cadena

para mostrarlo. Las operaciones matemáticas convierten valores en números.

También hay casos en los que necesitamos convertir explícitamente un valor al


tipo esperado.

Sin hablar de objetos todavía

En este capítulo, no cubriremos objetos. Por ahora solo hablaremos de


primitivas.

Más adelante, después de aprender acerca de los objetos, en el


capítulo Conversión de objeto a primitivo veremos cómo encajan los objetos.

Conversión de cadenas

La conversión de cadenas ocurre cuando necesitamos la forma de cadena de un


valor.

Por ejemplo, lo alert(value)hace para mostrar el valor.

También podemos llamar a la String(value)función para convertir un valor en

una cadena:

let value = true;

alert(typeof value); // boolean

value = String(value); // now value is a string "true"


alert(typeof value); // string

La conversión de cadenas es más obvia. A se falseconvierte "false",

se nullconvierte "null", etc.

Conversión numérica

La conversión numérica ocurre en funciones y expresiones matemáticas


automáticamente.

Por ejemplo, cuando la división /se aplica a no números:

alert( "6" / "2" ); // 3, strings are converted to numbers

Podemos usar la Number(value)función para convertir explícitamente valuea

en un número:

let str = "123";

alert(typeof str); // string

let num = Number(str); // becomes a number 123

alert(typeof num); // number

La conversión explícita generalmente se requiere cuando leemos un valor de


una fuente basada en cadenas como un formulario de texto, pero esperamos
que se ingrese un número.

Si la cadena no es un número válido, el resultado de dicha conversión


es NaN. Por ejemplo:

let age = Number("an arbitrary string instead of a number");


alert(age); // NaN, conversion failed

Reglas de conversión numérica:

Valor Se convierte en ...

undefined NaN

null 0

true and fals
1 y 0
e

Se eliminan los espacios en blanco del principio y del


final. Si la cadena restante está vacía, el resultado es 0. De
string
lo contrario, el número se "lee" de la cadena. Un error
da NaN.

Ejemplos:

alert( Number(" 123 ") ); // 123

alert( Number("123z") ); // NaN (error reading a number at


"z")

alert( Number(true) ); // 1

alert( Number(false) ); // 0

Tenga en cuenta que nully se undefinedcomporta de manera diferente aquí:

se nullconvierte en cero mientras se undefinedconvierte en NaN.

La mayoría de los operadores matemáticos también realizan dicha conversión,


lo veremos en el próximo capítulo.

Conversión booleana
La conversión booleana es la más simple.

Sucede en operaciones lógicas (más adelante veremos pruebas de condición y


otras cosas similares) pero también se puede realizar explícitamente con una

llamada a Boolean(value).

La regla de conversión:

 Los valores que son intuitivamente “vacío”, como 0, una cadena

vacía, null, undefined, y NaN, se hacen false.

 Se vuelven otros valores true.

Por ejemplo:

alert( Boolean(1) ); // true

alert( Boolean(0) ); // false

alert( Boolean("hello") ); // true

alert( Boolean("") ); // false

Tenga en cuenta: la cadena con cero "0"estrue

Algunos lenguajes (concretamente PHP) tratan "0"como false. Pero en

JavaScript, una cadena no vacía es siempre true.

alert( Boolean("0") ); // true

alert( Boolean(" ") ); // spaces, also true (any non-empty


string is true)

Resumen
Las tres conversiones de tipo más utilizadas son cadena, numeración y
booleana.

String Conversion- Ocurre cuando mostramos algo. Se puede realizar

con String(value). La conversión a cadena suele ser obvia para valores

primitivos.

Numeric Conversion- Ocurre en operaciones matemáticas. Se puede realizar

con Number(value).

La conversión sigue las reglas:

Valor Se convierte en ...

undefined NaN

null 0

true / fals
1 / 0
e

La cadena se lee "tal cual", los espacios en blanco de ambos lados se


string
ignoran. Una cadena vacía se convierte en 0. Un error da NaN.

Boolean Conversion- Ocurre en operaciones lógicas. Se puede realizar

con Boolean(value).

Sigue las reglas:

Valor Se convierte en ...

0, null, undefined, NaN,"" false

cualquier otro valor true

La mayoría de estas reglas son fáciles de entender y memorizar. Las notables


excepciones en las que las personas suelen cometer errores son:
 undefinedes NaNcomo un número, no 0.

 "0"y las cadenas de solo espacio como " "son verdaderas como

booleanas.

Los objetos no están cubiertos aquí. Volveremos a ellos más adelante en el


capítulo Conversión de objeto a primitivo que se dedica exclusivamente a
objetos después de que aprendamos cosas más básicas sobre JavaScript.
8. Operadores básicos, matemáticas

Conocemos a muchos operadores de la escuela. Son cosas como sumas +,

multiplicaciones *, restas -, etc.

En este capítulo, comenzaremos con operadores simples, luego nos


concentraremos en aspectos específicos de JavaScript, que no se tratan en la
aritmética escolar.

Términos: "unario", "binario", "operando"

Antes de continuar, comprendamos alguna terminología común.

 Un operando : es a lo que se aplican los operadores. Por ejemplo, en la

multiplicación de 5 * 2hay dos operandos: el operando izquierdo es 5y el

operando derecho es 2. A veces, la gente llama a estos "argumentos" en lugar

de "operandos".

 Un operador es unario si tiene un solo operando. Por ejemplo, la

negación unaria -invierte el signo de un número:

 let x = 1;

 x = -x;

alert( x ); // -1, unary negation was applied

 Un operador es binario si tiene dos operandos. El mismo signo negativo


también existe en forma binaria:

 let x = 1, y = 3;

alert( y - x ); // 2, binary minus subtracts values


Formalmente, en los ejemplos anteriores tenemos dos operadores diferentes
que comparten el mismo símbolo: el operador de negación, un operador unario
que invierte el signo, y el operador de resta, un operador binario que resta un
número de otro.

Matemáticas

Se admiten las siguientes operaciones matemáticas:

 Además +,

 la resta -,

 multiplicación *,

 división /,

 Resto %,

 Exponenciación **.

Los cuatro primeros son sencillos, mientras que %y **necesitan unas palabras

sobre ellos.

Recordatorio %

El operador restante %, a pesar de su apariencia, no está relacionado con los

porcentajes.

El resultado de a % bes el resto de la división entera de apor b.

Por ejemplo:

alert( 5 % 2 ); // 1, a remainder of 5 divided by 2

alert( 8 % 3 ); // 2, a remainder of 8 divided by 3


Exponenciación **

El operador de exponenciación se a ** bmultiplica apor sí mismo b.

Por ejemplo:

alert( 2 ** 2 ); // 4 (2 multiplied by itself 2 times)

alert( 2 ** 3 ); // 8 (2 * 2 * 2, 3 times)

alert( 2 ** 4 ); // 16 (2 * 2 * 2 * 2, 4 times)

Matemáticamente, la potenciación se define también para números no

enteros. Por ejemplo, una raíz cuadrada es una exponenciación por 1/2:

alert( 4 ** (1/2) ); // 2 (power of 1/2 is the same as a square


root)

alert( 8 ** (1/3) ); // 2 (power of 1/3 is the same as a cubic


root)

Concatenación de cadenas con binario +

Conozcamos las características de los operadores de JavaScript que van más allá
de la aritmética escolar.

Normalmente, el operador más +suma números.

Pero, si el binario +se aplica a cadenas, las fusiona (las concatena):

let s = "my" + "string";

alert(s); // mystring

Tenga en cuenta que si alguno de los operandos es una cadena, el otro también
se convierte en una cadena.

Por ejemplo:
alert( '1' + 2 ); // "12"

alert( 2 + '1' ); // "21"

Mira, no importa si el primer operando es una cadena o el segundo.

Aquí hay un ejemplo más complejo:

alert(2 + 2 + '1' ); // "41" and not "221"

Aquí, los operadores trabajan uno tras otro. El primero +suma dos números, por

lo que devuelve 4, luego el siguiente le +agrega la cadena 1, por lo que es

como 4 + '1' = 41.

El binario +es el único operador que admite cadenas de esa manera. Otros

operadores aritméticos funcionan solo con números y siempre convierten sus


operandos en números.

Aquí está la demostración para restar y dividir:

alert( 6 - '2' ); // 4, converts '2' to a number

alert( '6' / '2' ); // 3, converts both operands to numbers

Conversión numérica, unaria +

El plus +existe en dos formas: la forma binaria que usamos anteriormente y la

forma unaria.

El unario más o, en otras palabras, el operador más +aplicado a un solo valor, no

hace nada a los números. Pero si el operando no es un número, el unario más lo


convierte en un número.

Por ejemplo:

// No effect on numbers
let x = 1;

alert( +x ); // 1

let y = -2;

alert( +y ); // -2

// Converts non-numbers

alert( +true ); // 1

alert( +"" ); // 0

Realmente hace lo mismo que Number(...), pero es más corto.

La necesidad de convertir cadenas en números surge con mucha frecuencia. Por


ejemplo, si obtenemos valores de campos de formulario HTML, generalmente
son cadenas. ¿Y si queremos sumarlos?

El binario más los agregaría como cadenas:

let apples = "2";

let oranges = "3";

alert( apples + oranges ); // "23", the binary plus concatenates


strings

Si queremos tratarlos como números, necesitamos convertirlos y luego


sumarlos:

let apples = "2";


let oranges = "3";

// both values converted to numbers before the binary plus

alert( +apples + +oranges ); // 5

// the longer variant

// alert( Number(apples) + Number(oranges) ); // 5

Desde el punto de vista de un matemático, la abundancia de ventajas puede


parecer extraña. Pero desde el punto de vista de un programador, no hay nada
especial: las ventajas unarias se aplican primero, convierten cadenas en números
y luego el signo más binario las suma.

¿Por qué se aplican las ventajas unarias a los valores antes que a los
binarios? Como veremos, eso se debe a su mayor precedencia .

Precedencia del operador

Si una expresión tiene más de un operador, el orden de ejecución se define por


su precedencia o, en otras palabras, el orden de prioridad predeterminado de
los operadores.

De la escuela, todos sabemos que la multiplicación en la expresión 1 + 2 *

2debe calcularse antes de la suma. Esa es exactamente la cuestión de la

precedencia. Se dice que la multiplicación tiene mayor precedencia que la suma.

Los paréntesis anulan cualquier precedencia, por lo que si no estamos


satisfechos con el orden predeterminado, podemos usarlos para cambiarlo. Por

ejemplo, escribe (1 + 2) * 2.
Hay muchos operadores en JavaScript. Cada operador tiene un número de
precedencia correspondiente. El que tiene el número más grande se ejecuta
primero. Si la precedencia es la misma, el orden de ejecución es de izquierda a
derecha.

Aquí hay un extracto de la tabla de precedencia (no necesita recordar esto, pero


tenga en cuenta que los operadores unarios son más altos que los binarios
correspondientes):

Precedencia Nombre Firmar

... ... ...

17 unario más +

17 negación unaria -

dieciséis exponenciación **

15 multiplicación *

15 división /

13 adición +

13 sustracción -

... ... ...

3 asignación =

... ... ...


Como podemos ver, el “más unario” tiene una prioridad 17mayor que la 13de

“suma” (más binario). Por eso, en la expresión "+apples + +oranges", las

ventajas unarias funcionan antes de la suma.

Asignación

Observemos que una asignación =también es un operador. Aparece en la tabla

de precedencia con la prioridad muy baja de 3.

Por eso, cuando asignamos una variable, como x = 2 * 2 + 1, los cálculos se

hacen primero y luego =se evalúa, almacenando el resultado en x.

let x = 2 * 2 + 1;

alert( x ); // 5

Asignación = devuelve un valor

El hecho de =ser un operador, no un constructo de lenguaje “mágico”, tiene una

implicación interesante.

La mayoría de los operadores en JavaScript devuelven un valor. Eso es obvio

para +y -, pero también cierto para =.

La llamada x = valueescribe el valueen x y luego lo devuelve .

Aquí hay una demostración que usa una asignación como parte de una
expresión más compleja:

let a = 1;

let b = 2;
let c = 3 - (a = b + 1);

alert( a ); // 3

alert( c ); // 0

En el ejemplo anterior, el resultado de la expresión (a = b + 1)es el valor que

se le asignó a(es decir 3). Luego se utiliza para evaluaciones adicionales.

Código gracioso, ¿no? Deberíamos entender cómo funciona, porque a veces lo


vemos en bibliotecas de JavaScript.

Aunque, por favor no escriba el código así. Estos trucos definitivamente no


hacen que el código sea más claro o legible.

Encadenamiento de asignaciones

Otra característica interesante es la capacidad de encadenar asignaciones:

let a, b, c;

a = b = c = 2 + 2;

alert( a ); // 4

alert( b ); // 4

alert( c ); // 4

Las asignaciones encadenadas se evalúan de derecha a izquierda. En primer


lugar, la expresión más a la derecha 2 + 2se evalúa y se le asigna a las variables

de la izquierda: c, by a. Al final, todas las variables comparten un solo valor.


Una vez más, por motivos de legibilidad, es mejor dividir dicho código en pocas
líneas:

c = 2 + 2;

b = c;

a = c;

Eso es más fácil de leer, especialmente cuando se escanea el código


rápidamente.

Modificar en el lugar

A menudo necesitamos aplicar un operador a una variable y almacenar el nuevo


resultado en esa misma variable.

Por ejemplo:

let n = 2;

n = n + 5;

n = n * 2;

Esta notación se puede abreviar utilizando los operadores +=y *=:

let n = 2;

n += 5; // now n = 7 (same as n = n + 5)

n *= 2; // now n = 14 (same as n = n * 2)

alert( n ); // 14

Breve “modificar y asignar-” existe operadores para todos los operadores

aritméticos y bit a bit: /=, -=, etc.


Dichos operadores tienen la misma precedencia que una asignación normal, por
lo que se ejecutan después de la mayoría de los demás cálculos:

let n = 2;

n *= 3 + 5;

alert( n ); // 16 (right part evaluated first, same as n *= 8)

Incremento / decremento

Aumentar o disminuir un número en uno es una de las operaciones numéricas


más comunes.

Entonces, hay operadores especiales para ello:

 Incremento ++ aumenta una variable en 1:

 let counter = 2;

 counter++; // works the same as counter = counter +


1, but is shorter

alert( counter ); // 3

 El decremento -- disminuye una variable en 1:

 let counter = 2;

 counter--; // works the same as counter = counter -


1, but is shorter

alert( counter ); // 1

Importante:
El incremento / decremento solo se puede aplicar a variables. Intentar usarlo en

un valor como 5++dará un error.

Los operadores ++y --se pueden colocar antes o después de una variable.

 Cuando el operador va después de la variable, es en “forma de


sufijo”: counter++.

 La “forma de prefijo” es cuando el operador va delante de la variable: +

+counter.

Ambas declaraciones hacen lo mismo: aumentar counteren 1.

¿Hay alguna diferencia? Sí, pero solo podemos verlo si usamos el valor devuelto

de ++/--.

Aclaremos. Como sabemos, todos los operadores devuelven un valor. El


incremento / decremento no es una excepción. La forma de prefijo devuelve el
nuevo valor, mientras que la forma de sufijo devuelve el valor anterior (antes de
incrementar / disminuir).

Para ver la diferencia, aquí tienes un ejemplo:

let counter = 1;

let a = ++counter; // (*)

alert(a); // 2

En la línea (*), la forma de prefijo++counter aumenta countery devuelve el

nuevo valor 2,. Entonces, los alertespectáculos 2.

Ahora, usemos la forma de sufijo:

let counter = 1;
let a = counter++; // (*) changed ++counter to counter++

alert(a); // 1

En la línea (*), el postfix forma counter++también incrementos counterpero

vuelve el viejo valor (antes de incremento). Entonces, los alertespectáculos 1.

Para resumir:

 Si no se usa el resultado de incremento / decremento, no hay diferencia


en qué forma usar:

 let counter = 0;

 counter++;

 ++counter;

alert( counter ); // 2, the lines above did the same

 Si queremos aumentar un valor y usar inmediatamente el resultado del


operador, necesitamos la forma de prefijo:

 let counter = 0;

alert( ++counter ); // 1

 Si deseamos incrementar un valor pero usamos su valor anterior,


necesitamos la forma de sufijo:

 let counter = 0;

alert( counter++ ); // 0

Incremento / decremento entre otros operadores


Los operadores también ++/--se pueden usar dentro de expresiones. Su

precedencia es mayor que la de la mayoría de las otras operaciones aritméticas.

Por ejemplo:

let counter = 1;

alert( 2 * ++counter ); // 4

Comparar con:

let counter = 1;

alert( 2 * counter++ ); // 2, because counter++ returns the


"old" value

Aunque técnicamente está bien, esta notación suele hacer que el código sea
menos legible. Una línea hace varias cosas, no es bueno.

Mientras lee el código, un escaneo ocular “vertical” rápido puede fácilmente


pasar por alto algo como counter++y no será obvio que la variable aumentó.

Aconsejamos un estilo de "una línea - una acción":

let counter = 1;

alert( 2 * counter );

counter++;

Operadores bit a bit

Los operadores bit a bit tratan los argumentos como números enteros de 32
bits y trabajan en el nivel de su representación binaria.

Estos operadores no son específicos de JavaScript. Son compatibles con la


mayoría de los lenguajes de programación.
Estos operadores se usan muy raramente, cuando necesitamos jugar con
números en el nivel más bajo (bit a bit). No necesitaremos estos operadores en
el corto plazo, ya que el desarrollo web tiene poco uso de ellos, pero en algunas
áreas especiales, como la criptografía, son útiles. Puede leer
el capítulo Operadores bit a bit sobre MDN cuando surja una necesidad.

Coma

El operador de coma ,es uno de los operadores más raros e inusuales. A veces,

se usa para escribir código más corto, por lo que necesitamos saberlo para
comprender qué está pasando.

El operador de coma nos permite evaluar varias expresiones, dividiéndolas con

una coma ,. Cada uno de ellos se evalúa pero solo se devuelve el resultado del

último.

Por ejemplo:

let a = (1 + 2, 3 + 4);

alert( a ); // 7 (the result of 3 + 4)

Aquí, 1 + 2se evalúa la primera expresión y se desecha su resultado. Luego, 3

+ 4se evalúa y se devuelve como resultado.

La coma tiene una precedencia muy baja

Tenga en cuenta que el operador de coma tiene una precedencia muy baja,

inferior a =, por lo que los paréntesis son importantes en el ejemplo anterior.

Sin ellos: a = 1 + 2, 3 + 4evalúa +primero, sumando los números a = 3, 7,

luego el operador de asignación =asigna a = 3y el resto se ignora. Es como (a

= 1 + 2), 3 + 4.
¿Por qué necesitamos un operador que descarte todo excepto la última
expresión?

A veces, la gente lo usa en construcciones más complejas para poner varias


acciones en una línea.

Por ejemplo:

// three operations in one line

for (a = 1, b = 3, c = a * b; a < 10; a++) {

...

Estos trucos se utilizan en muchos marcos de JavaScript. Por eso los


mencionamos. Pero generalmente no mejoran la legibilidad del código, por lo
que debemos pensarlo bien antes de usarlos.

Comparaciones

Conocemos muchos operadores de comparación de las matemáticas.

En JavaScript están escritos así:

 Mayor / menor que: a > b, a < b.

 Mayor / menor que o igual: a >= b, a <= b.

 Es igual a:, a == btenga en cuenta que el signo de doble

igualdad ==significa la prueba de igualdad, mientras que uno solo a =

bsignifica una asignación.

 No es igual. En matemáticas, la notación es ≠, pero en JavaScript se

escribe como a != b.
En este artículo aprenderemos más sobre los diferentes tipos de comparaciones,
cómo las hace JavaScript, incluidas peculiaridades importantes.

Al final, encontrará una buena receta para evitar problemas relacionados con las
"peculiaridades de JavaScript".

Booleano es el resultado

Todos los operadores de comparación devuelven un valor booleano:

 true - significa "sí", "correcto" o "la verdad".

 false - significa "no", "incorrecto" o "no es la verdad".

Por ejemplo:

alert( 2 > 1 ); // true (correct)

alert( 2 == 1 ); // false (wrong)

alert( 2 != 1 ); // true (correct)

Se puede asignar un resultado de comparación a una variable, como cualquier


valor:

let result = 5 > 4; // assign the result of the comparison

alert( result ); // true

Comparación de cadenas

Para ver si una cadena es mayor que otra, JavaScript utiliza el llamado
"diccionario" o orden "lexicográfico".

En otras palabras, las cadenas se comparan letra por letra.

Por ejemplo:
alert( 'Z' > 'A' ); // true

alert( 'Glow' > 'Glee' ); // true

alert( 'Bee' > 'Be' ); // true

El algoritmo para comparar dos cadenas es simple:

1. Compare el primer carácter de ambas cadenas.

2. Si el primer carácter de la primera cadena es mayor (o menor) que el de


la otra cadena, entonces la primera cadena es mayor (o menor) que la
segunda. Hemos terminado.

3. De lo contrario, si los primeros caracteres de ambas cadenas son iguales,


compare los segundos caracteres de la misma manera.

4. Repita hasta el final de cualquiera de las cuerdas.

5. Si ambas cadenas terminan con la misma longitud, entonces son


iguales. De lo contrario, la cuerda más larga es mayor.

En los ejemplos anteriores, la comparación 'Z' > 'A'llega a un resultado en el

primer paso, mientras que las cadenas "Glow"y "Glee"se comparan carácter

por carácter:

1. Ges lo mismo que G.

2. les lo mismo que l.

3. oes mayor que e. Deténgase aquí. La primera cuerda es mayor.

No es un diccionario real, sino orden Unicode


El algoritmo de comparación proporcionado anteriormente es
aproximadamente equivalente al que se utiliza en diccionarios o guías
telefónicas, pero no es exactamente el mismo.

Por ejemplo, el caso importa. Una letra mayúscula "A"no es igual a

minúscula "a". Cual es mayor? La minúscula "a". ¿Por qué? Debido a que el

carácter en minúscula tiene un índice mayor en la tabla de codificación interna


que utiliza JavaScript (Unicode). Volveremos a los detalles específicos y las
consecuencias de esto en el capítulo Cadenas .

Comparación de diferentes tipos

Al comparar valores de diferentes tipos, JavaScript convierte los valores en


números.

Por ejemplo:

alert( '2' > 1 ); // true, string '2' becomes a number 2

alert( '01' == 1 ); // true, string '01' becomes a number 1

Para valores booleanos, se trueconvierte en 1y se falseconvierte en 0.

Por ejemplo:

alert( true == 1 ); // true

alert( false == 0 ); // true

Una consecuencia divertida

Es posible que al mismo tiempo:

 Dos valores son iguales.

 Uno de ellos es truebooleano y el otro es falsebooleano.


Por ejemplo:

let a = 0;

alert( Boolean(a) ); // false

let b = "0";

alert( Boolean(b) ); // true

alert(a == b); // true!

Desde el punto de vista de JavaScript, este resultado es bastante normal. Una


verificación de igualdad convierte valores usando la conversión numérica (por lo
tanto, se "0"convierte en 0), mientras que la Booleanconversión explícita usa

otro conjunto de reglas.

Igualdad estricta

Una verificación de igualdad regular ==tiene un problema. No se puede

diferenciar 0de false:

alert( 0 == false ); // true

Lo mismo sucede con una cadena vacía:

alert( '' == false ); // true

Esto sucede porque el operador de igualdad convierte operandos de diferentes

tipos en números ==. Una cadena vacía, al igual que false, se convierte en un

cero.

¿Qué hacer si nos gustaría diferenciar 0entre false?


Un operador de igualdad estricta ===comprueba la igualdad sin conversión de

tipo.

En otras palabras, si ay bson de diferentes tipos, a === bregresa

inmediatamente falsesin intentar convertirlos.

Vamos a intentarlo:

alert( 0 === false ); // false, because the types are different

También hay un operador de "no igualdad estricta" !==análogo a !=.

El operador de igualdad estricta es un poco más largo de escribir, pero hace


que sea obvio lo que está sucediendo y deja menos espacio para errores.

Comparación con nulo e indefinido

Hay un comportamiento no intuitivo cuando nullo undefinedse comparan

con otros valores.

Para un estricto control de igualdad ===

Estos valores son diferentes, porque cada uno de ellos es de un tipo


diferente.

alert( null === undefined ); // false

Para un control no estricto ==

Hay una regla especial. Estos dos son una “dulce pareja”: se igualan (en el
sentido de ==), pero no en ningún otro valor.

alert( null == undefined ); // true

Para matemáticas y otras comparaciones < > <= >=


null/undefinedse convierten en números: se nullconvierte 0, mientras

que se undefinedconvierte NaN.

Ahora veamos algunas cosas divertidas que suceden cuando aplicamos estas
reglas. Y, lo que es más importante, cómo no caer en una trampa con ellos.

Resultado extraño: nulo vs 0

Comparemos nullcon un cero:

alert( null > 0 ); // (1) false

alert( null == 0 ); // (2) false

alert( null >= 0 ); // (3) true

Matemáticamente, eso es extraño. El último resultado indica que " nulles

mayor o igual que cero", por lo que en una de las comparaciones anteriores

debe serlo true, pero ambos son falsos.

La razón es que una verificación de igualdad ==y las comparaciones > < >=

<=funcionan de manera diferente. Las comparaciones se convierten nullen un

número, tratándolo como 0. Por eso (3) null >= 0es verdadero y (1) null >

0es falso.

Por otra parte, la comprobación de igualdad ==para undefinedy nullse define

de tal manera que, sin ningún tipo de conversiones, que son iguales entre sí y

no lo hacen igual todo lo demás. Por eso (2) null == 0es falso.

Un indefinido incomparable

El valor undefinedno debe compararse con otros valores:

alert( undefined > 0 ); // false (1)

alert( undefined < 0 ); // false (2)


alert( undefined == 0 ); // false (3)

¿Por qué no le gusta tanto el cero? ¡Siempre falso!

Obtenemos estos resultados porque:

 Comparaciones (1)y (2)retorno falseporque undefinedse convierte

en un valor numérico especial que devuelve para todas las


comparaciones NaNy NaNes un valor numérico especial false.

 La comprobación de la igualdad
de (3)rendimientos falseporque undefinedsólo se iguala null, undefinedy

ningún otro valor.

Evitar problemas

¿Por qué repasamos estos ejemplos? ¿Debemos recordar estas peculiaridades


todo el tiempo? Bueno en realidad no. En realidad, estas cosas complicadas se
irán familiarizando gradualmente con el tiempo, pero hay una forma sólida de
evitar problemas con ellas:

 Trate cualquier comparación undefined/nullexcepto la estricta

igualdad ===con un cuidado excepcional.

 No utilice comparaciones >= > < <=con una variable que pueda

serlo null/undefined, a menos que esté realmente seguro de lo que está

haciendo. Si una variable puede tener estos valores, verifíquelos por separado.

Resumen

 Los operadores de comparación devuelven un valor booleano.

 Las cadenas se comparan letra por letra en el orden del "diccionario".


 Cuando se comparan valores de diferentes tipos, se convierten en
números (con la exclusión de una estricta verificación de igualdad).

 Los valores nully son undefinediguales ==entre sí y no son iguales a

ningún otro valor.

 Tenga cuidado al usar comparaciones como >o <con variables que

ocasionalmente pueden ser null/undefined. Verificar

por null/undefinedseparado es una buena idea.


9. Ramificación condicional: si, '?'

A veces, necesitamos realizar diferentes acciones en función de diferentes


condiciones.

Para hacer eso, podemos usar la ifdeclaración y el operador condicional ?,

también llamado operador de "signo de interrogación".

La declaración "si"

La if(...)declaración evalúa una condición entre paréntesis y, si el resultado

es true, ejecuta un bloque de código.

Por ejemplo:

let year = prompt('In which year was ECMAScript-2015


specification published?', '');

if (year == 2015) alert( 'You are right!' );

En el ejemplo anterior, la condición es una simple verificación de igualdad

( year == 2015), pero puede ser mucho más compleja.

Si queremos ejecutar más de una declaración, tenemos que envolver nuestro


bloque de código entre llaves:

if (year == 2015) {

alert( "That's correct!" );

alert( "You're so smart!" );

}
Recomendamos envolver su bloque de código con llaves {}cada vez que use

una ifdeclaración, incluso si solo hay una declaración para ejecutar. Hacerlo

mejora la legibilidad.

Conversión booleana

La if (…)declaración evalúa la expresión entre paréntesis y convierte el

resultado en un valor booleano.

Recordemos las reglas de conversión del capítulo Conversiones de tipos :

 Un número 0, una cadena vacía "", null, undefined, y NaNtodos se

convierten false. Por eso se les llama valores "falsos".

 Otros valores se vuelven true, por eso se les llama “veracidad”.

Entonces, el código bajo esta condición nunca se ejecutaría:

if (0) { // 0 is falsy

...

... y dentro de esta condición, siempre:

if (1) { // 1 is truthy

...

También podemos pasar un valor booleano evaluado previamente a if, como

este:

let cond = (year == 2015); // equality evaluates to true or


false
if (cond) {

...

La cláusula "else"

La ifdeclaración puede contener un bloque "else" opcional. Se ejecuta cuando

la condición es falsa.

Por ejemplo:

let year = prompt('In which year was the ECMAScript-2015


specification published?', '');

if (year == 2015) {

alert( 'You guessed it right!' );

} else {

alert( 'How can you be so wrong?' ); // any value except 2015

Varias condiciones: "si no"

A veces, nos gustaría probar varias variantes de una condición. La else

ifcláusula nos permite hacer eso.

Por ejemplo:

let year = prompt('In which year was the ECMAScript-2015


specification published?', '');
if (year < 2015) {

alert( 'Too early...' );

} else if (year > 2015) {

alert( 'Too late' );

} else {

alert( 'Exactly!' );

En el código anterior, JavaScript comprueba primero year < 2015. Si eso es

falso, pasa a la siguiente condición year > 2015. Si eso también es falso,

muestra el último alert.

Puede haber más else ifbloques. La final elsees opcional.

Operador condicional '?'

A veces, necesitamos asignar una variable dependiendo de una condición.

Por ejemplo:

let accessAllowed;

let age = prompt('How old are you?', '');

if (age > 18) {

accessAllowed = true;

} else {
accessAllowed = false;

alert(accessAllowed);

El llamado operador "condicional" o "signo de interrogación" nos permite


hacerlo de una manera más breve y sencilla.

El operador está representado por un signo de interrogación ?. A veces se le

llama "ternario", porque el operador tiene tres operandos. En realidad, es el


único operador en JavaScript que tiene tantos.

La sintaxis es:

let result = condition ? value1 : value2;

Se conditionevalúa: si es veraz, value1se devuelve, de lo contrario - value2.

Por ejemplo:

let accessAllowed = (age > 18) ? true : false;

Técnicamente, podemos omitir los paréntesis age > 18. El operador de signo

de interrogación tiene una precedencia baja, por lo que se ejecuta después de


la comparación >.

Este ejemplo hará lo mismo que el anterior:

// the comparison operator "age > 18" executes first anyway

// (no need to wrap it into parentheses)

let accessAllowed = age > 18 ? true : false;


Pero los paréntesis hacen que el código sea más legible, por lo que
recomendamos usarlos.

Tenga en cuenta:

En el ejemplo anterior, puede evitar usar el operador de signo de interrogación

porque la comparación en sí devuelve true/false:

// the same

let accessAllowed = age > 18;

Múltiples '?'

Una secuencia de operadores de signo de interrogación ?puede devolver un

valor que depende de más de una condición.

Por ejemplo:

let age = prompt('age?', 18);

let message = (age < 3) ? 'Hi, baby!' :

(age < 18) ? 'Hello!' :

(age < 100) ? 'Greetings!' :

'What an unusual age!';

alert( message );

Al principio puede resultar difícil comprender lo que está sucediendo. Pero


después de una mirada más cercana, podemos ver que es solo una secuencia
ordinaria de pruebas:
1. El primer signo de interrogación comprueba si age < 3.

2. Si es cierto, vuelve 'Hi, baby!'. De lo contrario, continúa con la

expresión después de los dos puntos '":"', comprobando age < 18.

3. Si eso es cierto, regresa 'Hello!'. De lo contrario, continúa con la

expresión después de los siguientes dos puntos '":"', marcando age < 100.

4. Si eso es cierto, regresa 'Greetings!'. De lo contrario, continúa con la

expresión después de los últimos dos puntos '":"', regresando 'What an

unusual age!'.

Así es como se ve esto usando if..else:

if (age < 3) {

message = 'Hi, baby!';

} else if (age < 18) {

message = 'Hello!';

} else if (age < 100) {

message = 'Greetings!';

} else {

message = 'What an unusual age!';

Uso no tradicional de '?'

A veces, el signo de interrogación ?se usa como reemplazo de if:

let company = prompt('Which company created JavaScript?', '');


(company == 'Netscape') ?

alert('Right!') : alert('Wrong.');

Dependiendo de la condición company == 'Netscape', la primera o la

segunda expresión después de ?se ejecuta y muestra una alerta.

Aquí no asignamos un resultado a una variable. En su lugar, ejecutamos un


código diferente según la condición.

No se recomienda utilizar el operador de signo de interrogación de esta


manera.

La notación es más corta que la ifdeclaración equivalente , lo que atrae a

algunos programadores. Pero es menos legible.

Aquí está el mismo código que se usa ifpara comparar:

let company = prompt('Which company created JavaScript?', '');

if (company == 'Netscape') {

alert('Right!');

} else {

alert('Wrong.');

Nuestros ojos escanean el código verticalmente. Los bloques de código que


abarcan varias líneas son más fáciles de entender que un conjunto de
instrucciones largo y horizontal.
El propósito del operador de signo de interrogación ?es devolver un valor u

otro dependiendo de su condición. Úselo exactamente para

eso. Úselo ifcuando necesite ejecutar diferentes ramas de código.

10. Operadores logicos

Hay tres operadores lógicos en JavaScript: ||(O), &&(Y), !(NO).

Aunque se denominan “lógicos”, se pueden aplicar a valores de cualquier tipo,


no solo booleanos. Su resultado también puede ser de cualquier tipo.

Veamos los detalles.

||  (O)

El operador "O" se representa con dos símbolos de línea vertical:

result = a || b;

En la programación clásica, el OR lógico está destinado a manipular valores


booleanos únicamente. Si alguno de sus argumentos lo es true, regresa true;

de lo contrario, regresa false.

En JavaScript, el operador es un poco más complicado y más poderoso. Pero


primero, veamos qué sucede con los valores booleanos.

Hay cuatro combinaciones lógicas posibles:

alert( true || true ); // true

alert( false || true ); // true

alert( true || false ); // true

alert( false || false ); // false


Como podemos ver, el resultado es siempre trueexcepto en el caso en que

ambos operandos son false.

Si un operando no es booleano, se convierte en booleano para la evaluación.

Por ejemplo, el número 1se trata como true, el número 0como false:

if (1 || 0) { // works just like if( true || false )

alert( 'truthy!' );

La mayoría de las veces, OR ||se usa en una ifdeclaración para probar

si alguna de las condiciones dadas lo es true.

Por ejemplo:

let hour = 9;

if (hour < 10 || hour > 18) {

alert( 'The office is closed.' );

Podemos pasar más condiciones:

let hour = 12;

let isWeekend = true;

if (hour < 10 || hour > 18 || isWeekend) {

alert( 'The office is closed.' ); // it is the weekend


}

O "||"  encuentra el primer valor de verdad

La lógica descrita anteriormente es algo clásica. Ahora, traigamos las


características "adicionales" de JavaScript.

El algoritmo extendido funciona de la siguiente manera.

Dados múltiples valores OR'ed:

result = value1 || value2 || value3;

El ||operador OR hace lo siguiente:

 Evalúa operandos de izquierda a derecha.

 Para cada operando, lo convierte en booleano. Si el resultado es true, se

detiene y devuelve el valor original de ese operando.

 Si se han evaluado todos los operandos (es decir, todos lo fueron false),

devuelve el último operando.

Un valor se devuelve en su forma original, sin la conversión.

En otras palabras, una cadena de OR "||"devuelve el primer valor de verdad o

el último si no se encuentra ningún valor de verdad.

Por ejemplo:

alert( 1 || 0 ); // 1 (1 is truthy)

alert( null || 1 ); // 1 (1 is the first truthy value)

alert( null || 0 || 1 ); // 1 (the first truthy value)


alert( undefined || null || 0 ); // 0 (all falsy, returns the
last value)

Esto conduce a un uso interesante en comparación con un "OR puro, clásico,


solo booleano".

1. Obtener el primer valor veraz de una lista de variables o expresiones.

Por ejemplo, tenemos firstName, lastNamey nickNamelas variables, todos

opcionales.

Usemos OR ||para elegir el que tiene los datos y mostrarlo (o anonymoussi no

hay nada configurado):

let firstName = "";

let lastName = "";

let nickName = "SuperCoder";

alert( firstName || lastName || nickName || "Anonymous"); //


SuperCoder

Si todas las variables fueran falsas, Anonymousaparecería.

2. Evaluación de cortocircuito.

Otra característica del ||operador de quirófano es la evaluación de

"cortocircuito".

Significa que ||procesa sus argumentos hasta que se alcanza el primer valor de

verdad, y luego el valor se devuelve inmediatamente, sin siquiera tocar el otro


argumento.
La importancia de esta característica se vuelve obvia si un operando no es solo
un valor, sino una expresión con un efecto secundario, como una asignación de
variable o una llamada a función.

En el siguiente ejemplo, solo se imprime el segundo mensaje:

true || alert("not printed");

false || alert("printed");

En la primera línea, el ||operador OR detiene la evaluación inmediatamente

después de ver true, por lo alertque no se ejecuta.

A veces, las personas usan esta función para ejecutar comandos solo si la
condición en la parte izquierda es falsa.

&& (Y)

El operador AND se representa con dos símbolos de unión &&:

result = a && b;

En la programación clásica, Y devuelve truesi ambos operandos son verdaderos

y falseno:

alert( true && true ); // true

alert( false && true ); // false

alert( true && false ); // false

alert( false && false ); // false

Un ejemplo con if:

let hour = 12;

let minute = 30;


if (hour == 12 && minute == 30) {

alert( 'The time is 12:30' );

Al igual que con OR, se permite cualquier valor como operando de AND:

if (1 && 0) { // evaluated as true && false

alert( "won't work, because the result is falsy" );

Y "&&" encuentra el primer valor falso

Dados múltiples valores con AND:

result = value1 && value2 && value3;

El &&operador AND hace lo siguiente:

 Evalúa operandos de izquierda a derecha.

 Para cada operando, lo convierte en booleano. Si el resultado es false,

se detiene y devuelve el valor original de ese operando.

 Si se han evaluado todos los operandos (es decir, todos eran verdaderos),
devuelve el último operando.

En otras palabras, Y devuelve el primer valor falso o el último valor si no se


encontró ninguno.

Las reglas anteriores son similares a OR. La diferencia es que AND devuelve el
primer valor falso mientras que OR devuelve el primer valor verdadero .

Ejemplos:
// if the first operand is truthy,

// AND returns the second operand:

alert( 1 && 0 ); // 0

alert( 1 && 5 ); // 5

// if the first operand is falsy,

// AND returns it. The second operand is ignored

alert( null && 5 ); // null

alert( 0 && "no matter what" ); // 0

También podemos pasar varios valores seguidos. Vea cómo se devuelve la


primera falsa:

alert( 1 && 2 && null && 3 ); // null

Cuando todos los valores son verdaderos, se devuelve el último valor:

alert( 1 && 2 && 3 ); // 3, the last one

La precedencia de AND &&es mayor que OR||

La precedencia del &&operador AND es mayor que OR ||.

Así que el código a && b || c && des esencialmente el mismo que si

las &&expresiones eran paréntesis: (a && b) || (c && d).

No reemplace ifcon || o &&

A veces, la gente usa el &&operador AND como una "forma más corta de

escribir if".
Por ejemplo:

let x = 1;

(x > 0) && alert( 'Greater than zero!' );

La acción en la parte derecha de &&se ejecutará solo si la evaluación la

alcanza. Es decir, solo si (x > 0)es cierto.

Así que básicamente tenemos un análogo para:

let x = 1;

if (x > 0) alert( 'Greater than zero!' );

Aunque la variante con &&parece más corta, ifes más obvia y tiende a ser un

poco más legible. Por lo tanto, recomendamos usar cada construcción para su

propósito: use ifsi queremos si y use &&si queremos Y.

!  (NO)

El operador booleano NOT se representa con un signo de exclamación !.

La sintaxis es bastante simple:

result = !value;

El operador acepta un solo argumento y hace lo siguiente:

1. Convierte el operando a tipo booleano: true/false.

2. Devuelve el valor inverso.

Por ejemplo:
alert( !true ); // false

alert( !0 ); // true

A !!veces se usa un doble NOT para convertir un valor a tipo booleano:

alert( !!"non-empty string" ); // true

alert( !!null ); // false

Es decir, el primer NO convierte el valor a booleano y devuelve el inverso, y el


segundo NO lo invierte nuevamente. Al final, tenemos una conversión simple de
valor a booleano.

Hay una forma un poco más detallada de hacer lo mismo: una Booleanfunción

incorporada:

alert( Boolean("non-empty string") ); // true

alert( Boolean(null) ); // false

La precedencia de NOT !es la más alta de todos los operadores lógicos, por lo

que siempre se ejecuta primero, antes &&o ||.


11. Operador de fusión nulo '??'

Una adición reciente

Esta es una adición reciente al idioma. Los navegadores antiguos pueden


necesitar polyfills.

El operador de fusión nula ??proporciona una sintaxis corta para seleccionar

una primera variable "definida" de la lista.

El resultado de a ?? bes:

 asi no lo es null, o undefined,

 b, de lo contrario.

Entonces, x = a ?? bes un equivalente corto a:

x = (a !== null && a !== undefined) ? a : b;

He aquí un ejemplo más extenso.

Imagínese, tenemos un usuario y hay


variables firstName, lastNameo nickNamepara su nombre, apellido y

apodo. Todos ellos pueden estar indefinidos, si el usuario decide no ingresar


ningún valor.

Nos gustaría mostrar el nombre de usuario: una de estas tres variables, o


mostrar "Anónimo" si no se establece nada.

Usemos el ??operador para seleccionar el primero definido:

let firstName = null;

let lastName = null;


let nickName = "Supercoder";

// show the first not-null/undefined value

alert(firstName ?? lastName ?? nickName ?? "Anonymous"); //


Supercoder

Comparación con ||

El ||operador OR se puede utilizar de la misma forma que ??. En realidad,

podemos reemplazar ??con ||en el código anterior y obtener el mismo

resultado, como se describió en el capítulo anterior .

La diferencia importante es que:

 ||devuelve el primer valor de verdad .

 ??devuelve el primer valor definido .

Esto es muy importante cuando nos gustaría tratar de

manera null/undefineddiferente a 0.

Por ejemplo, considere esto:

height = height ?? 100;

Se establece heighten 100si no está definido.

Vamos a compararlo con ||:

let height = 0;

alert(height || 100); // 100

alert(height ?? 100); // 0
Aquí, height || 100golosinas altura cero tan definida, mismo

que null, undefinedo cualquier otro valor Falsy. Entonces el resultado es 100.

Las height ?? 100devoluciones 100solo si heightes

exactamente nullo undefined. Entonces alertmuestra el valor de altura 0"tal

cual".

Qué comportamiento es mejor depende de un caso de uso particular. Cuando la


altura cero es un valor válido, entonces ??es preferible.

Precedencia

La precedencia del ??operador es bastante baja: 5en la tabla MDN .

Por tanto, ??se evalúa después de la mayoría de las demás operaciones, pero

antes =y ?.

Si necesitamos elegir un valor ??dentro de una expresión compleja, considere

agregar paréntesis:

let height = null;

let width = null;

// important: use parentheses

let area = (height ?? 100) * (width ?? 50);

alert(area); // 5000

De lo contrario, si omitimos los paréntesis, *tiene mayor precedencia que ??y se

ejecutaría primero.
Eso funcionaría igual que:

// probably not correct

let area = height ?? (100 * width) ?? 50;

También hay una limitación de nivel de idioma relacionada.

Por razones de seguridad, está prohibido su uso ??en conjunto con &&y ||

operadores.

El siguiente código desencadena un error de sintaxis:

let x = 1 && 2 ?? 3; // Syntax error

La limitación es sin duda discutible, pero se añadió a la especificación del


lenguaje con el fin de evitar errores de programación, ya que la gente empieza
a cambiar a ??partir de ||.

Utilice paréntesis explícitos para solucionarlo:

let x = (1 && 2) ?? 3; // Works

alert(x); // 2

Resumen

 El operador de fusión nula ??proporciona una forma breve de elegir un

valor "definido" de la lista.

Se utiliza para asignar valores predeterminados a las variables:

// set height=100, if height is null or undefined

height = height ?? 100;


 El operador ??tiene una precedencia muy baja, un poco más alta que ?

y =.

 Está prohibido usarlo con ||o &&sin paréntesis explícitos.


12. Bucles: mientras y para

A menudo necesitamos repetir acciones.

Por ejemplo, sacar productos de una lista uno tras otro o simplemente ejecutar
el mismo código para cada número del 1 al 10.

Los bucles son una forma de repetir el mismo código varias veces.

El bucle "mientras"

El whilebucle tiene la siguiente sintaxis:

while (condition) {

// code

// so-called "loop body"

Si bien conditiones cierto, se ejecuta codedesde el cuerpo del bucle.

Por ejemplo, el bucle de abajo genera imientras i < 3:

let i = 0;

while (i < 3) { // shows 0, then 1, then 2

alert( i );

i++;

Una sola ejecución del cuerpo del bucle se denomina iteración . El ciclo del
ejemplo anterior realiza tres iteraciones.
Si i++faltara en el ejemplo anterior, el ciclo se repetiría (en teoría) para

siempre. En la práctica, el navegador proporciona formas de detener tales


bucles y, en JavaScript del lado del servidor, podemos matar el proceso.

Cualquier expresión o variable puede ser una condición de bucle, no solo

comparaciones: la condición es evaluada y convertida a booleana por while.

Por ejemplo, una forma más corta de escribir while (i != 0)es while (i):

let i = 3;

while (i) { // when i becomes 0, the condition becomes falsy,


and the loop stops

alert( i );

i--;

Los tirantes rizados no son necesarios para un cuerpo de una sola línea

Si el cuerpo del bucle tiene una sola declaración, podemos omitir las llaves {…}:

let i = 3;

while (i) alert(i--);

El ciclo "hacer ... mientras"

La verificación de condición se puede mover debajo del cuerpo del bucle

usando la do..whilesintaxis:

do {

// loop body

} while (condition);
El bucle primero ejecutará el cuerpo, luego verificará la condición y, si bien es
cierto, lo ejecutará una y otra vez.

Por ejemplo:

let i = 0;

do {

alert( i );

i++;

} while (i < 3);

Esta forma de sintaxis solo debe usarse cuando desee que el cuerpo del bucle
se ejecute al menos una vez, independientemente de que la condición sea
verdadera. Por lo general, se prefiere la otra forma: while(…) {…}.

El bucle "for"

El forciclo es más complejo, pero también es el ciclo más utilizado.

Se parece a esto:

for (begin; condition; step) {

// ... loop body ...

Aprendamos el significado de estas partes con el ejemplo. El bucle por debajo


de carreras alert(i)para ide 0hasta (pero no incluyendo) 3:

for (let i = 0; i < 3; i++) { // shows 0, then 1, then 2

alert(i);

}
Examinemos la fordeclaración parte por parte:

parte

empezar i = 0 Se ejecuta una vez al entrar en el bucle.

Comprobado antes de cada iteración del ciclo. Si es falso,


condición i < 3
el bucle se detiene.

alert(i Se ejecuta una y otra vez mientras la condición es


cuerpo
) verdadera.

paso i++ Se ejecuta después del cuerpo en cada iteración.

El algoritmo de bucle general funciona así:

Run begin

→ (if condition → run body and run step)

→ (if condition → run body and run step)

→ (if condition → run body and run step)

→ ...

Es decir, se beginejecuta una vez y luego se itera: después de

cada conditionprueba bodyy stepse ejecutan.
Si es nuevo en los bucles, podría ser útil volver al ejemplo y reproducir paso a
paso cómo se ejecutan en una hoja de papel.

Esto es exactamente lo que sucede en nuestro caso:

// for (let i = 0; i < 3; i++) alert(i)

// run begin

let i = 0

// if condition → run body and run step

if (i < 3) { alert(i); i++ }

// if condition → run body and run step

if (i < 3) { alert(i); i++ }

// if condition → run body and run step

if (i < 3) { alert(i); i++ }

// ...finish, because now i == 3

Declaración de variable en línea

Aquí, la variable "contador" ise declara directamente en el ciclo. Esto se

denomina declaración de variable "en línea". Tales variables son visibles solo


dentro del ciclo.

for (let i = 0; i < 3; i++) {

alert(i); // 0, 1, 2

alert(i); // error, no such variable


En lugar de definir una variable, podríamos usar una existente:

let i = 0;

for (i = 0; i < 3; i++) { // use an existing variable

alert(i); // 0, 1, 2

alert(i); // 3, visible, because declared outside of the loop

Saltar partes

Se forpuede omitir cualquier parte de .

Por ejemplo, podemos omitir beginsi no necesitamos hacer nada al inicio del

ciclo.

Como aquí:

let i = 0; // we have i already declared and assigned

for (; i < 3; i++) { // no need for "begin"

alert( i ); // 0, 1, 2

También podemos eliminar la steppieza:

let i = 0;
for (; i < 3;) {

alert( i++ );

Esto hace que el bucle sea idéntico a while (i < 3).

De hecho, podemos eliminar todo, creando un bucle infinito:

for (;;) {

// repeats without limits

Tenga en cuenta que los dos forpuntos ;y comas deben estar presentes. De lo

contrario, habría un error de sintaxis.

Rompiendo el bucle

Normalmente, un bucle sale cuando su condición se vuelve falsa.

Pero podemos forzar la salida en cualquier momento usando


la breakdirectiva especial .

Por ejemplo, el ciclo siguiente le pide al usuario una serie de números, y se


"rompe" cuando no se ingresa ningún número:

let sum = 0;

while (true) {

let value = +prompt("Enter a number", '');


if (!value) break; // (*)

sum += value;

alert( 'Sum: ' + sum );

La breakdirectiva se activa en la línea (*)si el usuario ingresa una línea vacía o

cancela la entrada. Detiene el ciclo inmediatamente, pasando el control a la


primera línea después del ciclo. A saber, alert.

La combinación "bucle infinito + breaksegún sea necesario" es excelente para

situaciones en las que la condición de un bucle debe comprobarse no al


principio o al final del bucle, sino en el medio o incluso en varios lugares de su
cuerpo.

Continuar con la siguiente iteración

La continuedirectiva es una "versión más ligera" de break. No detiene todo el

ciclo. En cambio, detiene la iteración actual y fuerza al ciclo a iniciar uno nuevo
(si la condición lo permite).

Podemos usarlo si hemos terminado con la iteración actual y nos gustaría pasar
a la siguiente.

El ciclo siguiente se utiliza continuepara generar solo valores impares:

for (let i = 0; i < 10; i++) {


// if true, skip the remaining part of the body

if (i % 2 == 0) continue;

alert(i); // 1, then 3, 5, 7, 9

Para valores pares de i, la continuedirectiva deja de ejecutar el cuerpo y pasa

el control a la siguiente iteración de for(con el siguiente número). Por lo

tanto, alertsolo se solicita para valores impares.

La continuedirectiva ayuda a disminuir el anidamiento

Un bucle que muestra valores impares podría verse así:

for (let i = 0; i < 10; i++) {

if (i % 2) {

alert( i );

Desde un punto de vista técnico, esto es idéntico al ejemplo


anterior. Seguramente, podemos simplemente envolver el código en

un ifbloque en lugar de usar continue.


Pero como efecto secundario, esto creó un nivel más de anidación

(la alertllamada dentro de las llaves). Si el código dentro de ifes más largo

que unas pocas líneas, eso puede disminuir la legibilidad general.

No break/continueal lado derecho de '?'

Tenga en cuenta que las construcciones de sintaxis que no son expresiones no

se pueden utilizar con el operador ternario ?. En particular, directivas como las

que break/continueno se permiten allí.

Por ejemplo, si tomamos este código:

if (i > 5) {

alert(i);

} else {

continue;

... y reescribirlo usando un signo de interrogación:

(i > 5) ? alert(i) : continue; // continue isn't allowed here

… Deja de funcionar: hay un error de sintaxis.

Esta es solo otra razón para no usar el operador de signo de interrogación en ?

lugar de if.

Etiquetas para romper / continuar

A veces necesitamos salir de varios bucles anidados a la vez.

Por ejemplo, en el código a continuación, recorremos iy jsolicitamos las

coordenadas (i, j)de (0,0)a (2,2):
for (let i = 0; i < 3; i++) {

for (let j = 0; j < 3; j++) {

let input = prompt(`Value at coords (${i},${j})`, '');

// what if we want to exit from here to Done (below)?

alert('Done!');

Necesitamos una forma de detener el proceso si el usuario cancela la entrada.

El breakdespués ordinario inputsolo rompería el bucle interno. Eso no es

suficiente, etiquetas, ¡vengan al rescate!

Una etiqueta es un identificador con dos puntos antes de un bucle:

labelName: for (...) {

...

La break <labelName>declaración en el bucle a continuación se desglosa en la

etiqueta:

outer: for (let i = 0; i < 3; i++) {


for (let j = 0; j < 3; j++) {

let input = prompt(`Value at coords (${i},${j})`, '');

// if an empty string or canceled, then break out of both


loops

if (!input) break outer; // (*)

// do something with the value...

alert('Done!');

En el código anterior, break outerbusca hacia arriba la etiqueta

nombrada outery sale de ese bucle.

Entonces el control va directamente de (*)a alert('Done!').

También podemos mover la etiqueta a una línea separada:

outer:

for (let i = 0; i < 3; i++) { ... }

La continuedirectiva también se puede utilizar con una etiqueta. En este caso,

la ejecución del código salta a la siguiente iteración del ciclo etiquetado.

Las etiquetas no permiten "saltar" a ningún lado


Las etiquetas no nos permiten saltar a un lugar arbitrario en el código.

Por ejemplo, es imposible hacer esto:

break label; // doesn't jumps to the label below

label: for (...)

Una llamada a break/continuesolo es posible desde dentro de un bucle y la

etiqueta debe estar en algún lugar por encima de la directiva.

Resumen

Cubrimos 3 tipos de bucles:

 while - La condición se comprueba antes de cada iteración.

 do..while - La condición se comprueba después de cada iteración.

 for (;;) - La condición se comprueba antes de cada iteración, hay

configuraciones adicionales disponibles.

Para hacer un bucle "infinito", generalmente while(true)se

usa la construcción. Tal bucle, como cualquier otro, se puede detener con


la breakdirectiva.

Si no queremos hacer nada en la iteración actual y nos gustaría pasar a la


siguiente, podemos usar la continuedirectiva.

break/continueetiquetas de soporte antes del bucle. Una etiqueta es la única

forma de break/continueescapar de un bucle anidado para ir a uno externo.


13. La declaración "cambiar"

Una switchdeclaración puede reemplazar varios ifcheques.

Ofrece una forma más descriptiva de comparar un valor con múltiples variantes.

La sintaxis

La switchplaca presenta uno o más casebloques y un defecto opcional.

Se parece a esto:

switch(x) {

case 'value1': // if (x === 'value1')

...

[break]

case 'value2': // if (x === 'value2')

...

[break]

default:

...

[break]

 El valor de xse verifica para una igualdad estricta con el valor del

primero case(es decir, value1) luego al segundo ( value2) y así sucesivamente.


 Si se encuentra la igualdad, switchcomienza a ejecutar el código

comenzando por el correspondiente case, hasta el más cercano break(o hasta

el final de switch).

 Si ningún caso coincide, defaultse ejecuta el código (si existe).

Un ejemplo

Un ejemplo de switch(el código ejecutado está resaltado):

let a = 2 + 2;

switch (a) {

case 3:

alert( 'Too small' );

break;

case 4:

alert( 'Exactly!' );

break;

case 5:

alert( 'Too large' );

break;

default:

alert( "I don't know such values" );

}
Aquí se switchempieza a comparar adesde la primera casevariante que es 3. El

partido falla.

Entonces 4. Eso es una coincidencia, por lo que la ejecución comienza case

4hasta el más cercano break.

Si no hay break, la ejecución continúa con la siguiente casesin ningún control.

Un ejemplo sin break:

let a = 2 + 2;

switch (a) {

case 3:

alert( 'Too small' );

case 4:

alert( 'Exactly!' );

case 5:

alert( 'Too big' );

default:

alert( "I don't know such values" );

En el ejemplo anterior, veremos la ejecución secuencial de tres alerts:

alert( 'Exactly!' );

alert( 'Too big' );


alert( "I don't know such values" );

Cualquier expresión puede ser un switch/caseargumento

Ambos switchy casepermiten expresiones arbitrarias.

Por ejemplo:

let a = "1";

let b = 0;

switch (+a) {

case b + 1:

alert("this runs, because +a is 1, exactly equals b+1");

break;

default:

alert("this doesn't run");

Aquí +ada 1, que se compara con b + 1in case, y se ejecuta el código

correspondiente.

Agrupación de "caso"

Se casepueden agrupar varias variantes de las cuales comparten el mismo

código.

Por ejemplo, si queremos que se ejecute el mismo código case 3y case 5:


let a = 3;

switch (a) {

case 4:

alert('Right!');

break;

case 3: // (*) grouped two cases

case 5:

alert('Wrong!');

alert("Why don't you take a math class?");

break;

default:

alert('The result is strange. Really.');

Ahora ambos 3y 5muestran el mismo mensaje.

La capacidad de “agrupar” casos es un efecto secundario de


cómo switch/casefunciona sin break. Aquí la ejecución de case 3comienza

desde la línea (*)y pasa case 5, porque no hay break.

El tipo importa
Destaquemos que el control de igualdad siempre es estricto. Los valores deben
ser del mismo tipo para coincidir.

Por ejemplo, consideremos el código:

let arg = prompt("Enter a value?");

switch (arg) {

case '0':

case '1':

alert( 'One or zero' );

break;

case '2':

alert( 'Two' );

break;

case 3:

alert( 'Never executes!' );

break;

default:

alert( 'An unknown value' );

1. Porque 0, 1el primero alertcorre.
2. Para 2las segundas alertcarreras.

3. Pero para 3, el resultado de promptes una cadena "3", que no es

estrictamente igual ===al número 3. ¡Así que tenemos un código muerto case

3! La defaultvariante se ejecutará.
14. Funciones

Muy a menudo, necesitamos realizar una acción similar en muchos lugares del
guión.

Por ejemplo, necesitamos mostrar un mensaje atractivo cuando un visitante


inicia sesión, cierra sesión y tal vez en otro lugar.

Las funciones son los principales "bloques de construcción" del


programa. Permiten que el código sea llamado muchas veces sin repetición.

Ya hemos visto ejemplos de funciones incorporadas,

como alert(message), prompt(message,

default)y confirm(question). Pero también podemos crear nuestras propias

funciones.

Declaración de función

Para crear una función podemos usar una declaración de función .

Se parece a esto:

function showMessage() {

alert( 'Hello everyone!' );

La functionpalabra clave va primero, luego el nombre de la función , luego una

lista de parámetros entre paréntesis (separados por comas, vacíos en el ejemplo


anterior) y finalmente el código de la función, también llamado "el cuerpo de la
función", entre rizado tirantes.

function name(parameters) {

...body...
}

Nuestra nueva función puede ser llamada por su nombre: showMessage().

Por ejemplo:

function showMessage() {

alert( 'Hello everyone!' );

showMessage();

showMessage();

La llamada showMessage()ejecuta el código de la función. Aquí veremos el

mensaje dos veces.

Este ejemplo demuestra claramente uno de los propósitos principales de las


funciones: evitar la duplicación de código.

Si alguna vez necesitamos cambiar el mensaje o la forma en que se muestra,


basta con modificar el código en un lugar: la función que lo genera.

Variables locales

Una variable declarada dentro de una función solo es visible dentro de esa
función.

Por ejemplo:

function showMessage() {

let message = "Hello, I'm JavaScript!"; // local variable


alert( message );

showMessage(); // Hello, I'm JavaScript!

alert( message ); // <-- Error! The variable is local to the


function

Variables externas

Una función también puede acceder a una variable externa, por ejemplo:

let userName = 'John';

function showMessage() {

let message = 'Hello, ' + userName;

alert(message);

showMessage(); // Hello, John

La función tiene acceso completo a la variable externa. También puede


modificarlo.

Por ejemplo:

let userName = 'John';


function showMessage() {

userName = "Bob"; // (1) changed the outer variable

let message = 'Hello, ' + userName;

alert(message);

alert( userName ); // John before the function call

showMessage();

alert( userName ); // Bob, the value was modified by the


function

La variable externa solo se usa si no hay una local.

Si se declara una variable con el mismo nombre dentro de la


función, sombrea la exterior. Por ejemplo, en el código siguiente, la función usa
el local userName. El exterior se ignora:

let userName = 'John';

function showMessage() {

let userName = "Bob"; // declare a local variable


let message = 'Hello, ' + userName; // Bob

alert(message);

// the function will create and use its own userName

showMessage();

alert( userName ); // John, unchanged, the function did not


access the outer variable

Variables globales

Las variables declaradas fuera de cualquier función, como la


exterior userNameen el código anterior, se denominan globales .

Las variables globales son visibles desde cualquier función (a menos que sean
sombreadas por los locales).

Es una buena práctica minimizar el uso de variables globales. El código


moderno tiene pocas o ninguna global. La mayoría de las variables residen en
sus funciones. A veces, sin embargo, pueden ser útiles para almacenar datos a
nivel de proyecto.

Parámetros

Podemos pasar datos arbitrarios a funciones usando parámetros (también


llamados argumentos de función ).

En el siguiente ejemplo, la función tiene dos parámetros: fromy text.

function showMessage(from, text) { // arguments: from, text


alert(from + ': ' + text);

showMessage('Ann', 'Hello!'); // Ann: Hello! (*)

showMessage('Ann', "What's up?"); // Ann: What's up? (**)

Cuando se llama a la función en líneas (*)y (**), los valores dados se copian

en variables locales fromy text. Entonces la función los usa.

Aquí hay un ejemplo más: tenemos una variable fromy se la pasamos a la

función. Tenga en cuenta: la función cambia from, pero el cambio no se ve

afuera, porque una función siempre obtiene una copia del valor:

function showMessage(from, text) {

from = '*' + from + '*'; // make "from" look nicer

alert( from + ': ' + text );

let from = "Ann";

showMessage(from, "Hello"); // *Ann*: Hello


// the value of "from" is the same, the function modified a
local copy

alert( from ); // Ann

Valores predeterminados

Si no se proporciona un parámetro, su valor se convierte en undefined.

Por ejemplo, la función antes mencionada showMessage(from, text)se puede

llamar con un solo argumento:

showMessage("Ann");

Eso no es un error. Tal llamada saldría "Ann: undefined". No hay text, por lo

que se asume eso text === undefined.

Si queremos usar un "predeterminado" texten este caso, podemos

especificarlo después =:

function showMessage(from, text = "no text given") {

alert( from + ": " + text );

showMessage("Ann"); // Ann: no text given

Ahora, si textno se pasa el parámetro, obtendrá el valor"no text given"

Aquí "no text given"hay una cadena, pero puede ser una expresión más

compleja, que solo se evalúa y asigna si falta el parámetro. Entonces, esto


también es posible:

function showMessage(from, text = anotherFunction()) {


// anotherFunction() only executed if no text given

// its result becomes the value of text

Evaluación de parámetros predeterminados

En JavaScript, un parámetro predeterminado se evalúa cada vez que se llama a


la función sin el parámetro respectivo.

En el ejemplo anterior, anotherFunction()se llama cada vez

que showMessage()se llama sin el textparámetro.

Parámetros predeterminados alternativos

A veces tiene sentido establecer valores predeterminados para los parámetros


no en la declaración de la función, sino en una etapa posterior, durante su
ejecución.

Para comprobar si hay un parámetro omitido, podemos compararlo


con undefined:

function showMessage(text) {

if (text === undefined) {

text = 'empty message';

alert(text);

}
showMessage(); // empty message

… O podríamos usar el ||operador:

// if text parameter is omitted or "" is passed, set it to


'empty'

function showMessage(text) {

text = text || 'empty';

...

Los motores de JavaScript modernos admiten el operador de fusión nula ?? , es

mejor cuando los valores falsos, como 0, se consideran regulares:

// if there's no "count" parameter, show "unknown"

function showCount(count) {

alert(count ?? "unknown");

showCount(0); // 0

showCount(null); // unknown

showCount(); // unknown

Devolviendo un valor

Una función puede devolver un valor al código de llamada como resultado.

El ejemplo más simple sería una función que suma dos valores:
function sum(a, b) {

return a + b;

let result = sum(1, 2);

alert( result ); // 3

La directiva returnpuede estar en cualquier lugar de la función. Cuando la

ejecución lo alcanza, la función se detiene y el valor se devuelve al código de

llamada (asignado a resultarriba).

Puede haber muchas apariciones de returnen una sola función. Por ejemplo:

function checkAge(age) {

if (age >= 18) {

return true;

} else {

return confirm('Do you have permission from your parents?');

let age = prompt('How old are you?', 18);

if ( checkAge(age) ) {

alert( 'Access granted' );


} else {

alert( 'Access denied' );

Se puede utilizar returnsin valor. Eso hace que la función se cierre

inmediatamente.

Por ejemplo:

function showMovie(age) {

if ( !checkAge(age) ) {

return;

alert( "Showing you the movie" ); // (*)

// ...

En el código anterior, si checkAge(age)regresa false, showMovieno procederá

al alert.

Una función con un returnretorno vacío o sin élundefined

Si una función no devuelve un valor, es lo mismo que si devuelve undefined:

function doNothing() { /* empty */ }

alert( doNothing() === undefined ); // true


Un vacío returntambién es lo mismo que return undefined:

function doNothing() {

return;

alert( doNothing() === undefined ); // true

Nunca agregue una nueva línea entre returny el valor

Para una expresión larga return, puede ser tentador ponerla en una línea

separada, como esta:

return

(some + long + expression + or + whatever * f(a) + f(b))

Eso no funciona, porque JavaScript asume un punto y coma


después return. Eso funcionará igual que:

return;

(some + long + expression + or + whatever * f(a) + f(b))

Entonces, efectivamente se convierte en un retorno vacío.

Si queremos que la expresión devuelta se ajuste a varias líneas, debemos


comenzar en la misma línea que return. O al menos coloque el paréntesis de

apertura allí de la siguiente manera:

return (

some + long + expression

+ or +
whatever * f(a) + f(b)

Y funcionará tal como lo esperamos.

Nombrar una función

Las funciones son acciones. Entonces, su nombre suele ser un verbo. Debe ser


breve, lo más preciso posible y describir lo que hace la función, de modo que
alguien que lea el código obtenga una indicación de lo que hace la función.

Es una práctica generalizada comenzar una función con un prefijo verbal que
describe vagamente la acción. Debe haber un acuerdo dentro del equipo sobre
el significado de los prefijos.

Por ejemplo, las funciones que comienzan con "show"suelen mostrar algo.

Función que comienza con…

 "get…" - devolver un valor,

 "calc…" - calcular algo,

 "create…" - crear algo,

 "check…" - comprobar algo y devolver un booleano, etc.

Ejemplos de tales nombres:

showMessage(..) // shows a message

getAge(..) // returns the age (gets it somehow)

calcSum(..) // calculates a sum and returns the result

createForm(..) // creates a form (and usually returns it)


checkPermission(..) // checks a permission, returns true/false

Con los prefijos en su lugar, un vistazo al nombre de una función permite


comprender qué tipo de trabajo hace y qué tipo de valor devuelve.

Una función, una acción

Una función debe hacer exactamente lo que sugiere su nombre, nada más.

Dos acciones independientes generalmente merecen dos funciones, incluso si


generalmente se llaman juntas (en ese caso, podemos hacer una tercera función
que llame a esas dos).

Algunos ejemplos de incumplimiento de esta regla:

 getAge- Sería malo si muestra una alertcon la edad (solo debería

obtener).

 createForm - Sería malo si modifica el documento, agregando un

formulario (solo debe crearlo y regresar).

 checkPermission- Sería malo si muestra el access

granted/deniedmensaje (solo debe realizar la verificación y devolver el


resultado).

Estos ejemplos asumen significados comunes de prefijos. Usted y su equipo son


libres de acordar otros significados, pero generalmente no son muy
diferentes. En cualquier caso, debe tener una comprensión firme de lo que
significa un prefijo, lo que una función con prefijo puede y no puede
hacer. Todas las funciones con el mismo prefijo deben obedecer las reglas. Y el
equipo debe compartir el conocimiento.

Nombres de funciones ultracortos


Las funciones que se utilizan con mucha frecuencia a veces tienen nombres
ultracortos.

Por ejemplo, el marco jQuery define una función

con $. La biblioteca Lodash tiene su función principal nombrada _.

Éstas son excepciones. Generalmente, los nombres de las funciones deben ser


concisos y descriptivos.

Funciones == Comentarios

Las funciones deben ser cortas y hacer exactamente una cosa. Si eso es grande,
tal vez valga la pena dividir la función en algunas funciones más pequeñas. A
veces, seguir esta regla puede no ser tan fácil, pero definitivamente es algo
bueno.

Una función separada no solo es más fácil de probar y depurar, ¡su misma
existencia es un gran comentario!

Por ejemplo, compare las dos funciones showPrimes(n)siguientes. Cada uno

genera números primos hasta n.

La primera variante usa una etiqueta:

function showPrimes(n) {

nextPrime: for (let i = 2; i < n; i++) {

for (let j = 2; j < i; j++) {

if (i % j == 0) continue nextPrime;

}
alert( i ); // a prime

La segunda variante usa una función adicional isPrime(n)para probar la

primalidad:

function showPrimes(n) {

for (let i = 2; i < n; i++) {

if (!isPrime(i)) continue;

alert(i); // a prime

function isPrime(n) {

for (let i = 2; i < n; i++) {

if ( n % i == 0) return false;

return true;

}
La segunda variante es más fácil de entender, ¿no? En lugar de la pieza de

código, vemos un nombre de la acción ( isPrime). A veces, la gente se refiere a

este código como autodescriptivo .

Por lo tanto, se pueden crear funciones incluso si no tenemos la intención de


reutilizarlas. Estructuran el código y lo hacen legible.

Resumen

Una declaración de función se ve así:

function name(parameters, delimited, by, comma) {

/* code */

 Los valores que se pasan a una función como parámetros se copian en


sus variables locales.

 Una función puede acceder a variables externas. Pero funciona solo de


adentro hacia afuera. El código fuera de la función no ve sus variables locales.

 Una función puede devolver un valor. Si no es así, el resultado

es undefined.

Para que el código sea limpio y fácil de entender, se recomienda utilizar


principalmente variables y parámetros locales en la función, no variables
externas.

Siempre es más fácil entender una función que obtiene parámetros, trabaja con
ellos y devuelve un resultado que una función que no obtiene parámetros, pero
modifica variables externas como efecto secundario.

Denominación de funciones:
 Un nombre debe describir claramente lo que hace la función. Cuando
vemos una llamada a una función en el código, un buen nombre
instantáneamente nos da una comprensión de lo que hace y devuelve.

 Una función es una acción, por lo que los nombres de las funciones
suelen ser verbales.

 Existen muchos prefijos de función conocida

como create…, show…, get…, check…y así sucesivamente. Úselos para insinuar

lo que hace una función.

Las funciones son los bloques de construcción principales de los scripts. Ahora


hemos cubierto los conceptos básicos, por lo que podemos comenzar a crearlos
y usarlos. Pero ese es solo el comienzo del camino. Vamos a volver a ellos
muchas veces, profundizando en sus funciones avanzadas.

Expresiones de función

En JavaScript, una función no es una "estructura de lenguaje mágico", sino un


tipo especial de valor.

La sintaxis que usamos antes se llama Declaración de función :

function sayHi() {

alert( "Hello" );

Existe otra sintaxis para crear una función que se llama Expresión de función .

Se parece a esto:

let sayHi = function() {


alert( "Hello" );

};

Aquí, la función se crea y se asigna a la variable de forma explícita, como


cualquier otro valor. No importa cómo se defina la función, es solo un valor

almacenado en la variable sayHi.

El significado de estos ejemplos de código es el mismo: "crear una función y

ponerla en la variable sayHi".

Incluso podemos imprimir ese valor usando alert:

function sayHi() {

alert( "Hello" );

alert( sayHi ); // shows the function code

Tenga en cuenta que la última línea no ejecuta la función, porque no hay

paréntesis después sayHi. Hay lenguajes de programación donde cualquier

mención del nombre de una función provoca su ejecución, pero JavaScript no es


así.

En JavaScript, una función es un valor, por lo que podemos tratarla como un


valor. El código anterior muestra su representación de cadena, que es el código
fuente.

Seguramente, una función es un valor especial, en el sentido de que podemos


llamarlo como sayHi().
Pero sigue siendo un valor. Entonces podemos trabajar con él como con otros
tipos de valores.

Podemos copiar una función a otra variable:

function sayHi() { // (1) create

alert( "Hello" );

let func = sayHi; // (2) copy

func(); // Hello // (3) run the copy (it works)!

sayHi(); // Hello // this still works too (why wouldn't


it)

Esto es lo que sucede arriba en detalle:

1. La Declaración de función (1)crea la función y la coloca en la variable


nombrada sayHi.

2. Line lo (2)copia en la variable func. Tenga en cuenta de nuevo: no hay

paréntesis después sayHi. Si lo hubiera, func = sayHi()escribiría el resultado

de la llamada sayHi() en func, no la función en sayHi sí.

3. Ahora la función se puede llamar como sayHi()y func().

Tenga en cuenta que también podríamos haber utilizado una expresión de


función para declarar sayHi, en la primera línea:

let sayHi = function() {


alert( "Hello" );

};

let func = sayHi;

// ...

Todo funcionaría igual.

¿Por qué hay un punto y coma al final?

Es posible que se pregunte por qué la expresión de función tiene un punto y


coma ;al final, pero la declaración de función no:

function sayHi() {

// ...

let sayHi = function() {

// ...

};

La respuesta es simple:

 No hay necesidad de que ;al final de los bloques de código y las

estructuras sintácticas que los utilizan gustaría if { ... }, for

{ }, function f { }etc.

 Una expresión de función se utiliza dentro de la instrucción:, let sayHi

= ...;como valor. No es un bloque de código, sino una


asignación. Se ;recomienda el punto y coma al final de las declaraciones, sin

importar cuál sea el valor. Entonces, el punto y coma aquí no está relacionado


con la expresión de función en sí, simplemente termina la declaración.

Funciones de devolución de llamada

Veamos más ejemplos de pasar funciones como valores y usar expresiones de


función.

Escribiremos una función ask(question, yes, no)con tres parámetros:

question

Texto de la pregunta

yes

Función a ejecutar si la respuesta es "Sí"

no

Función a ejecutar si la respuesta es "No"

La función debe preguntar al questiony, dependiendo de la respuesta del

usuario, llamar yes()o no():

function ask(question, yes, no) {

if (confirm(question)) yes()

else no();

function showOk() {
alert( "You agreed." );

function showCancel() {

alert( "You canceled the execution." );

// usage: functions showOk, showCancel are passed as arguments


to ask

ask("Do you agree?", showOk, showCancel);

En la práctica, estas funciones son bastante útiles. La principal diferencia entre

una vida real asky el ejemplo anterior es que las funciones de la vida real

utilizan formas más complejas de interactuar con el usuario que una


simple confirm. En el navegador, esta función suele dibujar una bonita ventana

de preguntas. Pero esa es otra historia.

Los argumentos showOky showCancelde askse denominan funciones de

devolución de llamada o simplemente devoluciones de llamada .

La idea es que pasamos una función y esperamos que sea "llamada de nuevo"

más tarde si es necesario. En nuestro caso, se showOkconvierte en la devolución

de llamada para la respuesta "sí" y showCancelpara la respuesta "no".

Podemos usar Expresiones de función para escribir la misma función mucho


más corta:

function ask(question, yes, no) {


if (confirm(question)) yes()

else no();

ask(

"Do you agree?",

function() { alert("You agreed."); },

function() { alert("You canceled the execution."); }

);

Aquí, las funciones se declaran dentro de la ask(...)llamada. No tienen

nombre, por lo que se denominan anónimos . Tales funciones no son accesibles


fuera de ask(porque no están asignadas a variables), pero eso es lo que

queremos aquí.

Dicho código aparece en nuestros scripts de forma muy natural, en el espíritu


de JavaScript.

Una función es un valor que representa una "acción"

Los valores regulares como cadenas o números representan los datos .

Una función puede percibirse como una acción .

Podemos pasarlo entre variables y ejecutarlo cuando queramos.

Expresión de función frente a declaración de función

Formulemos las diferencias clave entre declaraciones de funciones y


expresiones.
Primero, la sintaxis: cómo diferenciarlos en el código.

 Declaración de función: una función, declarada como una declaración


separada, en el flujo de código principal.

 // Function Declaration

 function sum(a, b) {

 return a + b;

 Expresión de función: una función, creada dentro de una expresión o


dentro de otra construcción de sintaxis. Aquí, la función se crea en el lado
derecho de la "expresión de asignación" =:

 // Function Expression

 let sum = function(a, b) {

 return a + b;

};

La diferencia más sutil es cuando el motor JavaScript crea una función.

Una expresión de función se crea cuando la ejecución la alcanza y solo se puede


utilizar a partir de ese momento.

Una vez que el flujo de ejecución pasa al lado derecho de la asignación let

sum = function…, aquí vamos, se crea la función y se puede usar (asignar,

llamar, etc.) a partir de ahora.

Las declaraciones de funciones son diferentes.

Una declaración de función se puede llamar antes de que se defina.


Por ejemplo, una Declaración de función global es visible en todo el script, sin
importar dónde se encuentre.

Eso se debe a algoritmos internos. Cuando JavaScript se prepara para ejecutar el


script, primero busca declaraciones de funciones globales en él y crea las
funciones. Podemos pensar en ello como una "etapa de inicialización".

Y después de que se procesan todas las Declaraciones de funciones, se ejecuta


el código. Entonces tiene acceso a estas funciones.

Por ejemplo, esto funciona:

sayHi("John"); // Hello, John

function sayHi(name) {

alert( `Hello, ${name}` );

La Declaración de función sayHise crea cuando JavaScript se está preparando

para iniciar el script y está visible en todas partes.

... Si fuera una expresión de función, entonces no funcionaría:

sayHi("John"); // error!

let sayHi = function(name) { // (*) no magic any more

alert( `Hello, ${name}` );

};
Las expresiones de función se crean cuando la ejecución las alcanza. Eso

sucedería solo en la línea (*). Demasiado tarde.

Otra característica especial de las declaraciones de funciones es su alcance de


bloque.

En modo estricto, cuando una declaración de función está dentro de un bloque


de código, es visible en todas partes dentro de ese bloque. Pero no fuera de
ella.

Por ejemplo, imaginemos que necesitamos declarar una

función welcome()dependiendo de la agevariable que obtengamos durante el

tiempo de ejecución. Y luego planeamos usarlo algún tiempo después.

Si usamos la Declaración de función, no funcionará según lo previsto:

let age = prompt("What is your age?", 18);

// conditionally declare a function

if (age < 18) {

function welcome() {

alert("Hello!");

} else {
function welcome() {

alert("Greetings!");

// ...use it later

welcome(); // Error: welcome is not defined

Eso es porque una Declaración de función solo es visible dentro del bloque de
código en el que reside.

Aquí hay otro ejemplo:

let age = 16; // take 16 as an example

if (age < 18) {

welcome(); // \ (runs)

// |

function welcome() { // |

alert("Hello!"); // | Function Declaration is


available

} // | everywhere in the block where


it's declared

// |
welcome(); // / (runs)

} else {

function welcome() {

alert("Greetings!");

// Here we're out of curly braces,

// so we can not see Function Declarations made inside of them.

welcome(); // Error: welcome is not defined

¿Qué podemos hacer para hacer welcomevisible el exterior if?

El enfoque correcto sería usar una expresión de función y asignarla welcomea la

variable que se declara fuera ify tiene la visibilidad adecuada.

Este código funciona según lo previsto:

let age = prompt("What is your age?", 18);

let welcome;
if (age < 18) {

welcome = function() {

alert("Hello!");

};

} else {

welcome = function() {

alert("Greetings!");

};

welcome(); // ok now

O podríamos simplificarlo aún más usando un operador de signo de


interrogación ?:

let age = prompt("What is your age?", 18);

let welcome = (age < 18) ?

function() { alert("Hello!"); } :
function() { alert("Greetings!"); };

welcome(); // ok now

¿Cuándo elegir la declaración de función frente a la expresión de función?

Como regla general, cuando necesitamos declarar una función, lo primero que
debemos considerar es la sintaxis de Declaración de funciones. Da más libertad
sobre cómo organizar nuestro código, porque podemos llamar a tales funciones
antes de que se declaren.

Eso también es mejor para la legibilidad, ya que es más fácil buscar function

f(…) {…}en el código que let f = function(…) {…};. Las declaraciones de

funciones son más "llamativas".

… Pero si una declaración de función no nos conviene por alguna razón, o


necesitamos una declaración condicional (acabamos de ver un ejemplo),
entonces se debe usar la expresión de función.

Resumen

 Las funciones son valores. Se pueden asignar, copiar o declarar en


cualquier lugar del código.

 Si la función se declara como una declaración separada en el flujo de


código principal, se denomina "Declaración de función".

 Si la función se crea como parte de una expresión, se denomina


"Expresión de función".

 Las declaraciones de función se procesan antes de que se ejecute el


bloque de código. Son visibles en todas partes del bloque.
 Las expresiones de función se crean cuando el flujo de ejecución las
alcanza.

En la mayoría de los casos, cuando necesitamos declarar una función, es


preferible una Declaración de función, porque es visible antes que la declaración
en sí. Eso nos da más flexibilidad en la organización del código y, por lo general,
es más legible.

Por lo tanto, debemos usar una expresión de función solo cuando una
declaración de función no sea adecuada para la tarea. Hemos visto un par de
ejemplos de eso en este capítulo y veremos más en el futuro.
15. Funciones de flecha, conceptos básicos

Existe otra sintaxis muy simple y concisa para crear funciones, que a menudo es
mejor que las expresiones de función.

Se llama "funciones de flecha", porque se ve así:

let func = (arg1, arg2, ...argN) => expression

... Esto crea una función funcque acepta argumentos arg1..argN, luego evalúa

el expressiondel lado derecho con su uso y devuelve su resultado.

En otras palabras, es la versión más corta de:

let func = function(arg1, arg2, ...argN) {

return expression;

};

Veamos un ejemplo concreto:

let sum = (a, b) => a + b;

/* This arrow function is a shorter form of:

let sum = function(a, b) {

return a + b;

};

*/
alert( sum(1, 2) ); // 3

Como puede, ver (a, b) => a + bsignifica una función que acepta dos

argumentos llamados ay b. Tras la ejecución, evalúa la expresión a + by

devuelve el resultado.

 Si solo tenemos un argumento, entonces los paréntesis alrededor de los


parámetros se pueden omitir, haciéndolo aún más corto.

Por ejemplo:

let double = n => n * 2;

// roughly the same as: let double = function(n) { return n *


2 }

alert( double(3) ); // 6

 Si no hay argumentos, los paréntesis estarán vacíos (pero deberían estar


presentes):

 let sayHi = () => alert("Hello!");

sayHi();

Las funciones de flecha se pueden utilizar de la misma forma que las


expresiones de función.

Por ejemplo, para crear dinámicamente una función:

let age = prompt("What is your age?", 18);


let welcome = (age < 18) ?

() => alert('Hello') :

() => alert("Greetings!");

welcome();

Las funciones de las flechas pueden parecer desconocidas y poco legibles al


principio, pero eso cambia rápidamente a medida que los ojos se acostumbran
a la estructura.

Son muy convenientes para acciones simples de una línea, cuando somos
demasiado perezosos para escribir muchas palabras.

Funciones de flecha multilínea

Los ejemplos anteriores tomaron argumentos de la izquierda =>y evaluaron la

expresión del lado derecho con ellos.

A veces necesitamos algo un poco más complejo, como múltiples expresiones o


declaraciones. También es posible, pero debemos encerrarlos entre
llaves. Luego usa un normal returndentro de ellos.

Me gusta esto:

let sum = (a, b) => { // the curly brace opens a multiline


function

let result = a + b;

return result; // if we use curly braces, then we need an


explicit "return"

};
alert( sum(1, 2) ); // 3

Más por venir

Aquí elogiamos las funciones de flecha por su brevedad. ¡Pero eso no es todo!

Las funciones de flecha tienen otras características interesantes.

Para estudiarlos en profundidad, primero necesitamos conocer algunos otros


aspectos de JavaScript, por lo que volveremos a las funciones de flecha más
adelante en el capítulo Funciones de flecha revisadas .

Por ahora, ya podemos usar funciones de flecha para acciones de una línea y
devoluciones de llamada.

Resumen

Las funciones de flecha son útiles para una sola línea. Vienen en dos sabores:

1. Sin llaves: (...args) => expression- el lado derecho es una

expresión: la función la evalúa y devuelve el resultado.

2. Con llaves: (...args) => { body }- los corchetes nos permiten

escribir múltiples declaraciones dentro de la función, pero necesitamos una

explícita returnpara devolver algo.


16. Estilo de codificación

Nuestro código debe ser lo más limpio y fácil de leer posible.

Ese es en realidad el arte de la programación: tomar una tarea compleja y


codificarla de una manera correcta y legible por humanos. Un buen estilo de
código ayuda mucho en eso.

Sintaxis

Aquí hay una hoja de trucos con algunas reglas sugeridas (consulte a
continuación para obtener más detalles):

Ahora analicemos las reglas y las razones para ellas en detalle.

No hay reglas de "debes"

Aquí nada está escrito en piedra. Son preferencias de estilo, no dogmas


religiosos.
Tirantes rizados

En la mayoría de los proyectos de JavaScript, las llaves se escriben en estilo


"egipcio" con la llave de apertura en la misma línea que la palabra clave
correspondiente, no en una nueva línea. También debe haber un espacio antes
del corchete de apertura, como este:

if (condition) {

// do this

// ...and that

// ...and that

Una construcción de una sola línea, como if (condition) doSomething(), es

un caso de borde importante. ¿Deberíamos usar aparatos ortopédicos?

Aquí están las variantes anotadas para que pueda juzgar su legibilidad por
usted mismo:

1. 😠 Los principiantes a veces hacen eso. ¡Malo! No se necesitan tirantes


rizados:

if (n < 0) {alert(`Power ${n} is not supported`);}

2. 😠 Dividir en una línea separada sin tirantes. Nunca hagas eso, es fácil


cometer un error al agregar nuevas líneas:

3. if (n < 0)

alert(`Power ${n} is not supported`);

4. 😏 Una línea sin tirantes - aceptable, si es corta:


if (n < 0) alert(`Power ${n} is not supported`);

5. 😃 La mejor variante:

6. if (n < 0) {

7. alert(`Power ${n} is not supported`);

Para un código muy breve, se permite una línea, por ejemplo if (cond)

return null. Pero un bloque de código (la última variante) suele ser más

legible.

Longitud de la línea

A nadie le gusta leer una larga línea horizontal de código. Es una buena práctica
dividirlos.

Por ejemplo:

// backtick quotes ` allow to split the string into multiple


lines

let str = `

ECMA International's TC39 is a group of JavaScript developers,

implementers, academics, and more, collaborating with the


community

to maintain and evolve the definition of JavaScript.

`;

Y, para ifdeclaraciones:

if (
id === 123 &&

moonPhase === 'Waning Gibbous' &&

zodiacSign === 'Libra'

) {

letTheSorceryBegin();

La longitud máxima de la línea debe acordarse a nivel de equipo. Suele tener 80


o 120 caracteres.

Sangrías

Hay dos tipos de sangrías:

 Sangrías horizontales: 2 o 4 espacios.

Se hace una sangría horizontal utilizando 2 o 4 espacios o el símbolo de

tabulación horizontal (tecla  Tab ). Cuál elegir es una vieja guerra santa. Los

espacios son más habituales hoy en día.

Una ventaja de los espacios sobre las pestañas es que los espacios permiten
configuraciones de sangrías más flexibles que el símbolo de pestaña.

Por ejemplo, podemos alinear los argumentos con el corchete de apertura, así:

show(parameters,

aligned, // 5 spaces padding at the left

one,

after,

another
) {

// ...

 Sangrías verticales: líneas vacías para dividir el código en bloques lógicos.

Incluso una sola función a menudo se puede dividir en bloques lógicos. En el


siguiente ejemplo, la inicialización de las variables, el bucle principal y la
devolución del resultado se dividen verticalmente:

function pow(x, n) {

let result = 1;

// <--

for (let i = 0; i < n; i++) {

result *= x;

// <--

return result;

Inserte una nueva línea adicional donde ayude a que el código sea más
legible. No debe haber más de nueve líneas de código sin sangría vertical.

Punto y coma

Debe haber un punto y coma después de cada declaración, incluso si es posible


que se salte.
Hay idiomas en los que el punto y coma es realmente opcional y rara vez se
usa. En JavaScript, sin embargo, hay casos en los que un salto de línea no se
interpreta como un punto y coma, dejando el código vulnerable a errores. Vea
más sobre eso en el capítulo Estructura del código .

Si es un programador de JavaScript experimentado, puede elegir un estilo de


código sin punto y coma como StandardJS . De lo contrario, es mejor usar
punto y coma para evitar posibles errores. La mayoría de los desarrolladores
ponen punto y coma.

Niveles de anidación

Intente evitar anidar el código a demasiados niveles de profundidad.

Por ejemplo, en el bucle, a veces es una buena idea usar la continuedirectiva

para evitar anidaciones adicionales.

Por ejemplo, en lugar de agregar un ifcondicional anidado como este:

for (let i = 0; i < 10; i++) {

if (cond) {

... // <- one more nesting level

Podemos escribir:

for (let i = 0; i < 10; i++) {

if (!cond) continue;

... // <- no extra nesting level


}

Se puede hacer algo similar con if/elsey return.

Por ejemplo, dos construcciones a continuación son idénticas.

Opción 1:

function pow(x, n) {

if (n < 0) {

alert("Negative 'n' not supported");

} else {

let result = 1;

for (let i = 0; i < n; i++) {

result *= x;

return result;

Opcion 2:

function pow(x, n) {

if (n < 0) {

alert("Negative 'n' not supported");


return;

let result = 1;

for (let i = 0; i < n; i++) {

result *= x;

return result;

El segundo es más legible porque el "caso especial" de n < 0se maneja desde

el principio. Una vez que se realiza la verificación, podemos pasar al flujo de


código "principal" sin la necesidad de un anidamiento adicional.

Colocación de funciones

Si está escribiendo varias funciones "auxiliares" y el código que las usa, hay tres
formas de organizar las funciones.

1. Declare las funciones sobre el código que las usa:

2. // function declarations

3. function createElement() {

4. ...

5. }
6.

7. function setHandler(elem) {

8. ...

9. }

10.

11. function walkAround() {

12. ...

13. }

14.

15. // the code which uses them

16. let elem = createElement();

17. setHandler(elem);

walkAround();

18. Código primero, luego funciones

19. // the code which uses the functions

20. let elem = createElement();

21. setHandler(elem);

22. walkAround();

23.

24. // --- helper functions ---

25. function createElement() {


26. ...

27. }

28.

29. function setHandler(elem) {

30. ...

31. }

32.

33. function walkAround() {

34. ...

35. Mixta: una función se declara donde se usa por primera vez.

La mayoría de las veces, se prefiere la segunda variante.

Eso es porque cuando leemos código, primero queremos saber qué hace . Si el


código va primero, queda claro desde el principio. Entonces, tal vez no
necesitemos leer las funciones en absoluto, especialmente si sus nombres son
descriptivos de lo que realmente hacen.

Guías de estilo

Una guía de estilo contiene reglas generales sobre “cómo escribir” código, por
ejemplo, qué citas usar, cuántos espacios sangrar, la longitud máxima de línea,
etc. Muchas cosas menores.

Cuando todos los miembros de un equipo usan la misma guía de estilo, el


código se ve uniforme, independientemente de qué miembro del equipo lo
haya escrito.
Por supuesto, un equipo siempre puede escribir su propia guía de estilo, pero
normalmente no es necesario. Hay muchas guías existentes para elegir.

Algunas opciones populares:

 Guía de estilo de JavaScript de Google

 Guía de estilo de JavaScript de Airbnb

 Idiomatic.JS

 StandardJS

 (y muchos más)

Si es un desarrollador novato, comience con la hoja de trucos al comienzo de


este capítulo. Luego, puede buscar otras guías de estilo para recoger más ideas
y decidir cuál le gusta más.

Linters automatizados

Los linters son herramientas que pueden verificar automáticamente el estilo de


su código y hacer sugerencias de mejora.

Lo mejor de ellos es que la verificación de estilo también puede encontrar


algunos errores, como errores tipográficos en los nombres de variables o
funciones. Debido a esta característica, se recomienda utilizar un linter incluso si
no desea ceñirse a un "estilo de código" en particular.

Aquí hay algunas herramientas de pelusa conocidas:

 JSLint : uno de los primeros linters.

 JSHint : más configuraciones que JSLint.

 ESLint : probablemente el más nuevo.


Todos ellos pueden hacer el trabajo. El autor usa ESLint .

La mayoría de los linters están integrados con muchos editores populares:


simplemente habilite el complemento en el editor y configure el estilo.

Por ejemplo, para ESLint debe hacer lo siguiente:

1. Instalar Node.js .

2. Instale ESLint con el comando npm install -g eslint(npm es un

instalador de paquetes de JavaScript).

3. Cree un archivo de configuración con el nombre .eslintrcen la raíz de

su proyecto de JavaScript (en la carpeta que contiene todos sus archivos).

4. Instale / habilite el complemento para su editor que se integra con


ESLint. La mayoría de los editores tienen uno.

A continuación, se muestra un ejemplo de un .eslintrcarchivo:

"extends": "eslint:recommended",

"env": {

"browser": true,

"node": true,

"es6": true

},

"rules": {

"no-console": 0,

"indent": ["warning", 2]
}

Aquí la directiva "extends"denota que la configuración se basa en el conjunto

de ajustes “eslint: recommended”. Después de eso, especificamos el nuestro.

También es posible descargar conjuntos de reglas de estilo de la web y


ampliarlos. Consulte http://eslint.org/docs/user-guide/getting-started para
obtener más detalles sobre la instalación.

Además, ciertos IDE tienen linting incorporado, lo cual es conveniente pero no


tan personalizable como ESLint.

Resumen

Todas las reglas de sintaxis descritas en este capítulo (y en las guías de estilo a
las que se hace referencia) tienen como objetivo aumentar la legibilidad de su
código. Todos ellos son discutibles.

Cuando pensamos en escribir un código "mejor", las preguntas que debemos


hacernos son: "¿Qué hace que el código sea más legible y más fácil de
entender?" y "¿Qué puede ayudarnos a evitar errores?" Estos son los aspectos
principales a tener en cuenta al elegir y debatir estilos de código.

La lectura de guías de estilo populares le permitirá mantenerse actualizado con


las últimas ideas sobre las tendencias de estilo de código y las mejores
prácticas.
17. Comentarios

Como sabemos por el capítulo de estructura de código , los comentarios

pueden ser de una sola línea: comenzando con //y de varias líneas: /* ... */.

Normalmente los usamos para describir cómo y por qué funciona el código.

A primera vista, comentar puede ser obvio, pero los principiantes en


programación a menudo los usan incorrectamente.

Malos comentarios

Los novatos tienden a utilizar comentarios para explicar "lo que está pasando en
el código". Me gusta esto:

// This code will do this thing (...) and that thing (...)

// ...and who knows what else...

very;

complex;

code;

Pero en un buen código, la cantidad de tales comentarios "explicativos" debería


ser mínima. En serio, el código debería ser fácil de entender sin ellos.

Hay una gran regla al respecto: "si el código es tan poco claro que requiere un
comentario, entonces tal vez debería reescribirse".

Receta: funciones de factorización

A veces es beneficioso reemplazar un fragmento de código con una función,


como aquí:

function showPrimes(n) {
nextPrime:

for (let i = 2; i < n; i++) {

// check if i is a prime number

for (let j = 2; j < i; j++) {

if (i % j == 0) continue nextPrime;

alert(i);

La mejor variante, con una función factorizada isPrime:

function showPrimes(n) {

for (let i = 2; i < n; i++) {

if (!isPrime(i)) continue;

alert(i);

}
function isPrime(n) {

for (let i = 2; i < n; i++) {

if (n % i == 0) return false;

return true;

Ahora podemos entender el código fácilmente. La función en sí se convierte en


el comentario. Este código se denomina autodescriptivo .

Receta: crear funciones

Y si tenemos una "hoja de códigos" larga como esta:

// here we add whiskey

for(let i = 0; i < 10; i++) {

let drop = getWhiskey();

smell(drop);

add(drop, glass);

// here we add juice

for(let t = 0; t < 3; t++) {

let tomato = getTomato();


examine(tomato);

let juice = press(tomato);

add(juice, glass);

// ...

Entonces podría ser una mejor variante refactorizarlo en funciones como:

addWhiskey(glass);

addJuice(glass);

function addWhiskey(container) {

for(let i = 0; i < 10; i++) {

let drop = getWhiskey();

//...

function addJuice(container) {

for(let t = 0; t < 3; t++) {

let tomato = getTomato();

//...
}

Una vez más, las funciones mismas dicen lo que está sucediendo. No hay nada
que comentar. Y también la estructura del código es mejor cuando se
divide. Está claro lo que hace cada función, lo que necesita y lo que devuelve.

En realidad, no podemos evitar por completo los comentarios


"explicativos". Hay algoritmos complejos. Y hay "ajustes" inteligentes con fines
de optimización. Pero, en general, deberíamos tratar de mantener el código
simple y autodescriptivo.

Buenos comentarios

Entonces, los comentarios explicativos suelen ser malos. ¿Qué comentarios son


buenos?

Describe la arquitectura

Proporcione una descripción general de alto nivel de los componentes,


cómo interactúan, cuál es el flujo de control en varias situaciones ... En
resumen, la vista de pájaro del código. Hay un lenguaje
especial UML para construir diagramas de arquitectura de alto nivel que
explican el código. Definitivamente vale la pena estudiarlo.

Parámetros y uso de la función del documento

Hay una sintaxis especial JSDoc para documentar una función: uso,


parámetros, valor devuelto.

Por ejemplo:

/**
* Returns x raised to the n-th power.

* @param {number} x The number to raise.

* @param {number} n The power, must be a natural number.

* @return {number} x raised to the n-th power.

*/

function pow(x, n) {

...

Dichos comentarios nos permiten comprender el propósito de la función y


usarla de la manera correcta sin mirar en su código.

Por cierto, muchos editores como WebStorm también pueden entenderlos y


usarlos para proporcionar autocompletar y alguna verificación automática de
código.

Además, existen herramientas como JSDoc 3 que pueden generar


documentación HTML a partir de los comentarios. Puede leer más información
sobre JSDoc en http://usejsdoc.org/ .

¿Por qué se resuelve la tarea de esta manera?

Lo que está escrito es importante. Pero lo que no está escrito puede ser


aún más importante para comprender lo que está sucediendo. ¿Por qué
la tarea se resuelve exactamente de esta manera? El código no da
respuesta.
Si hay muchas formas de resolver la tarea, ¿por qué esta? Especialmente
cuando no es el más obvio.

Sin tales comentarios, es posible la siguiente situación:

1. Usted (o su colega) abre el código escrito hace algún tiempo y ve que es


"subóptimo".

2. Piensas: "Qué estúpido era entonces, y cuánto más inteligente soy


ahora", y reescribe usando la variante "más obvia y correcta".

3. … La necesidad de reescribir era buena. Pero en el proceso se ve que en


realidad falta la solución "más obvia". Incluso recuerdas vagamente por
qué, porque ya lo probaste hace mucho tiempo. Vuelve a la variante
correcta, pero se perdió el tiempo.

Los comentarios que explican la solución son muy importantes. Ayudan a


continuar el desarrollo de la manera correcta.

¿Alguna característica sutil del código? ¿Dónde se utilizan?

Si el código tiene algo sutil y contrario a la intuición, definitivamente vale


la pena comentarlo.

Resumen

Un signo importante de un buen desarrollador son los comentarios: su


presencia e incluso su ausencia.

Los buenos comentarios nos permiten mantener bien el código, volver a él


después de un tiempo y usarlo de manera más efectiva.

Comenta esto:

 Arquitectura general, vista de alto nivel.


 Uso de funciones.

 Soluciones importantes, especialmente cuando no son inmediatamente


obvias.

Evite los comentarios:

 Que dicen "cómo funciona el código" y "qué hace".

 Colóquelos solo si es imposible hacer que el código sea tan simple y


autodescriptivo que no los requiera.

Los comentarios también se utilizan para herramientas de documentación


automática como JSDoc3: los leen y generan documentos HTML (o documentos
en otro formato).
18. Objetos

Como sabemos por el capítulo Tipos de datos , hay ocho tipos de datos en


JavaScript. Siete de ellos se denominan "primitivos", porque sus valores
contienen sólo una cosa (ya sea una cadena o un número o lo que sea).

Por el contrario, los objetos se utilizan para almacenar colecciones con clave de
varios datos y entidades más complejas. En JavaScript, los objetos penetran en
casi todos los aspectos del lenguaje. Por lo tanto, primero debemos entenderlos
antes de profundizar en cualquier otro lugar.

Se puede crear un objeto con corchetes de figura {…}con una lista opcional

de propiedades . Una propiedad es un par "clave: valor", donde keyes una

cadena (también llamada "nombre de propiedad"), y valuepuede ser cualquier

cosa.

Podemos imaginar un objeto como un armario con archivos firmados. Cada


dato se almacena en su archivo mediante la clave. Es fácil encontrar un archivo
por su nombre o agregar / eliminar un archivo.

Se puede crear un objeto vacío ("gabinete vacío") usando una de dos sintaxis:

let user = new Object(); // "object constructor" syntax

let user = {}; // "object literal" syntax

Por lo general, {...}se utilizan los corchetes de cifras . Esa declaración se

llama literal de objeto .

Literales y propiedades

Podemos poner inmediatamente algunas propiedades en {...}pares "clave:

valor":
let user = { // an object

name: "John", // by key "name" store value "John"

age: 30 // by key "age" store value 30

};

Una propiedad tiene una clave (también conocida como "nombre" o

"identificador") antes de los dos puntos ":"y un valor a la derecha.

En el userobjeto, hay dos propiedades:

1. La primera propiedad tiene el nombre "name"y el valor "John".

2. El segundo tiene el nombre "age"y el valor 30.

El userobjeto resultante se puede imaginar como un gabinete con dos archivos

firmados etiquetados como "nombre" y "edad".

Podemos agregar, eliminar y leer archivos en cualquier momento.

Los valores de propiedad son accesibles usando la notación de puntos:

// get property values of the object:

alert( user.name ); // John

alert( user.age ); // 30

El valor puede ser de cualquier tipo. Agreguemos uno booleano:

user.isAdmin = true;

Para eliminar una propiedad, podemos usar el deleteoperador:

delete user.age;
También podemos usar nombres de propiedades de varias palabras, pero luego
deben citarse:

let user = {

name: "John",

age: 30,

"likes birds": true // multiword property name must be quoted

};

La última propiedad de la lista puede terminar con una coma:

let user = {

name: "John",

age: 30,

Eso se llama una coma "final" o "colgante". Hace que sea más fácil agregar /
eliminar / mover propiedades, porque todas las líneas se vuelven iguales.

El objeto con constante se puede cambiar

Tenga en cuenta: un objeto declarado como const se puede modificar.

Por ejemplo:

const user = {

name: "John"

};
user.name = "Pete"; // (*)

alert(user.name); // Pete

Podría parecer que la línea (*)causaría un error, pero no. El constfija el valor

de user, pero no su contenido.

El constdaría un error solo si intentamos establecer user=...como un todo.

Hay otra forma de hacer que las propiedades de los objetos sean constantes, la
cubriremos más adelante en el capítulo Indicadores y descriptores de
propiedades .

Corchetes

Para propiedades de varias palabras, el acceso de puntos no funciona:

// this would give a syntax error

user.likes birds = true

JavaScript no entiende eso. Piensa que abordamos user.likesy luego da un

error de sintaxis cuando se encuentra inesperado birds.

El punto requiere que la clave sea un identificador de variable válido. Eso


implica: no contiene espacios, no comienza con un dígito y no incluye

caracteres especiales ( $y _están permitidos).

Existe una "notación de corchetes" alternativa que funciona con cualquier


cadena:

let user = {};


// set

user["likes birds"] = true;

// get

alert(user["likes birds"]); // true

// delete

delete user["likes birds"];

Ahora todo esta bien. Tenga en cuenta que la cadena dentro de los corchetes
está correctamente citada (cualquier tipo de comillas servirá).

Los corchetes también proporcionan una forma de obtener el nombre de la


propiedad como resultado de cualquier expresión, a diferencia de una cadena
literal, como de una variable de la siguiente manera:

let key = "likes birds";

// same as user["likes birds"] = true;

user[key] = true;

Aquí, la variable keypuede calcularse en tiempo de ejecución o depender de la

entrada del usuario. Y luego lo usamos para acceder a la propiedad. Eso nos da


mucha flexibilidad.

Por ejemplo:

let user = {
name: "John",

age: 30

};

let key = prompt("What do you want to know about the user?",


"name");

// access by variable

alert( user[key] ); // John (if enter "name")

La notación de puntos no se puede utilizar de forma similar:

let user = {

name: "John",

age: 30

};

let key = "name";

alert( user.key ) // undefined

Propiedades calculadas

Podemos usar corchetes en un objeto literal, al crear un objeto. Eso se


llama propiedades calculadas .

Por ejemplo:
let fruit = prompt("Which fruit to buy?", "apple");

let bag = {

[fruit]: 5, // the name of the property is taken from the


variable fruit

};

alert( bag.apple ); // 5 if fruit="apple"

El significado de una propiedad calculada es simple: [fruit]significa que se

debe tomar el nombre de la propiedad fruit.

Entonces, si entra un visitante "apple", bagse convertirá en {apple: 5}.

Básicamente, eso funciona igual que:

let fruit = prompt("Which fruit to buy?", "apple");

let bag = {};

// take property name from the fruit variable

bag[fruit] = 5;

… Pero se ve mejor.

Podemos usar expresiones más complejas entre corchetes:

let fruit = 'apple';

let bag = {
[fruit + 'Computers']: 5 // bag.appleComputers = 5

};

Los corchetes son mucho más poderosos que la notación de puntos. Permiten


cualquier nombre de propiedad y variable. Pero también son más engorrosos
de escribir.

Entonces, la mayoría de las veces, cuando los nombres de propiedad son


conocidos y simples, se usa el punto. Y si necesitamos algo más complejo,
cambiamos a corchetes.

Valor de propiedad taquigrafía

En código real, a menudo usamos variables existentes como valores para


nombres de propiedades.

Por ejemplo:

function makeUser(name, age) {

return {

name: name,

age: age,

// ...other properties

};

let user = makeUser("John", 30);

alert(user.name); // John
En el ejemplo anterior, las propiedades tienen los mismos nombres que las
variables. El caso de uso de hacer una propiedad a partir de una variable es tan
común que hay una abreviatura de valor de propiedad especial para acortarlo.

En lugar de name:namesimplemente podemos escribir name, así:

function makeUser(name, age) {

return {

name, // same as name: name

age, // same as age: age

// ...

};

Podemos usar propiedades normales y abreviaturas en el mismo objeto:

let user = {

name, // same as name:name

age: 30

};

Limitaciones de los nombres de propiedad

Como ya sabemos, una variable no puede tener un nombre igual a una de las
palabras reservadas en el idioma como "para", "dejar", "volver", etc.

Pero para una propiedad de objeto, no existe tal restricción:

// these properties are all right

let obj = {
for: 1,

let: 2,

return: 3

};

alert( obj.for + obj.let + obj.return ); // 6

En resumen, no existen limitaciones en los nombres de las propiedades. Pueden


ser cualquier cadena o símbolo (un tipo especial de identificadores, que se
tratará más adelante).

Otros tipos se convierten automáticamente en cadenas.

Por ejemplo, un número se 0convierte en una cadena "0"cuando se usa como

clave de propiedad:

let obj = {

0: "test" // same as "0": "test"

};

// both alerts access the same property (the number 0 is


converted to string "0")

alert( obj["0"] ); // test

alert( obj[0] ); // test (same property)

Hay un problema menor con una propiedad especial nombrada __proto__. No

podemos establecerlo en un valor que no sea de objeto:


let obj = {};

obj.__proto__ = 5; // assign a number

alert(obj.__proto__); // [object Object] - the value is an


object, didn't work as intended

Como vemos en el código, 5se ignora la asignación a una primitiva .

Cubriremos la naturaleza especial de __proto__en los capítulos siguientes y

sugeriremos las formas de corregir dicho comportamiento.

Prueba de existencia de propiedad, operador "en"

Una característica notable de los objetos en JavaScript, en comparación con


muchos otros lenguajes, es que es posible acceder a cualquier propiedad. ¡No
habrá error si la propiedad no existe!

Leer una propiedad inexistente simplemente regresa undefined. Entonces

podemos probar fácilmente si la propiedad existe:

let user = {};

alert( user.noSuchProperty === undefined ); // true means "no


such property"

También hay un operador especial "in"para eso.

La sintaxis es:

"key" in object

Por ejemplo:

let user = { name: "John", age: 30 };


alert( "age" in user ); // true, user.age exists

alert( "blabla" in user ); // false, user.blabla doesn't exist

Tenga en cuenta que en el lado izquierdo indebe haber un nombre de

propiedad . Suele ser una cadena entre comillas.

Si omitimos las comillas, eso significa una variable, debe contener el nombre
real que se va a probar. Por ejemplo:

let user = { age: 30 };

let key = "age";

alert( key in user ); // true, property "age" exists

¿Por qué existe el inoperador? ¿No es suficiente para comparar undefined?

Bueno, la mayoría de las veces la comparación undefinedfunciona bien. Pero

hay un caso especial cuando falla, pero "in"funciona correctamente.

Es cuando existe una propiedad de objeto, pero almacena undefined:

let obj = {

test: undefined

};

alert( obj.test ); // it's undefined, so - no such property?

alert( "test" in obj ); // true, the property does exist!


En el código anterior, la propiedad obj.testexiste técnicamente. Entonces

el inoperador trabaja bien.

Situaciones como esta ocurren muy raramente, porque undefinedno deben

asignarse explícitamente. Usamos principalmente nullpara valores

"desconocidos" o "vacíos". Entonces, el inoperador es un invitado exótico en el

código.

El bucle "for ... in"

Para caminar sobre todas las teclas de un objeto, existe una forma especial del

bucle: for..in. Esto es algo completamente diferente del for(;;)constructo

que estudiamos antes.

La sintaxis:

for (key in object) {

// executes the body for each key among object properties

Por ejemplo, generemos todas las propiedades de user:

let user = {

name: "John",

age: 30,

isAdmin: true

};

for (let key in user) {


// keys

alert( key ); // name, age, isAdmin

// values for the keys

alert( user[key] ); // John, 30, true

Tenga en cuenta que todas las construcciones "for" nos permiten declarar la
variable de bucle dentro del bucle, como let keyaquí.

Además, podríamos usar otro nombre de variable aquí en lugar de key. Por

ejemplo, "for (let prop in obj)"también se usa ampliamente.

Ordenado como un objeto

¿Están ordenados los objetos? En otras palabras, si recorremos un objeto,


¿obtenemos todas las propiedades en el mismo orden en que fueron
agregadas? ¿Podemos confiar en esto?

La respuesta corta es: “ordenada de una manera especial”: las propiedades


enteras están ordenadas, otras aparecen en orden de creación. Los detalles
siguen.

Como ejemplo, consideremos un objeto con los códigos telefónicos:

let codes = {

"49": "Germany",

"41": "Switzerland",

"44": "Great Britain",

// ..,
"1": "USA"

};

for (let code in codes) {

alert(code); // 1, 41, 44, 49

El objeto se puede usar para sugerir una lista de opciones al usuario. Si estamos
creando un sitio principalmente para la audiencia alemana, probablemente

queramos 49ser los primeros.

Pero si ejecutamos el código, vemos una imagen totalmente diferente:

 USA (1) va primero

 luego Suiza (41) y así sucesivamente.

Los códigos telefónicos van en orden ascendente, porque son números

enteros. Entonces vemos 1, 41, 44, 49.

¿Propiedades enteras? ¿Que es eso?

El término "propiedad de entero" aquí significa una cadena que se puede


convertir ay desde un entero sin cambios.

Entonces, "49" es un nombre de propiedad entero, porque cuando se


transforma en un número entero y viceversa, sigue siendo el mismo. Pero "+49"
y "1.2" no son:

// Math.trunc is a built-in function that removes the decimal


part
alert( String(Math.trunc(Number("49"))) ); // "49", same,
integer property

alert( String(Math.trunc(Number("+49"))) ); // "49", not same


"+49" ⇒ not integer property

alert( String(Math.trunc(Number("1.2"))) ); // "1", not same


"1.2" ⇒ not integer property

... Por otro lado, si las claves no son enteras, entonces se enumeran en el orden
de creación, por ejemplo:

let user = {

name: "John",

surname: "Smith"

};

user.age = 25; // add one more

// non-integer properties are listed in the creation order

for (let prop in user) {

alert( prop ); // name, surname, age

Entonces, para solucionar el problema con los códigos telefónicos, podemos

"hacer trampa" haciendo que los códigos no sean enteros. "+"Es

suficiente agregar un signo más antes de cada código.

Me gusta esto:

let codes = {
"+49": "Germany",

"+41": "Switzerland",

"+44": "Great Britain",

// ..,

"+1": "USA"

};

for (let code in codes) {

alert( +code ); // 49, 41, 44, 1

Ahora funciona según lo previsto.

Resumen

Los objetos son matrices asociativas con varias características especiales.

Almacenan propiedades (pares clave-valor), donde:

 Las claves de propiedad deben ser cadenas o símbolos (generalmente


cadenas).

 Los valores pueden ser de cualquier tipo.

Para acceder a una propiedad, podemos utilizar:

 La notación de puntos: obj.property.

 Notación de corchetes obj["property"]. Los corchetes permiten tomar

la clave de una variable, como obj[varWithKey].


Operadores adicionales:

 Para eliminar una propiedad: delete obj.prop.

 Para comprobar si existe una propiedad con la clave dada: "key" in

obj.

 Para iterar sobre un objeto: for (let key in obj)bucle.

Lo que hemos estudiado en este capítulo se llama "objeto simple", o

simplemente Object.

Hay muchos otros tipos de objetos en JavaScript:

 Array para almacenar colecciones de datos ordenadas,

 Date para almacenar la información sobre la fecha y la hora,

 Error para almacenar la información sobre un error.

 …Y así.

Tienen sus características especiales que estudiaremos más adelante. A veces,


las personas dicen algo como "Tipo de matriz" o "Tipo de fecha", pero
formalmente no son tipos propios, sino que pertenecen a un solo tipo de datos
de "objeto". Y lo amplían de diversas formas.

Los objetos en JavaScript son muy poderosos. Aquí acabamos de tocar la


superficie de un tema que es realmente enorme. Trabajaremos de cerca con los
objetos y aprenderemos más sobre ellos en otras partes del tutorial.
19. Copia de objetos, referencias

Una de las diferencias fundamentales de los objetos frente a los primitivos es


que se almacenan y copian "por referencia".

Los valores primitivos: cadenas, números, booleanos - se asignan / copian


"como un valor total".

Por ejemplo:

let message = "Hello!";

let phrase = message;

Como resultado, tenemos dos variables independientes, cada una almacena la

cadena "Hello!".

Los objetos no son así.

Una variable almacena no el objeto en sí, sino su "dirección en la memoria", en


otras palabras, "una referencia" a él.

Aquí está la imagen del objeto:

let user = {

name: "John"

};

Aquí, el objeto se almacena en algún lugar de la memoria. Y la


variable usertiene una "referencia" a ella.

Cuando se copia una variable de objeto, la referencia se copia, el objeto no se


duplica.

Por ejemplo:
let user = { name: "John" };

let admin = user; // copy the reference

Ahora tenemos dos variables, cada una con la referencia al mismo objeto:

Podemos utilizar cualquier variable para acceder al objeto y modificar su


contenido:

let user = { name: 'John' };

let admin = user;

admin.name = 'Pete'; // changed by the "admin" reference

alert(user.name); // 'Pete', changes are seen from the "user"


reference

El ejemplo anterior demuestra que solo hay un objeto. Como si tuviéramos un


armario con dos llaves y usáramos una de ellas ( admin) para meternos en

él. Entonces, si luego usamos otra tecla ( user) podemos ver cambios.

Comparación por referencia

Los operadores de igualdad ==e igualdad estricta ===para objetos funcionan

exactamente igual.

Dos objetos son iguales solo si son el mismo objeto.

Aquí dos variables hacen referencia al mismo objeto, por lo que son iguales:
let a = {};

let b = a; // copy the reference

alert( a == b ); // true, both variables reference the same


object

alert( a === b ); // true

Y aquí dos objetos independientes no son iguales, aunque ambos estén vacíos:

let a = {};

let b = {}; // two independent objects

alert( a == b ); // false

Para comparaciones como obj1 > obj2o para una comparación con un

primitivo obj == 5, los objetos se convierten en primitivos. Muy pronto

estudiaremos cómo funcionan las conversiones de objetos, pero a decir verdad,


estas comparaciones ocurren muy raramente, generalmente como resultado de
un error de codificación.

Clonación y fusión, Object.assign

Entonces, copiar una variable de objeto crea una referencia más al mismo
objeto.

Pero, ¿y si necesitamos duplicar un objeto? Crear una copia independiente, ¿un


clon?
Eso también es factible, pero un poco más difícil, porque no hay un método
incorporado para eso en JavaScript. En realidad, eso rara vez se necesita. Copiar
por referencia es bueno la mayor parte del tiempo.

Pero si realmente queremos eso, entonces necesitamos crear un nuevo objeto y


replicar la estructura del existente iterando sobre sus propiedades y copiándolas
en el nivel primitivo.

Me gusta esto:

let user = {

name: "John",

age: 30

};

let clone = {}; // the new empty object

// let's copy all user properties into it

for (let key in user) {

clone[key] = user[key];

// now clone is a fully independent object with the same content

clone.name = "Pete"; // changed the data in it


alert( user.name ); // still John in the original object

También podemos usar el método Object.assign para eso.

La sintaxis es:

Object.assign(dest, [src1, src2, src3...])

 El primer argumento destes un objeto de destino.

 Otros argumentos src1, ..., srcN(pueden ser tantos como sean

necesarios) son objetos fuente.

 Copia las propiedades de todos los objetos de origen src1, ...,

srcNen el destino dest. En otras palabras, las propiedades de todos los

argumentos a partir del segundo se copian en el primer objeto.

 Vuelve la llamada dest.

Por ejemplo, podemos usarlo para fusionar varios objetos en uno:

let user = { name: "John" };

let permissions1 = { canView: true };

let permissions2 = { canEdit: true };

// copies all properties from permissions1 and permissions2 into


user

Object.assign(user, permissions1, permissions2);

// now user = { name: "John", canView: true, canEdit: true }


Si el nombre de propiedad copiado ya existe, se sobrescribe:

let user = { name: "John" };

Object.assign(user, { name: "Pete" });

alert(user.name); // now user = { name: "Pete" }

También podemos usar Object.assignpara reemplazar el for..inbucle para

una clonación simple:

let user = {

name: "John",

age: 30

};

let clone = Object.assign({}, user);

Copia todas las propiedades de useren el objeto vacío y lo devuelve.

Clonación anidada

Hasta ahora asumimos que todas las propiedades de userson primitivas. Pero

las propiedades pueden ser referencias a otros objetos. Qué hacer con ellos?

Me gusta esto:

let user = {

name: "John",
sizes: {

height: 182,

width: 50

};

alert( user.sizes.height ); // 182

Ahora no es suficiente copiar clone.sizes = user.sizes,

porque user.sizeses un objeto, se copiará por

referencia. Entonces cloney usercompartiremos los mismos tamaños:

Me gusta esto:

let user = {

name: "John",

sizes: {

height: 182,

width: 50

};

let clone = Object.assign({}, user);

alert( user.sizes === clone.sizes ); // true, same object


// user and clone share sizes

user.sizes.width++; // change a property from one place

alert(clone.sizes.width); // 51, see the result from the other


one

Para solucionar eso, debemos usar el ciclo de clonación que examina cada valor
de user[key]y, si es un objeto, luego replicar su estructura también. Eso se

denomina "clonación profunda".

Podemos usar la recursividad para implementarlo. O, para no reinventar la


rueda, tome una implementación existente, por ejemplo _.cloneDeep (obj) de la
biblioteca JavaScript lodash .

Resumen

Los objetos se asignan y copian por referencia. En otras palabras, una variable
almacena no el "valor del objeto", sino una "referencia" (dirección en la
memoria) para el valor. Entonces, copiar dicha variable o pasarla como un
argumento de función copia esa referencia, no el objeto.

Todas las operaciones a través de referencias copiadas (como agregar / eliminar


propiedades) se realizan en el mismo objeto.

Para hacer una "copia real" (un clon) podemos utilizar Object.assignpara la

llamada "copia superficial" (los objetos anidados se copian por referencia) o una
función de "clonación profunda", como _.cloneDeep (obj) .
20. Recolección de basura

La gestión de la memoria en JavaScript se realiza de forma automática e


invisible para nosotros. Creamos primitivas, objetos, funciones… Todo eso toma
memoria.

¿Qué sucede cuando algo ya no se necesita? ¿Cómo lo descubre y limpia el


motor JavaScript?

Accesibilidad

El concepto principal de gestión de memoria en JavaScript es la accesibilidad .

En pocas palabras, los valores "alcanzables" son aquellos que son accesibles o
utilizables de alguna manera. Se garantiza que se almacenarán en la memoria.

1. Existe un conjunto básico de valores inherentemente alcanzables que no


se pueden eliminar por razones obvias.

Por ejemplo:

o Variables locales y parámetros de la función actual.

o Variables y parámetros para otras funciones en la cadena actual de

llamadas anidadas.

o Variables globales.

o (hay algunos otros internos también)

Estos valores se denominan raíces .

2. Cualquier otro valor se considera accesible si es accesible desde una raíz


mediante una referencia o una cadena de referencias.
Por ejemplo, si hay un objeto en una variable global y ese objeto tiene una
propiedad que hace referencia a otro objeto, ese objeto se considera
accesible. Y aquellos a los que hace referencia también son accesibles. Ejemplos
detallados a seguir.

Hay un proceso en segundo plano en el motor de JavaScript que se


llama recolector de basura . Supervisa todos los objetos y elimina los que se han
vuelto inalcanzables.

Un simple ejemplo

Este es el ejemplo más simple:

// user has a reference to the object

let user = {

name: "John"

};

Aquí la flecha muestra una referencia de objeto. La variable global

hace "user"referencia al objeto {name: "John"}(lo llamaremos John para

abreviar). La "name"propiedad de John almacena un primitivo, por lo que está

pintado dentro del objeto.

Si userse sobrescribe el valor de , se pierde la referencia:

user = null;

Ahora John se vuelve inalcanzable. No hay forma de acceder a él, no hay


referencias a él. El recolector de basura desechará los datos y liberará la
memoria.

Dos referencias
Ahora imaginemos que copiamos la referencia de usera admin:

// user has a reference to the object

let user = {

name: "John"

};

let admin = user;

Ahora si hacemos lo mismo:

user = null;

… Entonces, el objeto todavía es accesible a través de adminuna variable global,

por lo que está en la memoria. Si admintambién sobrescribimos , se puede

eliminar.

Objetos interconectados

Ahora un ejemplo más complejo. La familia:

function marry(man, woman) {

woman.husband = man;

man.wife = woman;

return {

father: man,

mother: woman
}

let family = marry({

name: "John"

}, {

name: "Ann"

});

La función marry"casa" dos objetos dándoles referencias entre sí y devuelve un

nuevo objeto que los contiene a ambos.

La estructura de memoria resultante:

A partir de ahora, todos los objetos son accesibles.

Ahora eliminemos dos referencias:

delete family.father;

delete family.mother.husband;

No es suficiente eliminar solo una de estas dos referencias, porque todos los
objetos seguirían siendo accesibles.

Pero si eliminamos ambos, entonces podemos ver que John ya no tiene ninguna
referencia entrante:

Las referencias salientes no importan. Solo los entrantes pueden hacer que un


objeto sea accesible. Entonces, John ahora es inalcanzable y se eliminará de la
memoria con todos sus datos que también se volvieron inaccesibles.
Después de la recolección de basura:

Isla inalcanzable

Es posible que toda la isla de objetos interconectados se vuelva inalcanzable y


se elimine de la memoria.

El objeto de origen es el mismo que el anterior. Luego:

family = null;

La imagen en memoria se convierte en:

Este ejemplo demuestra la importancia del concepto de accesibilidad.

Es obvio que John y Ann todavía están vinculados, ambos tienen referencias
entrantes. Pero eso no es suficiente.

El "family"objeto anterior se ha desvinculado de la raíz, ya no hay ninguna

referencia a él, por lo que toda la isla se vuelve inalcanzable y se eliminará.

Algoritmos internos

El algoritmo básico de recolección de basura se llama "marcar y barrer".

Los siguientes pasos de "recolección de basura" se realizan con regularidad:

 El recolector de basura echa raíces y las “marca” (recuerda).

 Luego visita y "marca" todas las referencias de ellos.

 Luego visita los objetos marcados y marca sus referencias. Todos los


objetos visitados se recuerdan, para no visitar el mismo objeto dos veces en el
futuro.

 … Y así sucesivamente hasta que se visiten todas las referencias


accesibles (desde las raíces).
 Se eliminan todos los objetos excepto los marcados.

Por ejemplo, deje que nuestra estructura de objeto se vea así:

Podemos ver claramente una "isla inalcanzable" al lado derecho. Ahora veamos


cómo lo maneja el recolector de basura “marcar y barrer”.

El primer paso marca las raíces:

Luego se marcan sus referencias:

… Y sus referencias, si bien es posible:

Ahora los objetos que no se pudieron visitar en el proceso se consideran


inalcanzables y se eliminarán:

También podemos imaginar el proceso como derramar un enorme cubo de


pintura desde las raíces, que fluye a través de todas las referencias y marca
todos los objetos alcanzables. A continuación, se eliminan los que no están
marcados.

Ese es el concepto de cómo funciona la recolección de basura. Los motores de


JavaScript aplican muchas optimizaciones para que se ejecute más rápido y no
afecte la ejecución.

Algunas de las optimizaciones:

 Colección generacional : los objetos se dividen en dos conjuntos:


"nuevos" y "antiguos". Aparecen muchos objetos, hacen su trabajo y mueren
rápido, se pueden limpiar agresivamente. Aquellos que sobreviven lo suficiente,
se vuelven "viejos" y se examinan con menos frecuencia.

 Colección incremental : si hay muchos objetos e intentamos caminar y


marcar todo el conjunto de objetos a la vez, puede llevar algún tiempo e
introducir retrasos visibles en la ejecución. Entonces, el motor intenta dividir la
recolección de basura en pedazos. Luego, las piezas se ejecutan una a una, por
separado. Eso requiere una contabilidad adicional entre ellos para rastrear los
cambios, pero tenemos muchos retrasos pequeños en lugar de uno grande.

 Recolección en tiempo de inactividad : el recolector de basura intenta


ejecutarse solo mientras la CPU está inactiva, para reducir el posible efecto en la
ejecución.

Existen otras optimizaciones y sabores de algoritmos de recolección de


basura. Por mucho que me gustaría describirlos aquí, tengo que esperar, porque
diferentes motores implementan diferentes ajustes y técnicas. Y, lo que es aún
más importante, las cosas cambian a medida que se desarrollan los motores,
por lo que estudiar más a fondo "por adelantado", sin una necesidad real,
probablemente no valga la pena. A menos que, por supuesto, sea una cuestión
de puro interés, a continuación encontrará algunos enlaces para usted.

Resumen

Las principales cosas que debes saber:

 La recolección de basura se realiza automáticamente. No podemos


forzarlo ni prevenirlo.

 Los objetos se conservan en la memoria mientras están accesibles.

 Ser referenciado no es lo mismo que ser accesible (desde una raíz): un


paquete de objetos interconectados puede volverse inalcanzable como un todo.

Los motores modernos implementan algoritmos avanzados de recolección de


basura.

Un libro general "El manual de recolección de basura: el arte de la gestión


automática de la memoria" (R. Jones et al) cubre algunos de ellos.
Si está familiarizado con la programación de bajo nivel, encontrará información
más detallada sobre el recolector de basura V8 en el artículo Un recorrido por
V8: Recolección de basura .

El blog V8 también publica artículos sobre cambios en la gestión de la memoria


de vez en cuando. Naturalmente, para aprender la recolección de basura, es
mejor que se prepare aprendiendo sobre los componentes internos de V8 en
general y lea el blog de Vyacheslav Egorov, quien trabajó como uno de los
ingenieros de V8. Digo: "V8", porque se cubre mejor con artículos en
Internet. Para otros motores, muchos enfoques son similares, pero la
recolección de basura difiere en muchos aspectos.

El conocimiento profundo de los motores es bueno cuando necesita


optimizaciones de bajo nivel. Sería aconsejable planificarlo como el siguiente
paso después de que se haya familiarizado con el idioma.

21. Métodos de objeto, "esto"

Los objetos generalmente se crean para representar entidades del mundo real,
como usuarios, pedidos, etc.

let user = {

name: "John",

age: 30

};

Y, en el mundo real, un usuario puede actuar : seleccionar algo del carrito de


compras, iniciar sesión, cerrar sesión, etc.

Las acciones están representadas en JavaScript por funciones en propiedades.


Ejemplos de métodos

Para empezar, enseñemos usera saludar:

let user = {

name: "John",

age: 30

};

user.sayHi = function() {

alert("Hello!");

};

user.sayHi(); // Hello!

Aquí acabamos de usar una expresión de función para crear la función y


asignarla a la propiedad user.sayHidel objeto.

Entonces podemos llamarlo. ¡El usuario ahora puede hablar!

Una función que es propiedad de un objeto se denomina método .

Entonces, aquí tenemos un método sayHidel objeto user.

Por supuesto, podríamos usar una función declarada previamente como


método, como este:

let user = {

// ...
};

// first, declare

function sayHi() {

alert("Hello!");

};

// then add as a method

user.sayHi = sayHi;

user.sayHi(); // Hello!

Programación orientada a objetos

Cuando escribimos nuestro código usando objetos para representar entidades,


eso se llama programación orientada a objetos , en resumen: "OOP".

La programación orientada a objetos es una gran cosa, una ciencia interesante


en sí misma. ¿Cómo elegir las entidades adecuadas? ¿Cómo organizar la
interacción entre ellos? Eso es arquitectura, y hay grandes libros sobre ese tema,
como "Patrones de diseño: elementos de software orientado a objetos
reutilizable" de E. Gamma, R. Helm, R. Johnson, J. Vissides o "Análisis y diseño
orientado a objetos con Aplicaciones ”de G. Booch, y más.

Método abreviado

Existe una sintaxis más corta para los métodos en un objeto literal:
// these objects do the same

user = {

sayHi: function() {

alert("Hello");

};

// method shorthand looks better, right?

user = {

sayHi() { // same as "sayHi: function()"

alert("Hello");

};

Como se demostró, podemos omitir "function"y simplemente

escribir sayHi().

A decir verdad, las notaciones no son completamente idénticas. Existen


diferencias sutiles relacionadas con la herencia de objetos (que se tratarán más
adelante), pero por ahora no importan. En casi todos los casos se prefiere la
sintaxis más corta.

"Esto" en métodos
Es común que un método de objeto necesite acceder a la información
almacenada en el objeto para hacer su trabajo.

Por ejemplo, el código interno user.sayHi()puede necesitar el nombre

del user.

Para acceder al objeto, un método puede utilizar la thispalabra clave.

El valor de thises el objeto "antes del punto", el que se usa para llamar al

método.

Por ejemplo:

let user = {

name: "John",

age: 30,

sayHi() {

// "this" is the "current object"

alert(this.name);

};

user.sayHi(); // John

Aquí durante la ejecución de user.sayHi(), el valor de thisserá user.


Técnicamente, también es posible acceder al objeto sin this, haciendo

referencia a él a través de la variable externa:

let user = {

name: "John",

age: 30,

sayHi() {

alert(user.name); // "user" instead of "this"

};

… Pero tal código no es confiable. Si decidimos copiar usera otra variable, por

ejemplo, admin = usery sobrescribir usercon otra cosa, entonces accederá al

objeto incorrecto.

Eso se demuestra a continuación:

let user = {

name: "John",

age: 30,

sayHi() {

alert( user.name ); // leads to an error

}
};

let admin = user;

user = null; // overwrite to make things obvious

admin.sayHi(); // Whoops! inside sayHi(), the old name is used!


error!

Si usáramos en this.namelugar de user.namedentro de alert, entonces el

código funcionaría.

"Esto" no está obligado

En JavaScript, la palabra clave se thiscomporta a diferencia de la mayoría de

los otros lenguajes de programación. Se puede utilizar en cualquier función.

No hay ningún error de sintaxis en el siguiente ejemplo:

function sayHi() {

alert( this.name );

El valor de thisse evalúa durante el tiempo de ejecución, según el contexto.

Por ejemplo, aquí se asigna la misma función a dos objetos diferentes y tiene un
"esto" diferente en las llamadas:

let user = { name: "John" };


let admin = { name: "Admin" };

function sayHi() {

alert( this.name );

// use the same function in two objects

user.f = sayHi;

admin.f = sayHi;

// these calls have different this

// "this" inside the function is the object "before the dot"

user.f(); // John (this == user)

admin.f(); // Admin (this == admin)

admin['f'](); // Admin (dot or square brackets access the method


– doesn't matter)

La regla es simple: si obj.f()se llama, entonces thises objdurante la llamada

de f. Entonces es usero adminen el ejemplo anterior.

Llamando sin un objeto: this == undefined

Incluso podemos llamar a la función sin ningún objeto:

function sayHi() {
alert(this);

sayHi(); // undefined

En este caso thisestá undefineden modo estricto. Si intentamos

acceder this.name, habrá un error.

En modo no estricto, el valor de thisen tal caso será el objeto

global ( windowen un navegador, lo veremos más adelante en el capítulo Objeto

global ). Este es un comportamiento histórico que "use strict"arregla.

Por lo general, dicha llamada es un error de programación. Si hay thisuna

función dentro, espera ser llamada en un contexto de objeto.

Las consecuencias de desatar this

Si viene de otro lenguaje de programación, entonces probablemente esté

acostumbrado a la idea de un "límite this", donde los métodos definidos en un

objeto siempre hacen thisreferencia a ese objeto.

En JavaScript thises "gratuito", su valor se evalúa en el momento de la llamada

y no depende de dónde se declaró el método, sino de qué objeto está "antes


del punto".

El concepto de tiempo de ejecución evaluado thistiene ventajas y

desventajas. Por un lado, una función se puede reutilizar para diferentes


objetos. Por otro lado, la mayor flexibilidad crea más posibilidades de errores.

Aquí nuestra posición no es juzgar si esta decisión de diseño de lenguaje es


buena o mala. Entenderemos cómo trabajar con él, cómo obtener beneficios y
evitar problemas.
Las funciones de flecha no tienen "esto"

Las funciones de flecha son especiales: no tienen su propia función this. Si

hacemos referencia thisa una función de este tipo, se toma de la función

"normal" externa.

Por ejemplo, aquí se arrow()usa thisel user.sayHi()método externo :

let user = {

firstName: "Ilya",

sayHi() {

let arrow = () => alert(this.firstName);

arrow();

};

user.sayHi(); // Ilya

Esa es una característica especial de las funciones de flecha, es útil cuando en


realidad no queremos tener una separada this, sino tomarla del contexto

externo. Más adelante en el capítulo Funciones de flecha revisadas ,


profundizaremos en las funciones de flecha.

Resumen

 Las funciones que se almacenan en las propiedades del objeto se


denominan "métodos".

 Los métodos permiten que los objetos "actúen"

como object.doSomething().
 Los métodos pueden hacer referencia al objeto como this.

El valor de thisse define en tiempo de ejecución.

 Cuando se declara una función, se puede usar this, pero eso thisno

tiene valor hasta que se llama a la función.

 Una función se puede copiar entre objetos.

 Cuando se llama a una función en la sintaxis del

"método":, object.method()el valor de thisdurante la llamada es object.

Tenga en cuenta que las funciones de flecha son especiales: no

tienen this. Cuando thisse accede dentro de una función de flecha, se toma

desde fuera.
22. Constructor, operador "nuevo"

La {...}sintaxis regular permite crear un objeto. Pero a menudo necesitamos

crear muchos objetos similares, como múltiples usuarios o elementos de menú,


etc.

Eso se puede hacer usando funciones constructoras y el "new"operador.

Función constructora

Las funciones de constructor técnicamente son funciones regulares. Sin


embargo, hay dos convenciones:

1. Se nombran con mayúscula primero.

2. Deben ejecutarse solo con "new"operador.

Por ejemplo:

function User(name) {

this.name = name;

this.isAdmin = false;

let user = new User("Jack");

alert(user.name); // Jack

alert(user.isAdmin); // false

Cuando se ejecuta una función con new, realiza los siguientes pasos:


1. Se crea y se asigna un nuevo objeto vacío this.

2. Se ejecuta el cuerpo de la función. Por lo general, lo modifica this, le

agrega nuevas propiedades.

3. Se thisdevuelve el valor de .

En otras palabras, new User(...)hace algo como:

function User(name) {

// this = {}; (implicitly)

// add properties to this

this.name = name;

this.isAdmin = false;

// return this; (implicitly)

Entonces let user = new User("Jack")da el mismo resultado que:

let user = {

name: "Jack",

isAdmin: false

};

Ahora, si queremos crear otros usuarios, podemos llamar new

User("Ann"), new User("Alice")y así sucesivamente. Mucho más corto que

usar literales cada vez, y también fácil de leer.


Ese es el propósito principal de los constructores: implementar código de
creación de objetos reutilizable.

Observemos una vez más: técnicamente, cualquier función se puede usar como

constructor. Es decir: se puede ejecutar con cualquier función newy ejecutará el

algoritmo anterior. La "letra mayúscula primero" es un acuerdo común, para


dejar en claro que se debe ejecutar una función new.

nueva función () {…}

Si tenemos muchas líneas de código sobre la creación de un solo objeto


complejo, podemos envolverlas en la función constructora, así:

let user = new function() {

this.name = "John";

this.isAdmin = false;

// ...other code for user creation

// maybe complex logic and statements

// local variables etc

};

No se puede volver a llamar al constructor, porque no se guarda en ningún


lugar, solo se crea y se llama. Entonces, este truco tiene como objetivo
encapsular el código que construye el objeto único, sin reutilización futura.

Prueba del modo de constructor: new.target

Cosas avanzadas
La sintaxis de esta sección rara vez se usa, omítala a menos que quieras saberlo
todo.

Dentro de una función, podemos verificar si fue llamada con newo sin ella,

usando una new.targetpropiedad especial .

Está vacío para llamadas regulares y es igual a la función si se llama con new:

function User() {

alert(new.target);

// without "new":

User(); // undefined

// with "new":

new User(); // function User { ... }

Eso se puede usar dentro de la función para saber si se llamó con new, "en

modo constructor", o sin él, "en modo regular".

También podemos hacer newllamadas regulares para hacer lo mismo, así:

function User(name) {

if (!new.target) { // if you run me without new

return new User(name); // ...I will add new for you

}
this.name = name;

let john = User("John"); // redirects call to new User

alert(john.name); // John

Este enfoque se utiliza a veces en bibliotecas para hacer que la sintaxis sea más
flexible. Para que la gente pueda llamar a la función con o sin new, y todavía

funciona.

Sin embargo, probablemente no sea algo bueno para usar en todas partes,

porque omitir newhace que sea un poco menos obvio lo que está

sucediendo. Con newtodos sabemos que se está creando el nuevo objeto.

Retorno de constructores

Por lo general, los constructores no tienen una returndeclaración. Su tarea es

escribir todas las cosas necesarias thisy automáticamente se convierte en el

resultado.

Pero si hay una returndeclaración, entonces la regla es simple:

 Si returnse llama con un objeto, se devuelve el objeto en lugar de this.

 Si returnse llama con una primitiva, se ignora.

En otras palabras, returncon un objeto devuelve ese objeto, en todos los

demás casos thisse devuelve.

Por ejemplo, aquí se returnanula thisal devolver un objeto:

function BigUser() {
this.name = "John";

return { name: "Godzilla" }; // <-- returns this object

alert( new BigUser().name ); // Godzilla, got that object

Y aquí hay un ejemplo con un vacío return(o podríamos colocar un primitivo

después, no importa):

function SmallUser() {

this.name = "John";

return; // <-- returns this

alert( new SmallUser().name ); // John

Por lo general, los constructores no tienen una returndeclaración. Aquí

mencionamos el comportamiento especial con los objetos que regresan


principalmente por razones de integridad.

Omitir paréntesis

Por cierto, podemos omitir los paréntesis después new, si no tiene argumentos:


let user = new User; // <-- no parentheses

// same as

let user = new User();

Omitir paréntesis aquí no se considera un "buen estilo", pero la sintaxis está


permitida por la especificación.

Métodos en constructor

El uso de funciones de constructor para crear objetos proporciona una gran


flexibilidad. La función constructora puede tener parámetros que definan cómo
construir el objeto y qué poner en él.

Por supuesto, podemos agregar thisno solo propiedades, sino también

métodos.

Por ejemplo, a new User(name)continuación se crea un objeto con nameel

método dado sayHi:

function User(name) {

this.name = name;

this.sayHi = function() {

alert( "My name is: " + this.name );

};

let john = new User("John");


john.sayHi(); // My name is: John

/*

john = {

name: "John",

sayHi: function() { ... }

*/

Para crear objetos complejos, hay una sintaxis más avanzada, clases , que
cubriremos más adelante.

Resumen

 Las funciones de constructor o, brevemente, los constructores, son


funciones regulares, pero hay un acuerdo común para nombrarlas con
mayúscula primero.

 Las funciones de constructor solo deben llamarse usando new. Tal

llamada implica una creación de vacío thisal principio y devolver el poblado al

final.

Podemos usar funciones de constructor para crear múltiples objetos similares.

JavaScript proporciona funciones de constructor para muchos objetos de

lenguaje integrados: como Datefechas, Setconjuntos y otros que planeamos

estudiar.
23. Encadenamiento opcional '?.'

Una adición reciente

Esta es una adición reciente al idioma. Los navegadores antiguos pueden


necesitar polyfills.

El encadenamiento opcional ?.es una forma a prueba de errores de acceder a

las propiedades de los objetos anidados, incluso si no existe una propiedad


intermedia.

El problema

Si acaba de comenzar a leer el tutorial y aprender JavaScript, tal vez el problema


aún no lo haya afectado, pero es bastante común.

Por ejemplo, algunos de nuestros usuarios tienen direcciones, pero pocos no las
proporcionaron. Entonces no podemos leer con

seguridad user.address.street:

let user = {}; // the user happens to be without address

alert(user.address.street); // Error!

O, en el desarrollo web, nos gustaría obtener información sobre un elemento en


la página, pero puede que no exista:

// Error if the result of querySelector(...) is null

let html = document.querySelector('.my-element').innerHTML;

Antes de que ?.apareciera en el idioma, el &&operador estaba acostumbrado a

solucionarlo.
Por ejemplo:

let user = {}; // user has no address

alert( user && user.address && user.address.street ); //


undefined (no error)

La operación AND de la ruta completa a la propiedad asegura que todos los


componentes existen, pero es engorroso de escribir.

Encadenamiento opcional

El encadenamiento opcional ?.detiene la evaluación y regresa undefinedsi la

parte anterior ?.es undefinedo null.

Más adelante en este artículo, por brevedad, diremos que algo "existe" si no lo

es nully no lo es undefined.

Esta es la forma segura de acceder user.address.street:

let user = {}; // user has no address

alert( user?.address?.street ); // undefined (no error)

Leer la dirección con user?.addressfunciona incluso si el userobjeto no existe:

let user = null;

alert( user?.address ); // undefined

alert( user?.address.street ); // undefined

Tenga en cuenta: la ?.sintaxis hace opcional el valor anterior, pero no más.


En el ejemplo anterior, user?.solo permite userser null/undefined.

Por otro lado, si userexiste, entonces debe tener user.addresspropiedad; de

lo contrario, user?.address.streetda un error en el segundo punto.

No abuse del encadenamiento opcional

Debemos usar ?.solo donde esté bien que algo no exista.

Por ejemplo, si de acuerdo con nuestra lógica de codificación el userobjeto

debe estar ahí, pero addresses opcional,

entonces user.address?.streetsería mejor.

Entonces, si userno está definido debido a un error, lo sabremos y lo

arreglaremos. De lo contrario, los errores de codificación pueden silenciarse


cuando no sea apropiado y volverse más difíciles de depurar.

La variable anterior ?.debe declararse

Si no hay ninguna variable user, se user?.anythinggenera un error:

// ReferenceError: user is not defined

user?.address;

Debe haber let/const/var user. El encadenamiento opcional funciona solo

para variables declaradas.

Cortocircuito

Como se dijo antes, el ?.inmediatamente detiene (“cortocircuita”) la evaluación

si la parte izquierda no existe.

Entonces, si hay más llamadas a funciones o efectos secundarios, no ocurren:

let user = null;


let x = 0;

user?.sayHi(x++); // nothing happens

alert(x); // 0, value not incremented

Otros casos: ?.(), ?.[]

El encadenamiento opcional ?.no es un operador, sino una construcción de

sintaxis especial, que también funciona con funciones y corchetes.

Por ejemplo, ?.()se utiliza para llamar a una función que puede no existir.

En el siguiente código, algunos de nuestros usuarios tienen un adminmétodo y

otros no:

let user1 = {

admin() {

alert("I am admin");

let user2 = {};

user1.admin?.(); // I am admin

user2.admin?.();
Aquí, en ambas líneas, primero usamos el punto .para obtener

la adminpropiedad, porque el objeto de usuario debe existir, por lo que es

seguro leerlo.

Luego ?.()verifica la parte izquierda: si la función de administración existe,

entonces se ejecuta (para user1). De lo contrario (para user2) la evaluación se

detiene sin errores.

La ?.[]sintaxis también funciona, si queremos usar corchetes []para acceder a

las propiedades en lugar del punto .. Al igual que en casos anteriores, permite

leer de forma segura una propiedad de un objeto que puede no existir.

let user1 = {

firstName: "John"

};

let user2 = null; // Imagine, we couldn't authorize the user

let key = "firstName";

alert( user1?.[key] ); // John

alert( user2?.[key] ); // undefined

alert( user1?.[key]?.something?.not?.existing); // undefined

También podemos usar ?.con delete:

delete user?.name; // delete user.name if user exists


Podemos usar ?.para leer y borrar de forma segura, pero no para escribir

El encadenamiento opcional ?.no tiene ningún uso en el lado izquierdo de una

asignación:

// the idea of the code below is to write user.name, if user


exists

user?.name = "John"; // Error, doesn't work

// because it evaluates to undefined = "John"

Resumen

La ?.sintaxis tiene tres formas:

1. obj?.prop- devuelve obj.propsi objexiste, de lo contrario undefined.

2. obj?.[prop]- devuelve obj[prop]si objexiste, de lo

contrario undefined.

3. obj?.method()- llama obj.method()si objexiste, de lo contrario

regresa undefined.

Como podemos ver, todos son sencillos y fáciles de usar. Los ?.controles de la

parte izquierda de null/undefinedy permite la evaluación de proceder si no es

así.

Una cadena de ?.permite acceder de forma segura a propiedades anidadas.

Aún así, debemos aplicarlo con ?.cuidado, solo donde esté bien que la parte

izquierda no exista.

Para que no nos oculte errores de programación, si ocurren.


24. Tipo de símbolo

Por especificación, las claves de propiedad del objeto pueden ser de tipo
cadena o de tipo símbolo. No números, no booleanos, solo cadenas o símbolos,
estos dos tipos.

Hasta ahora hemos estado usando solo cadenas. Ahora veamos los beneficios
que nos pueden dar los símbolos.

Simbolos

Un "símbolo" representa un identificador único.

Se puede crear un valor de este tipo usando Symbol():

// id is a new symbol

let id = Symbol();

Una vez creado, podemos darle al símbolo una descripción (también llamado
nombre de símbolo), que es muy útil para fines de depuración:

// id is a symbol with the description "id"

let id = Symbol("id");

Se garantiza que los símbolos son únicos. Incluso si creamos muchos símbolos


con la misma descripción, son valores diferentes. La descripción es solo una
etiqueta que no afecta a nada.

Por ejemplo, aquí hay dos símbolos con la misma descripción, no son iguales:

let id1 = Symbol("id");

let id2 = Symbol("id");


alert(id1 == id2); // false

Si está familiarizado con Ruby u otro idioma que también tenga algún tipo de
"símbolos", no se equivoque. Los símbolos de JavaScript son diferentes.

Los símbolos no se convierten automáticamente en una cadena

La mayoría de los valores en JavaScript admiten la conversión implícita a una

cadena. Por ejemplo, podemos alertcasi cualquier valor y funcionará. Los

símbolos son especiales. No se convierten automáticamente.

Por ejemplo, esto alertmostrará un error:

let id = Symbol("id");

alert(id); // TypeError: Cannot convert a Symbol value to a


string

Eso es una “protección del lenguaje” contra el desorden, porque las cadenas y
los símbolos son fundamentalmente diferentes y no deben convertirse
accidentalmente uno en otro.

Si realmente queremos mostrar un símbolo, debemos invocarlo


explícitamente .toString(), como aquí:

let id = Symbol("id");

alert(id.toString()); // Symbol(id), now it works

O obtenga la symbol.descriptionpropiedad para mostrar solo la descripción:

let id = Symbol("id");

alert(id.description); // id

Propiedades "ocultas"
Los símbolos nos permiten crear propiedades "ocultas" de un objeto, a las que
ninguna otra parte del código puede acceder o sobrescribir accidentalmente.

Por ejemplo, si estamos trabajando con userobjetos que pertenecen a un

código de terceros. Nos gustaría agregarles identificadores.

Usemos una tecla de símbolo para ello:

let user = { // belongs to another code

name: "John"

};

let id = Symbol("id");

user[id] = 1;

alert( user[id] ); // we can access the data using the symbol as


the key

¿Cuál es el beneficio de usar Symbol("id")sobre una cuerda "id"?

Como los userobjetos pertenecen a otro código, y ese código también

funciona con ellos, no deberíamos simplemente agregarle campos. Eso es


inseguro. Pero no se puede acceder a un símbolo accidentalmente, el código de
terceros probablemente ni siquiera lo verá, por lo que probablemente esté bien
hacerlo.
Además, imagine que otro script quiere tener su propio identificador

dentro user, para sus propios fines. Esa puede ser otra biblioteca de JavaScript,

por lo que los scripts no se conocen por completo.

Entonces ese script puede crear el suyo propio Symbol("id"), así:

// ...

let id = Symbol("id");

user[id] = "Their id value";

No habrá conflicto entre nuestros identificadores y sus identificadores, porque


los símbolos siempre son diferentes, incluso si tienen el mismo nombre.

... Pero si usamos una cadena en "id"lugar de un símbolo para el mismo

propósito, entonces habría un conflicto:

let user = { name: "John" };

// Our script uses "id" property

user.id = "Our id value";

// ...Another script also wants "id" for its purposes...

user.id = "Their id value"

// Boom! overwritten by another script!


Símbolos en un objeto literal

Si queremos usar un símbolo en un objeto literal {...}, necesitamos corchetes

alrededor.

Me gusta esto:

let id = Symbol("id");

let user = {

name: "John",

[id]: 123 // not "id": 123

};

Eso es porque necesitamos el valor de la variable idcomo clave, no la cadena

"id".

Los símbolos se omiten durante ... en

Las propiedades simbólicas no participan en el for..inbucle.

Por ejemplo:

let id = Symbol("id");

let user = {

name: "John",

age: 30,

[id]: 123

};
for (let key in user) alert(key); // name, age (no symbols)

// the direct access by the symbol works

alert( "Direct: " + user[id] );

Object.keys(user)también los ignora. Eso es parte del principio general de

"ocultar propiedades simbólicas". Si otro script o biblioteca recorre nuestro


objeto, no accederá inesperadamente a una propiedad simbólica.

Por el contrario, Object.assign copia las propiedades de cadena y símbolo:

let id = Symbol("id");

let user = {

[id]: 123

};

let clone = Object.assign({}, user);

alert( clone[id] ); // 123

Aquí no hay ninguna paradoja. Eso es por diseño. La idea es que cuando


clonamos un objeto o fusionamos objetos, normalmente queremos que se
copien todas las propiedades (incluidos los símbolos como id).

Símbolos globales
Como hemos visto, normalmente todos los símbolos son diferentes, incluso si
tienen el mismo nombre. Pero a veces queremos que los símbolos del mismo
nombre sean las mismas entidades. Por ejemplo, diferentes partes de nuestra

aplicación quieren acceder al símbolo que "id"significa exactamente la misma

propiedad.

Para lograrlo, existe un registro de símbolos global . Podemos crear símbolos en


él y acceder a ellos más tarde, y garantiza que accesos repetidos con el mismo
nombre devuelvan exactamente el mismo símbolo.

Para leer (crear si está ausente) un símbolo del registro,


utilice Symbol.for(key).

Esa llamada verifica el registro global, y si hay un símbolo descrito como key, lo

devuelve; de lo contrario, crea un nuevo símbolo Symbol(key)y lo almacena en

el registro por el dado key.

Por ejemplo:

// read from the global registry

let id = Symbol.for("id"); // if the symbol did not exist, it is


created

// read it again (maybe from another part of the code)

let idAgain = Symbol.for("id");

// the same symbol

alert( id === idAgain ); // true


Los símbolos dentro del registro se denominan símbolos globales . Si queremos
un símbolo para toda la aplicación, accesible en todas partes del código, para
eso están.

Eso suena como ruby

En algunos lenguajes de programación, como Ruby, hay un solo símbolo por


nombre.

En JavaScript, como podemos ver, eso es correcto para los símbolos globales.

Symbol.keyFor

Para los símbolos globales, no solo Symbol.for(key)devuelve un símbolo por

nombre, sino que hay una llamada inversa:, Symbol.keyFor(sym)que hace lo

contrario: devuelve un nombre mediante un símbolo global.

Por ejemplo:

// get symbol by name

let sym = Symbol.for("name");

let sym2 = Symbol.for("id");

// get name by symbol

alert( Symbol.keyFor(sym) ); // name

alert( Symbol.keyFor(sym2) ); // id

La Symbol.keyForinternamente utiliza el registro global de símbolo para

buscar la clave para el símbolo. Por tanto, no funciona con símbolos no


globales. Si el símbolo no es global, no podrá encontrarlo y

regresará undefined.
Dicho esto, cualquier símbolo tiene descriptionpropiedad.

Por ejemplo:

let globalSymbol = Symbol.for("name");

let localSymbol = Symbol("name");

alert( Symbol.keyFor(globalSymbol) ); // name, global symbol

alert( Symbol.keyFor(localSymbol) ); // undefined, not global

alert( localSymbol.description ); // name

Símbolos del sistema

Existen muchos símbolos de "sistema" que JavaScript usa internamente, y


podemos usarlos para ajustar varios aspectos de nuestros objetos.

Se enumeran en la especificación en la tabla de símbolos conocidos :

 Symbol.hasInstance

 Symbol.isConcatSpreadable

 Symbol.iterator

 Symbol.toPrimitive

 …y así.

Por ejemplo, Symbol.toPrimitivenos permite describir la conversión de

objeto a primitivo. Veremos su uso muy pronto.


Otros símbolos también se familiarizarán cuando estudiemos las características
del lenguaje correspondiente.

Resumen

Symbol es un tipo primitivo para identificadores únicos.

Los símbolos se crean con una Symbol()llamada con una descripción opcional

(nombre).

Los símbolos son siempre valores diferentes, incluso si tienen el mismo


nombre. Si queremos que los símbolos del mismo nombre sean iguales,
entonces debemos usar el registro global: Symbol.for(key)devuelve (crea si

es necesario) un símbolo global con keyel nombre. Varias llamadas

de Symbol.forcon el mismo keydevuelven exactamente el mismo símbolo.

Los símbolos tienen dos casos de uso principales:

1. Propiedades del objeto "oculto". Si queremos agregar una propiedad a


un objeto que “pertenece” a otro script o biblioteca, podemos crear un símbolo
y usarlo como clave de propiedad. Una propiedad simbólica no
aparece for..in, por lo que no se procesará accidentalmente junto con otras

propiedades. Además, no se accederá directamente, porque otro script no tiene


nuestro símbolo. Por lo tanto, la propiedad estará protegida contra el uso
accidental o sobreescritura.

De modo que podemos ocultar algo "encubiertamente" en los objetos que


necesitamos, pero otros no deberían ver, utilizando propiedades simbólicas.

2. Hay muchos símbolos del sistema utilizados por JavaScript a los que se

puede acceder como Symbol.*. Podemos usarlos para alterar algunos

comportamientos incorporados. Por ejemplo, más adelante en el tutorial lo


usaremos Symbol.iteratorpara iterables , Symbol.toPrimitivepara

configurar la conversión de objeto a primitivo, etc.

Técnicamente, los símbolos no están 100% ocultos. Hay un método incorporado


Object.getOwnPropertySymbols (obj) que nos permite obtener todos los
símbolos. También hay un método llamado Reflect.ownKeys (obj) que
devuelve todas las claves de un objeto, incluidas las simbólicas. Por tanto, no
están realmente ocultos. Pero la mayoría de las bibliotecas, funciones integradas
y construcciones de sintaxis no utilizan estos métodos.
25. Objeto a conversión primitiva

¿Qué sucede cuando se suman obj1 + obj2, restan obj1 - obj2o

imprimen objetos usando alert(obj)?

En ese caso, los objetos se convierten automáticamente en primitivas y luego se


lleva a cabo la operación.

En el capítulo Conversiones de tipos , hemos visto las reglas para las


conversiones numéricas, de cadena y booleanas de primitivas. Pero dejamos un
hueco para los objetos. Ahora, como sabemos sobre métodos y símbolos, es
posible completarlo.

1. Todos los objetos están trueen un contexto booleano. Solo hay

conversiones numéricas y de cadenas.

2. La conversión numérica ocurre cuando restamos objetos o aplicamos

funciones matemáticas. Por ejemplo, los Dateobjetos (que se tratarán en el

capítulo Fecha y hora ) se pueden restar y el resultado de date1 - date2es la

diferencia de tiempo entre dos fechas.

3. En cuanto a la conversión de cadenas, generalmente ocurre cuando

generamos un objeto como alert(obj)y en contextos similares.

ToPrimitive

Podemos ajustar la conversión numérica y de cadenas utilizando métodos de


objetos especiales.

Hay tres variantes de conversión de tipos, las denominadas "sugerencias", que


se describen en la especificación :

"string"
Para una conversión de objeto a cadena, cuando estamos haciendo una

operación en un objeto que espera una cadena, como alert:

// output

alert(obj);

// using object as a property key

anotherObj[obj] = 123;

"number"

Para una conversión de objeto a número, como cuando estamos


haciendo matemáticas:

// explicit conversion

let num = Number(obj);

// maths (except binary plus)

let n = +obj; // unary plus

let delta = date1 - date2;

// less/greater comparison

let greater = user1 > user2;

"default"

Ocurre en casos raros cuando el operador "no está seguro" de qué tipo
esperar.
Por ejemplo, binary plus +puede funcionar tanto con cadenas (las

concatena) como con números (las agrega), por lo que tanto cadenas
como números funcionarían. Entonces, si un binario más obtiene un

objeto como argumento, usa la "default"sugerencia para convertirlo.

Además, si un objeto se compara ==con una cadena, un número o un

símbolo, tampoco está claro qué conversión se debe realizar, por lo

que "default"se usa la pista.

// binary plus uses the "default" hint

let total = obj1 + obj2;

// obj == number uses the "default" hint

if (user == 1) { ... };

Los operadores de comparación mayor y menor, como < >, también

pueden funcionar con cadenas y números. Aún así, usan


la "number"pista, no "default". Eso es por razones históricas.

Sin embargo, en la práctica, no necesitamos recordar estos detalles


peculiares, porque todos los objetos incorporados excepto un caso
( Dateobjeto, lo aprenderemos más adelante) implementan

la "default"conversión de la misma manera que "number". Y podemos

hacer lo mismo.

Sin "boolean"pista

Tenga en cuenta que solo hay tres sugerencias. Es así de simple.


No hay ninguna pista "booleana" (todos los objetos están trueen contexto

booleano) ni nada más. Y si tratamos "default"y "number"lo mismo, como lo

hacen la mayoría de los integrados, entonces solo hay dos conversiones.

Para realizar la conversión, JavaScript intenta buscar y llamar a tres métodos de


objeto:

1. Llamar obj[Symbol.toPrimitive](hint): el método con la tecla

simbólica Symbol.toPrimitive(símbolo del sistema), si tal método existe,

2. De lo contrario, si la pista es "string"

o prueba obj.toString()y obj.valueOf(), lo que sea que exista.

3. De lo contrario, si la pista es "number"o"default"

o prueba obj.valueOf()y obj.toString(), lo que sea que exista.

Símbolo a Primitivo

Empecemos por el primer método. Hay un símbolo incorporado


llamado Symbol.toPrimitiveque debe usarse para nombrar el método de
conversión, como este:

obj[Symbol.toPrimitive] = function(hint) {

// must return a primitive value

// hint = one of "string", "number", "default"

};

Por ejemplo, aquí el userobjeto lo implementa:

let user = {

name: "John",
money: 1000,

[Symbol.toPrimitive](hint) {

alert(`hint: ${hint}`);

return hint == "string" ? `{name: "${this.name}"}` :


this.money;

};

// conversions demo:

alert(user); // hint: string -> {name: "John"}

alert(+user); // hint: number -> 1000

alert(user + 500); // hint: default -> 1500

Como podemos ver en el código, se userconvierte en una cadena

autodescriptiva o una cantidad de dinero dependiendo de la conversión. El


método único user[Symbol.toPrimitive]maneja todos los casos de

conversión.

toString / valueOf

Métodos toStringy valueOfproceden de la antigüedad. No son símbolos (los

símbolos no existían hace tanto tiempo), sino métodos "regulares" con nombre
de cadena. Proporcionan una forma alternativa "al estilo antiguo" de
implementar la conversión.
Si no hay Symbol.toPrimitive, JavaScript intenta encontrarlos y probar en el

orden:

 toString -> valueOf para la pista de "cadena".

 valueOf -> toString de otra manera.

Estos métodos deben devolver un valor

primitivo. Si toStringo valueOfdevuelve un objeto, se ignora (lo mismo que si

no hubiera un método).

De forma predeterminada, un objeto simple tiene los


siguientes métodos toStringy valueOf:

 El toStringmétodo devuelve una cadena "[object Object]".

 El valueOfmétodo devuelve el objeto en sí.

Aquí está la demostración:

let user = {name: "John"};

alert(user); // [object Object]

alert(user.valueOf() === user); // true

Entonces, si intentamos usar un objeto como una cadena, como en uno alerto

así, entonces vemos por defecto [object Object].

Y el valor predeterminado valueOfse menciona aquí solo en aras de la

integridad, para evitar cualquier confusión. Como puede ver, devuelve el objeto


en sí, por lo que se ignora. No me preguntes por qué, eso es por razones
históricas. Entonces podemos asumir que no existe.

Implementemos estos métodos.


Por ejemplo, aquí userhace lo mismo que el anterior usando una combinación

de toStringy en valueOflugar de Symbol.toPrimitive:

let user = {

name: "John",

money: 1000,

// for hint="string"

toString() {

return `{name: "${this.name}"}`;

},

// for hint="number" or "default"

valueOf() {

return this.money;

};

alert(user); // toString -> {name: "John"}

alert(+user); // valueOf -> 1000

alert(user + 500); // valueOf -> 1500


Como podemos ver, el comportamiento es el mismo que en el ejemplo anterior

con Symbol.toPrimitive.

A menudo queremos un único lugar "general" para manejar todas las

conversiones primitivas. En este caso, solo podemos implementar toString, así:

let user = {

name: "John",

toString() {

return this.name;

};

alert(user); // toString -> John

alert(user + 500); // toString -> John500

En ausencia de Symbol.toPrimitivey valueOf, toStringmanejará todas las

conversiones primitivas.

Tipos de devolución

Lo importante que debe saber acerca de todos los métodos de conversión


primitiva es que no necesariamente devuelven la primitiva "insinuada".

No hay control si toStringdevuelve exactamente una cadena o si

el Symbol.toPrimitivemétodo devuelve un número para una pista "number".

Lo único obligatorio: estos métodos deben devolver una primitiva, no un objeto.


Notas históricas

Por razones históricas, si toStringo valueOfdevuelve un objeto, no hay error,

pero dicho valor se ignora (como si el método no existiera). Eso es porque en la


antigüedad no había un buen concepto de "error" en JavaScript.

Por el contrario, Symbol.toPrimitive debe devolver una primitiva, de lo

contrario habrá un error.

Más conversiones

Como ya sabemos, muchos operadores y funciones realizan conversiones de

tipos, por ejemplo, la multiplicación *convierte operandos en números.

Si pasamos un objeto como argumento, entonces hay dos etapas:

1. El objeto se convierte en un primitivo (utilizando las reglas descritas


anteriormente).

2. Si la primitiva resultante no es del tipo correcto, se convierte.

Por ejemplo:

let obj = {

// toString handles all conversions in the absence of other


methods

toString() {

return "2";

};
alert(obj * 2); // 4, object converted to primitive "2", then
multiplication made it a number

1. La multiplicación obj * 2primero convierte el objeto en primitivo (que

es una cadena "2").

2. Luego se "2" * 2convierte en 2 * 2(la cadena se convierte en número).

Binary plus concatenará cadenas en la misma situación, ya que acepta con gusto
una cadena:

let obj = {

toString() {

return "2";

};

alert(obj + 2); // 22 ("2" + 2), conversion to primitive


returned a string => concatenation

Resumen

La conversión de objeto a primitivo es llamada automáticamente por muchas


funciones y operadores incorporados que esperan un primitivo como valor.

Hay 3 tipos (pistas) de esto:

 "string"(para alerty otras operaciones que necesitan una cadena)

 "number" (para matemáticas)

 "default" (pocos operadores)
La especificación describe explícitamente qué operador usa qué sugerencia. Hay
muy pocos operadores que "no saben qué esperar" y usan

la "default"pista. Por lo general, para los objetos incorporados,

la "default"sugerencia se maneja de la misma manera que "number", por lo

que en la práctica, los dos últimos a menudo se combinan.

El algoritmo de conversión es:

1. Llame obj[Symbol.toPrimitive](hint)si el método existe,

2. De lo contrario, si la pista es "string"

o prueba obj.toString()y obj.valueOf(), lo que sea que exista.

3. De lo contrario, si la pista es "number"o"default"

o prueba obj.valueOf()y obj.toString(), lo que sea que exista.

En la práctica, a menudo es suficiente implementar solo obj.toString()como

un método "general" para todas las conversiones que devuelven una


representación "legible por humanos" de un objeto, con fines de registro o
depuración.
26. Métodos JSON, toJSON

Digamos que tenemos un objeto complejo y nos gustaría convertirlo en una


cadena, enviarlo a través de una red o simplemente generarlo con fines de
registro.

Naturalmente, dicha cadena debe incluir todas las propiedades importantes.

Podríamos implementar la conversión de esta manera:

let user = {

name: "John",

age: 30,

toString() {

return `{name: "${this.name}", age: ${this.age}}`;

};

alert(user); // {name: "John", age: 30}

… Pero en el proceso de desarrollo, se agregan nuevas propiedades, las

propiedades antiguas se renombran y eliminan. Actualizarlo toStringcada vez

puede convertirse en una molestia. Podríamos intentar recorrer las propiedades


en él, pero ¿qué pasa si el objeto es complejo y tiene objetos anidados en las
propiedades? Necesitaríamos implementar su conversión también.
Afortunadamente, no es necesario escribir el código para manejar todo esto. La
tarea ya está resuelta.

JSON.stringify

El JSON (JavaScript Object Notation) es un formato general para representar los


valores y objetos. Se describe como en el estándar RFC 4627 . Inicialmente se
hizo para JavaScript, pero muchos otros lenguajes también tienen bibliotecas
para manejarlo. Por lo tanto, es fácil usar JSON para el intercambio de datos
cuando el cliente usa JavaScript y el servidor está escrito en Ruby / PHP / Java /
Whatever.

JavaScript proporciona métodos:

 JSON.stringify para convertir objetos en JSON.

 JSON.parse para convertir JSON de nuevo en un objeto.

Por ejemplo, aquí somos JSON.stringifyun estudiante:

let student = {

name: 'John',

age: 30,

isAdmin: false,

courses: ['html', 'css', 'js'],

wife: null

};

let json = JSON.stringify(student);


alert(typeof json); // we've got a string!

alert(json);

/* JSON-encoded object:

"name": "John",

"age": 30,

"isAdmin": false,

"courses": ["html", "css", "js"],

"wife": null

*/

El método JSON.stringify(student)toma el objeto y lo convierte en una

cadena.

El resultante jsoncadena se denomina JSON

codificado o serializado o stringified o marshalled objeto. Estamos listos para


enviarlo por cable o ponerlo en un almacén de datos simple.

Tenga en cuenta que un objeto codificado en JSON tiene varias diferencias


importantes con el objeto literal:

 Las cadenas utilizan comillas dobles. No hay comillas simples ni comillas


inversas en JSON. Así se 'John'convierte "John".
 Los nombres de las propiedades de los objetos también están entre

comillas dobles. Eso es obligatorio. Así se age:30convierte "age":30.

JSON.stringify también se puede aplicar a primitivas.

JSON admite los siguientes tipos de datos:

 Objetos { ... }

 Matrices [ ... ]

 Primitivas:

o instrumentos de cuerda,

o números,

o valores booleanos true/false,

o null.

Por ejemplo:

// a number in JSON is just a number

alert( JSON.stringify(1) ) // 1

// a string in JSON is still a string, but double-quoted

alert( JSON.stringify('test') ) // "test"

alert( JSON.stringify(true) ); // true


alert( JSON.stringify([1, 2, 3]) ); // [1,2,3]

JSON es una especificación independiente del lenguaje de solo datos, por lo


que se omiten algunas propiedades de objetos específicas de
JavaScript JSON.stringify.

A saber:

 Propiedades de la función (métodos).

 Propiedades simbólicas.

 Propiedades que almacenan undefined.

let user = {

sayHi() { // ignored

alert("Hello");

},

[Symbol("id")]: 123, // ignored

something: undefined // ignored

};

alert( JSON.stringify(user) ); // {} (empty object)

Normalmente eso está bien. Si eso no es lo que queremos, pronto veremos


cómo personalizar el proceso.

Lo mejor es que los objetos anidados se admiten y se convierten


automáticamente.

Por ejemplo:
let meetup = {

title: "Conference",

room: {

number: 23,

participants: ["john", "ann"]

};

alert( JSON.stringify(meetup) );

/* The whole structure is stringified:

"title":"Conference",

"room":{"number":23,"participants":["john","ann"]},

*/

La limitación importante: no debe haber referencias circulares.

Por ejemplo:

let room = {

number: 23

};
let meetup = {

title: "Conference",

participants: ["john", "ann"]

};

meetup.place = room; // meetup references room

room.occupiedBy = meetup; // room references meetup

JSON.stringify(meetup); // Error: Converting circular structure


to JSON

Aquí, la conversión falla, debido a la referencia

circular: room.occupiedByreferencias meetupy meetup.placereferencias room:

Excluyendo y transformando: reemplazante

La sintaxis completa de JSON.stringifyes:

let json = JSON.stringify(value[, replacer, space])

valor

Un valor para codificar.

sustituto

Matriz de propiedades para codificar o función de

mapeo function(key, value).

espacio
Cantidad de espacio a utilizar para formatear

La mayoría de las veces, JSON.stringifyse usa solo con el primer

argumento. Pero si necesitamos ajustar el proceso de reemplazo, como para


filtrar referencias circulares, podemos usar el segundo argumento

de JSON.stringify.

Si le pasamos una matriz de propiedades, solo se codificarán estas propiedades.

Por ejemplo:

let room = {

number: 23

};

let meetup = {

title: "Conference",

participants: [{name: "John"}, {name: "Alice"}],

place: room // meetup references room

};

room.occupiedBy = meetup; // room references meetup

alert( JSON.stringify(meetup, ['title', 'participants']) );

// {"title":"Conference","participants":[{},{}]}
Aquí probablemente seamos demasiado estrictos. La lista de propiedades se
aplica a toda la estructura del objeto. Entonces los objetos

de participantsestán vacíos, porque nameno están en la lista.

Incluyamos en la lista todas las propiedades excepto la room.occupiedByque

causaría la referencia circular:

let room = {

number: 23

};

let meetup = {

title: "Conference",

participants: [{name: "John"}, {name: "Alice"}],

place: room // meetup references room

};

room.occupiedBy = meetup; // room references meetup

alert( JSON.stringify(meetup, ['title', 'participants', 'place',


'name', 'number']) );

/*

"title":"Conference",
"participants":[{"name":"John"},{"name":"Alice"}],

"place":{"number":23}

*/

Ahora todo excepto occupiedByestá serializado. Pero la lista de propiedades es

bastante larga.

Afortunadamente, podemos usar una función en lugar de una matriz

como replacer.

La función se llamará para cada (key, value)par y debería devolver el valor

"reemplazado", que se utilizará en lugar del original. O undefinedsi se debe

omitir el valor.

En nuestro caso, podemos devolver value"tal cual" para todo

excepto occupiedBy. Para ignorar occupiedBy, el siguiente código

devuelve undefined:

let room = {

number: 23

};

let meetup = {

title: "Conference",

participants: [{name: "John"}, {name: "Alice"}],

place: room // meetup references room

};
room.occupiedBy = meetup; // room references meetup

alert( JSON.stringify(meetup, function replacer(key, value) {

alert(`${key}: ${value}`);

return (key == 'occupiedBy') ? undefined : value;

}));

/* key:value pairs that come to replacer:

: [object Object]

title: Conference

participants: [object Object],[object Object]

0: [object Object]

name: John

1: [object Object]

name: Alice

place: [object Object]

number: 23

*/

Tenga en cuenta que la replacerfunción obtiene cada par clave / valor,

incluidos los objetos anidados y los elementos de la matriz. Se aplica de forma


recursiva. El valor de thisinside replaceres el objeto que contiene la

propiedad actual.

La primera llamada es especial. Se hace uso de un especial de “envoltorio

objeto”: {"": meetup}. En otras palabras, el primer (key, value)par tiene

una clave vacía y el valor es el objeto de destino como un todo. Es por eso que
la primera línea está ":[object Object]"en el ejemplo anterior.

La idea es proporcionar tanta potencia replacercomo sea posible: tiene la

oportunidad de analizar y reemplazar / omitir incluso el objeto completo si es


necesario.

Formateo: espacio

El tercer argumento de JSON.stringify(value, replacer, space)es el

número de espacios a usar para un formato bonito.

Anteriormente, todos los objetos en cadena no tenían sangrías ni espacios


adicionales. Eso está bien si queremos enviar un objeto a través de una
red. El spaceargumento se usa exclusivamente para una buena salida.

Aquí space = 2le dice a JavaScript que muestre objetos anidados en varias

líneas, con sangría de 2 espacios dentro de un objeto:

let user = {

name: "John",

age: 25,

roles: {

isAdmin: false,

isEditor: true
}

};

alert(JSON.stringify(user, null, 2));

/* two-space indents:

"name": "John",

"age": 25,

"roles": {

"isAdmin": false,

"isEditor": true

*/

/* for JSON.stringify(user, null, 4) the result would be more


indented:

"name": "John",

"age": 25,

"roles": {

"isAdmin": false,
"isEditor": true

*/

El spaceparámetro se utiliza únicamente para fines de registro y salida

agradable.

Personalizado "toJSON"

Al igual que toStringpara la conversión de cadenas, un objeto puede

proporcionar un método toJSONpara la conversión a JSON. JSON.stringifylo

llama automáticamente si está disponible.

Por ejemplo:

let room = {

number: 23

};

let meetup = {

title: "Conference",

date: new Date(Date.UTC(2017, 0, 1)),

room

};

alert( JSON.stringify(meetup) );
/*

"title":"Conference",

"date":"2017-01-01T00:00:00.000Z", // (1)

"room": {"number":23} // (2)

*/

Aquí podemos ver que se date (1)convirtió en una cuerda. Eso es porque

todas las fechas tienen un toJSONmétodo incorporado que devuelve ese tipo

de cadena.

Ahora agreguemos una personalizada toJSONpara nuestro objeto room (2):

let room = {

number: 23,

toJSON() {

return this.number;

};

let meetup = {

title: "Conference",

room

};
alert( JSON.stringify(room) ); // 23

alert( JSON.stringify(meetup) );

/*

"title":"Conference",

"room": 23

*/

Como podemos ver, toJSONse usa tanto para la llamada

directa JSON.stringify(room)como cuando roomestá anidado en otro objeto

codificado.

JSON.parse

Para decodificar una cadena JSON, necesitamos otro método


llamado JSON.parse .

La sintaxis:

let value = JSON.parse(str, [reviver]);

str

JSON-string para analizar.

vivificador
Función opcional (clave, valor) que se llamará para cada (key,

value)par y puede transformar el valor.

Por ejemplo:

// stringified array

let numbers = "[0, 1, 2, 3]";

numbers = JSON.parse(numbers);

alert( numbers[1] ); // 1

O para objetos anidados:

let userData = '{ "name": "John", "age": 35, "isAdmin": false,


"friends": [0,1,2,3] }';

let user = JSON.parse(userData);

alert( user.friends[1] ); // 1

El JSON puede ser tan complejo como sea necesario, los objetos y las matrices
pueden incluir otros objetos y matrices. Pero deben obedecer el mismo formato
JSON.

Aquí hay errores típicos en JSON escrito a mano (a veces tenemos que escribirlo
con fines de depuración):

let json = `{
name: "John", // mistake: property name
without quotes

"surname": 'Smith', // mistake: single quotes in


value (must be double)

'isAdmin': false // mistake: single quotes in


key (must be double)

"birthday": new Date(2000, 2, 3), // mistake: no "new" is


allowed, only bare values

"friends": [0,1,2,3] // here all fine

}`;

Además, JSON no admite comentarios. Agregar un comentario a JSON lo


invalida.

Hay otro formato llamado JSON5 , que permite claves sin comillas, comentarios,
etc. Pero esta es una biblioteca independiente, no en la especificación del
lenguaje.

El JSON normal es tan estricto no porque sus desarrolladores sean perezosos,


sino para permitir implementaciones fáciles, confiables y muy rápidas del
algoritmo de análisis.

Usando reviver

Imagínese, obtuvimos un meetupobjeto en cadena del servidor.

Se parece a esto:

// title: (meetup title), date: (meetup date)

let str = '{"title":"Conference","date":"2017-11-


30T12:00:00.000Z"}';
... Y ahora tenemos que deserializarlo , para convertirlo de nuevo en un objeto
JavaScript.

Hagámoslo llamando a JSON.parse:

let str = '{"title":"Conference","date":"2017-11-


30T12:00:00.000Z"}';

let meetup = JSON.parse(str);

alert( meetup.date.getDate() ); // Error!

¡Ups! ¡Un error!

El valor de meetup.datees una cadena, no un Dateobjeto. ¿Cómo

podría JSON.parsesaber que debería transformar esa cadena en una Date?

Pasemos a JSON.parsela función reviving como segundo argumento, que

devuelve todos los valores "como están", pero datese convertirá en Date:

let str = '{"title":"Conference","date":"2017-11-


30T12:00:00.000Z"}';

let meetup = JSON.parse(str, function(key, value) {

if (key == 'date') return new Date(value);

return value;

});
alert( meetup.date.getDate() ); // now works!

Por cierto, eso también funciona para objetos anidados:

let schedule = `{

"meetups": [

{"title":"Conference","date":"2017-11-30T12:00:00.000Z"},

{"title":"Birthday","date":"2017-04-18T12:00:00.000Z"}

}`;

schedule = JSON.parse(schedule, function(key, value) {

if (key == 'date') return new Date(value);

return value;

});

alert( schedule.meetups[1].date.getDate() ); // works!

Resumen

 JSON es un formato de datos que tiene su propio estándar


independiente y bibliotecas para la mayoría de los lenguajes de programación.

 JSON admite objetos simples, matrices, cadenas, números, valores


booleanos y null.

 JavaScript proporciona métodos JSON.stringify para serializar en JSON


y JSON.parse para leer desde JSON.
 Ambos métodos admiten funciones de transformador para lectura /
escritura inteligente.

 Si un objeto tiene toJSON, lo llama JSON.stringify.

27. Métodos GET y POST

¿Qué es HTTP?

HTTP (Hypertext Transfer Protocol) está diseñado para permitir una


comunicación entre clientes y servidores.

HTTP funciona como un protocolo de pedido-respuesta entre el cliente y


servidor

Un navegador web puede ser el cliente, y la aplicación en una computadora que


aloja un sitio web puede ser el servidor.

Ejemplo: Un cliente (navegador web) envía un pedido HTTP al servidor, luego el


servidor responde con una respuesta al cliente, la respuesta contiene la
información de estado sobre el pedido realizado y también podrá contener el
contenido solicitado (pedido). Este contenido puede ser un archivo .html (una
página web), o archivo a descargar, etc.

Método GET

El método GET se utiliza para solicitar la URL de un servidor web para así
obtener los documentos HTML. El método GET es uno de los métodos HTTP
más utilizado.

Notar que los parámetros de consulta que envía, se encuentran en la URL


mismo, en la forma par valor:

/test/demo_form.php?nombre1=valor1&nombre2=valor2
El método GET tiene algunos problemas de seguridad porque los datos
insertados son visibles en la URL. Sólo una cantidad restringida de datos puede
ser pasada a través del método GET, ya que la longitud de la URL que un
navegador puede atravesar podría ser de mil caracteres.

Método POST

El método POST es adecuado en la situación en la que una cantidad significativa


de información puede transmitirse.

Cuando un servidor recibe la solicitud a través de un formulario que utiliza


POST, sigue “escuchando” la información que queda. En pocas palabras, el
método transfiere toda la información relevante de la entrada del
formulario inmediatamente después de que se realiza la solicitud a la URL.

El método POST necesita establecer dos contactos con el servidor web mientras
que GET sólo hace uno. Las peticiones en el POST se gestionan de la misma
manera que en el método GET, donde los espacios se representan en el signo
más (+) y los caracteres restantes se codifican en el patrón URL. También puede
enviar los elementos de un archivo.

En este caso los parámetros se envían en el cuerpo del mensaje, y no en la URL


mismo (cómo sí lo hace el método GET):

/test/demo_form.php HTTP/1.1

Host: w3schools.com

name1=value1&name2=value2

Diferencias entre el método GET y POST


Después de ver por separado en qué consisten el método GET y POST, ahora
vamos a ver sus principales diferencias para así entenderlos mejor.

GET y POST son dos técnicas eficientes que pueden enviar los datos a un
servidor o navegador y necesariamente que estos se comuniquen. Los dos
métodos son distintos cuando el método GET añade los datos codificados a la
URI, mientras que en el caso del método POST los datos se añaden al cuerpo y
no a la URI. Además, se utiliza el método GET para recuperar los datos. Por el
contrario, el método POST se utiliza para almacenar o actualizar los datos.

La etiqueta <form> se utiliza para expresar el contenido del formulario; esto


también se conoce como control de formulario. Estos formularios se rellenan
con los datos correspondientes y se envían a la máquina remota para su
posterior procesamiento. El funcionamiento del formulario incluye dos cosas
cruciales: la primera es la especificación de la dirección del programa que
maneja el contenido del formulario con la ayuda de action. Más adelante se
encuentra la especificación del método dentro del cual fluyen los datos del
formulario con la ayuda del atributo method.

El atributo actiondescribe cómo se debe manejar el formulario HTML. El atributo


methodgestiona el proceso de presentación de los datos. Los métodos GET y
POST se encuentran bajo el atributo method.

 El método GET coloca los parámetros dentro de la URI mientras que el método
POST los agrega al cuerpo.

 2

 GET se utiliza esencialmente para obtener la información. En cambio, el


objetivo del método POST es actualizar los datos.
 Los resultados de la consulta POST no pueden marcarse, mientras que los
resultados de la consulta GET pueden marcarse porque existen en forma de
URL.

 En el método GET la información es visible en la URL lo que aumenta las


vulnerabilidades y el riesgo de hacking. Por el contrario, el método POST no
muestra variables en la URL y también se pueden utilizar múltiples técnicas de
codificación, lo que lo hace resistente.

 Cuando se utiliza el método GET en el formulario, sólo se aceptan caracteres


ASCII en los tipos de datos. Por el contrario, el método POST no enlaza tipos de
datos de formulario y permite caracteres binarios y ASCII.

 El tamaño variable en el método GET es de aproximadamente 2000 caracteres.


A la inversa, el método POST permite hasta 8 Mb de tamaño variable.

 Los datos del método GET se pueden almacenar en caché, mientras que los
datos del método POST no
28. Fetch (Comunicación con el servidor)

JavaScript puede enviar pedidos por la red a un servidor, cuando se desea obtener
nueva información

Por ejemplo podemos mandar pedidos al servidor para:

 Obtener la información completa de un usuario

 Cargar un listado de productos

 Recibir notificaciones desde un servidor

Y todo esto ¡sin refrescar la página!

Hay un término global "AJAX" (Asynchronous JavaScript And XML) para realizar


pedidos al servidor desde JavaScript. En realidad ya no se debe usar XML, el
término viene desde los viejos tiempos.

Cómo sus siglas lo indican, permite realizar pedidos a servidor de


forma asincrónica, lo cuál permite realizar pedidos sin tener que refrescar la
página

Hay múltiples formas de crear pedidos y obtener información desde el servidor.


Hoy en día el estándar desde JavaScript, se hacen a partir de la función fetch().

La sintáxis básica es:

let promise = fetch(url, [options])

 url: la URL a la que se envía el pedido

 options: parámetros opcionales: método HTTP, encabezados, etc.


Sin especificar options, se realizará un simple pedido GET, descargando el contenido
que se encuentre en la url especificada. Obteniendo la respuesta, generalmente se da
en un proceso de 2 fases:

Primero, la promesa, devuelta por fetch, resuelve con un objeto de tipo Response tan
pronto cómo el servidor (al que se le realizó el pedido) responda.

En esta fase podemos consultar por el estado HTTP, para saber si el pedido ha sido
respondido con éxito

La promesa es rechazada si el fetch no pudo realizar el pedido HTTP, por ejemplo por
problemas de red, url inválida, etc.

Para ver el estado HTTP (por ejemplo el número 200), en las propiedades del objeto
devuelto de tipo Response:

 status: código HTTP, por ejemplo 200.

 ok: booleano, verdadero si el código de estado HTTP está entre 200-


299.

La segunda fase, es obtener el cuerpo de la respuesta, para eso necesitamos realizar


una llamada adicional

El objeto de tipo Response provee múltiples métodos para acceder al cuerpo de la


respuesta en variados formatos, dos de ellos son:
 response.text(): lee la respuesta y la devuelve en forma de texto

 response.json(): se asume que la respuesta es notación JSON, y devuelve el


objeto construido a partir de dicha notación.

29. DOM ( Document Object Model )

Introducción a DOM (Document Object Model)

La creación del Document Object Model o DOM es una de las innovaciones que más ha


influido en el desarrollo de las páginas web dinámicas y de las aplicaciones web más
complejas.

DOM permite a los programadores web acceder y manipular las páginas HTML.

A pesar de sus orígenes, DOM se ha convertido en una utilidad disponible para la


mayoría de lenguajes de programación (Java, PHP, JavaScript) y cuyas únicas
diferencias se encuentran en la forma de implementarlo.

Árbol de nodos

Una de las tareas habituales en la programación de aplicaciones web con JavaScript


consiste en la manipulación de las páginas web. De esta forma, es habitual obtener el
valor almacenado por algunos elementos (por ejemplo, los elementos de un
formulario), crear o eliminar un elemento ( <p> , <div> , <img> , etc.) de forma
dinámica, manipular (poner/quitar/cambiar) una clase de un elemento, etc. Todas
estas tareas habituales son muy sencillas de realizar gracias a DOM, pero para ello es
necesario "cambiar" la página original.

Los navegadores web transforman automáticamente todas las páginas web en una


estructura más eficiente de manipular, el DOM. 
Esta transformación la realizan todos los navegadores de forma automática y nos
permite utilizar las herramientas de DOM de forma muy sencilla. DOM transforma
todos los documentos HTML en un conjunto de elementos llamados nodos, que están
interconectados y que representan los contenidos de las páginas web y las relaciones
entre ellos. Por su aspecto, la unión de todos los nodos se llama " árbol de nodos ". 

La raíz del árbol de nodos de cualquier página HTML siempre es la misma: un nodo de
tipo especial denominado document. A partir de ese nodo raíz, cada etiqueta HTML se
transforma en un nodo de tipo "Elemento". Como se puede suponer, las páginas HTML
habituales producen árboles con miles de nodos. Aun así, el proceso de transformación
es rápido y automático.

Tipos de nodo

La especificación completa de DOM define 12 tipos de nodos, aunque en las páginas


HTML se manipulan habitualmente cuatro tipos de nodos:

 Document: nodo raíz del que derivan todos los demás nodos del
árbol.

 Element: representa cada una de las etiquetas HTML. Se trata del


único nodo que puede contener atributos y el único del que pueden
derivar otros nodos.
 Attr: se define un nodo de este tipo para representar cada uno de
los atributos de las etiquetas HTML, es decir, uno por cada par
atributo=valor.

 Text: nodo que contiene el texto encerrado por una etiqueta HTML.

Acceso a nodos

Una vez que se ha accedido a un nodo, el siguiente paso consiste en acceder y/o
modificar sus atributos y propiedades. Mediante DOM, es posible acceder de forma
sencilla a todos los atributos HTML y todas las propiedades CSS de cualquier elemento
de la página.

Los atributos HTML de los elementos de la página se transforman automáticamente en


propiedades de los nodos. Para acceder a su valor, simplemente se indica el nombre
del atributo XHTML detrás del nombre del nodo.

<a href="https://es.wikipedia.org">Wikipedia</a>

var enlace = document.getElementsByTagName('a');

alert(enlace[0].href); // mostrando la URL

En el ejemplo anterior, se obtiene el nodo DOM que representa el enlace mediante el


método document.getElementsByTagName() que devuelve un array (arreglo) con
todos los elementos <a> de la página. A continuación, se obtiene el atributo href del
enlace mediante enlace[0].href. Para obtener por ejemplo el atributo target, se
utilizará enlace[0].target. 

Las propiedades CSS no son tan fáciles de obtener como los atributos XHTML. Para
obtener el valor de cualquier propiedad CSS del nodo, se debe utilizar el atributo style .
Si el nombre de una propiedad CSS es compuesto, se accede a su valor modificando
ligeramente su nombre. La transformación del nombre de las propiedades CSS
compuestas consiste en eliminar todos los guiones medios (-) y escribir en mayúscula
la letra siguiente a cada guión medio.

El único atributo HTML que no tiene el mismo nombre en HTML y en las propiedades
DOM es el atributo class. Como la palabra class está reservada por JavaScript, no es
posible utilizarla para acceder al atributo class del elemento HTML. En su lugar, DOM
utiliza el nombre className para acceder al atributo class de HTML.

Como hemos dicho, tenemos varias maneras de acceder a un determinado nodo:


acceder un determinado nodo:

getElementsByTagName('etiquetaHTML');
getElementsByName('nameHTML');

getElementById('id');

getElementsByClassName('class');

Creación/eliminación de nodos

Cuatro pasos para crear elementos HTML simples:

1. Creación de un nodo de tipo Element que represente al


elemento. createElement('etiqueta');.

2. Creación de un nodo de tipo Text que represente el contenido del


elemento. createTextNode('contenido');.

3. Añadir el nodo Text como nodo hijo del nodo


Element. nodoPadre.appendChild(nodoHijo);.

4. Añadir el nodo Element a la página, en forma de nodo hijo


nodoPadre.appendChild(nodoHijo); del nodo correspondiente al
lugar en el que se quiere insertar el elemento.

Ejemplos

5. Crear nodo de tipo Element


var parrafo = document.createElement("p");

6. Crear nodo de tipo Text


var contenido = document.createTextNode("Hola Mundo!");

7. Añadir el nodo Text como hijo del nodo Element


parrafo.appendChild(contenido);
8. Añadir el nodo Element como hijo de la página
document.body.appendChild(parrafo);

9. Acceso a los atributos

Añadir y eliminar clases es una de las tareas más comunes que llevamos a cabo al
trabajar con JavaScript y el DOM. Los elementos del DOM tienen una propiedad
llamada classList la cual es una matriz que contiene todas las clases que tiene el
elemento.

Además, tiene varios métodos adicionales:

 add: Añade una clase.

 remove: Elimina una clase.

 toggle: Añade una clase si está presente y si no, la elimina.

 contains: Revisa si la clase está presente en el elemento.

Añadiendo y eliminando clases

Añadir y eliminar una clase es muy sencillo, simplemente pasa la clase que quieres
añadir/eliminar como argumento al método. 

También podría gustarte