Apéndice

A excepción de null y undefined, cada tipo de datos primitivo tiene una prototype, un wrapper del objeto correspondiente que proporciona métodos para trabajar con valores. Cuando se invoca un método o una búsqueda de propiedades en un primitivo, JavaScript une la primitiva en segundo plano y llama al método o realiza la búsqueda de propiedades en el objeto wrapper.

Por ejemplo, un literal de string no tiene métodos propios, pero puedes llamar .toUpperCase() gracias al objeto String correspondiente. wrapper:

"this is a string literal".toUpperCase();
> THIS IS A STRING LITERAL

Esto se llama herencia prototípica: heredar propiedades y métodos a partir del constructor correspondiente de un valor.

Number.prototype
> Number { 0 }
>  constructor: function Number()
>  toExponential: function toExponential()
>  toFixed: function toFixed()
>  toLocaleString: function toLocaleString()
>  toPrecision: function toPrecision()
>  toString: function toString()
>  valueOf: function valueOf()
>  <prototype>: Object { }

Puedes crear primitivas con estos constructores, en lugar de solo definir por su valor. Por ejemplo, con el constructor String, se crea un Un objeto de cadena, no un literal de cadena: un objeto que no solo contiene la cadena. valor, sino también todas las propiedades y los métodos heredados del constructor.

const myString = new String( "I'm a string." );

myString
;
> String { "I'm a string." }

typeof myString;
> "object"

myString
.valueOf();
> "I'm a string."

En general, los objetos resultantes se comportan como los valores que hemos usado para definirlos. Por ejemplo, si bien definir un valor numérico con el atributo El constructor new Number da como resultado un objeto que contiene todos los métodos y propiedades del prototipo Number, puedes usar operadores matemáticos en esos objetos como lo harías con literales numéricos:

const numberOne = new Number(1);
const numberTwo = new Number(2);

numberOne
;
> Number { 1 }

typeof numberOne;
> "object"

numberTwo
;
> Number { 2 }

typeof numberTwo;
> "object"

numberOne
+ numberTwo;
> 3

Rara vez necesitarás usar estos constructores, ya que JavaScript tiene la herencia prototípica significa que no proporcionan ningún beneficio práctico. Creando las primitivas que usan constructores también pueden conducir a resultados inesperados, porque El resultado es un objeto, no un literal simple:

let stringLiteral = "String literal."

typeof stringLiteral;
> "string"

let stringObject
= new String( "String object." );

stringObject
> "object"

Esto puede complicar el uso de operadores de comparación estrictos:

const myStringLiteral = "My string";
const myStringObject = new String( "My string" );

myStringLiteral
=== "My string";
> true

myStringObject
=== "My string";
> false

Inserción automática de punto y coma (ASI)

Durante el análisis de una secuencia de comandos, los intérpretes de JavaScript pueden usar una función llamada inserción de punto y coma automática (ASI) para intentar corregir instancias de palabras clave omitidas punto y coma. Si el analizador de JavaScript encuentra un token no permitido, agrega un punto y coma antes del token para corregir el posible error de sintaxis, ya que siempre que se cumpla una o más de las siguientes condiciones:

  • Ese token está separado del token anterior por un salto de línea.
  • Ese token es }.
  • El token anterior es ), y el punto y coma insertado sería la finalización punto y coma de una sentencia do...while.

Para obtener más información, consulta las reglas de ASI.

Por ejemplo, omitir punto y coma después de las siguientes sentencias no provocará una error de sintaxis debido a ASI:

const myVariable = 2
myVariable
+ 3
> 5

Sin embargo, ASI no puede representar varios estados en la misma línea. Si escribes más de una sentencia en la misma línea, asegúrate de separarlas con punto y coma:

const myVariable = 2 myVariable + 3
> Uncaught SyntaxError: unexpected token: identifier

const myVariable = 2; myVariable + 3;
> 5

ASI es un intento de corregir errores, no un tipo de flexibilidad sintáctica creada en JavaScript. Asegúrense de usar punto y coma cuando sea apropiado para no confiar en para producir el código correcto.

Modo estricto

Los estándares que rigen la forma en que se escribe JavaScript han evolucionado mucho más allá todo lo que se tuvo en cuenta durante el diseño inicial del lenguaje. Cada nuevo cambio en El comportamiento esperado de JavaScript debe evitar causar errores en sitios web más antiguos.

ES5 soluciona algunos problemas antiguos con la semántica de JavaScript sin y rompen las implementaciones existentes con el “modo estricto”, una forma de habilitar en un conjunto de reglas de lenguaje más restrictivo para una secuencia de comandos completa o función individual. Para habilitar el modo estricto, usa el literal de string "use strict", seguido de un punto y coma, en la primera línea de una secuencia de comandos o función:

"use strict";
function myFunction() {
 
"use strict";
}

El modo estricto evita ciertos o funciones obsoletas, genera errores explícitos en lugar de la palabra “silencio” común y prohíbe el uso de que podrían entrar en conflicto con funciones futuras del lenguaje. Por ejemplo, al principio decisiones de diseño en torno al alcance de las variables aumentó las probabilidades de que los desarrolladores “confundan” por error el alcance global cuando declarar una variable, independientemente del contexto que la contiene, omitiendo el Palabra clave var:

(function() {
  mySloppyGlobal
= true;
}());

mySloppyGlobal
;
> true

Los tiempos de ejecución modernos de JavaScript no pueden corregir este comportamiento sin el riesgo de romper cualquier sitio web que se base en ellos, ya sea por error o a propósito. En cambio, JavaScript moderno lo evita y permite que los desarrolladores usen reglas de para nuevos trabajos y habilitar el modo estricto de forma predeterminada solo en el contexto de nuevas funciones de lenguaje que no interrumpen las implementaciones heredadas:

(function() {
   
"use strict";
    mySloppyGlobal
= true;
}());
> Uncaught ReferenceError: assignment to undeclared variable mySloppyGlobal

Debes escribir "use strict" como un literal de cadena. Un literal de plantilla (use strict) no funcionará. También debes incluir "use strict" antes de cualquier ejecutable en el contexto previsto. De lo contrario, el intérprete lo ignorará.

(function() {
   
"use strict";
    let myVariable
= "String.";
    console
.log( myVariable );
    sloppyGlobal
= true;
}());
> "String."
> Uncaught ReferenceError: assignment to undeclared variable sloppyGlobal

(function() {
    let myVariable
= "String.";
   
"use strict";
    console
.log( myVariable );
    sloppyGlobal
= true;
}());
> "String." // Because there was code prior to "use strict", this variable still pollutes the global scope

Por referencia, por valor

Cualquier variable, incluidas las propiedades de un objeto, parámetros de función y elementos de una array, establece o map: puede contener un valor primitivo o un valor de referencia.

Cuando se asigna un valor primitivo de una variable a otra, el código Engine crea una copia de ese valor y lo asigna a la variable.

Cuando asignas un objeto (instancias de clase, arrays y funciones) a un variable, en lugar de crear una copia nueva de ese objeto, la variable contiene una referencia a la posición almacenada del objeto en la memoria. Por este motivo, cambiar un objeto al que hace referencia una variable cambia el objeto al que se hace referencia, no solo un valor que contiene esa variable. Por ejemplo, si inicializas un nuevo variable con una variable que contiene una referencia de objeto, usar el nuevo variable para agregar una propiedad a ese objeto, la propiedad y su valor se agregan al objeto original:

const myObject = {};
const myObjectReference = myObject;

myObjectReference
.myProperty = true;

myObject
;
> Object { myProperty: true }

Esto es importante no solo para alterar objetos, sino también para realizar controles de comparación, ya que la igualdad estricta entre objetos requiere que ambas variables hacer referencia al mismo objeto para evaluar como true. No pueden hacer referencia diferentes, incluso si esos objetos son estructuralmente idénticos:

const myObject = {};
const myReferencedObject = myObject;
const myNewObject = {};

myObject
=== myNewObject;
> false

myObject
=== myReferencedObject;
> true

Asignación de memoria:

JavaScript utiliza la administración automática de la memoria, lo que significa que la memoria no necesita asignar o desasignar explícitamente durante el curso del desarrollo. Mientras que los detalles de los motores JavaScript de la administración de la memoria van más allá En este módulo, comprender cómo se asigna la memoria proporciona para trabajar con valores de referencia.

Hay dos “áreas” en la memoria: la “pila” y el "montón". La pila almacena datos estáticos (valores primitivos y referencias a objetos) porque la se puede asignar una cantidad fija de espacio necesario para almacenar estos datos se ejecuta tu secuencia de comandos. El montón almacena objetos, que necesitan espacio de asignación dinámica. porque su tamaño puede cambiar durante la ejecución. Un proceso libera la memoria llamada “recolección de elementos no utilizados”, que quita los objetos sin referencias de memoria.

El subproceso principal

JavaScript es un lenguaje de un solo subproceso con un enfoque modelo de ejecución, lo que significa que solo puede ejecutar una tarea por vez. Este contexto de ejecución secuencial se denomina subproceso principal.

Otras tareas del navegador comparten el subproceso principal, como el análisis de HTML, renderizar y volver a representar partes de la página, ejecutar animaciones CSS y manejar interacciones del usuario que van desde lo simple (como destacar texto) hasta lo complejo (como interactuar con elementos del formulario). Los proveedores del navegador encontraron maneras de optimizar las tareas realizadas por el subproceso principal, pero más complejas las secuencias de comandos pueden usar demasiados recursos del subproceso principal y generar un impacto general el rendimiento de la página.

Algunas tareas se pueden ejecutar subprocesos en segundo plano llamados Web Workers, con algunas limitaciones:

  • Los subprocesos de trabajo solo pueden actuar en archivos JavaScript independientes.
  • Ha reducido considerablemente el acceso a la ventana del navegador y a la IU, o no ha podido hacerlo.
  • Están limitados en cuanto a cómo se puede comunicar con el subproceso principal.

Estas limitaciones los hacen ideales para tareas enfocadas en un uso intensivo de recursos que de otro modo, podría ocupar el subproceso principal.

La pila de llamadas

La estructura de datos usada para administrar los “contextos de ejecución”, es decir, el código que ejecutada, es una lista denominada pila de llamadas (a menudo, solo “la pila”). Cuando se ejecuta una secuencia de comandos por primera vez, el intérprete de JavaScript Crea un “contexto de ejecución global” y lo envía a la pila de llamadas, con de instrucciones dentro de ese contexto global ejecutadas una a la vez, desde arriba hacia abajo. Cuando el intérprete encuentra una llamada a función mientras ejecuta el contexto global, envía un “contexto de ejecución de la función” de esa llamada a la en la parte superior de la pila, pausa el contexto de ejecución global y ejecuta la función el contexto de ejecución.

Cada vez que se llama a una función, se establece el contexto de ejecución de la función para esa llamada se inserta en la parte superior de la pila, justo encima del contexto de ejecución actual. La pila de llamadas opera según un modelo de "primero en entrar, primero en salir". lo que significa que lo más llamada a función reciente, que es la más alta en la pila, se ejecuta y continúa hasta que se resuelva. Cuando se completa la función, el intérprete la quita de la pila de llamadas, y el contexto de ejecución que contiene esa llamada a función vuelve a ser el elemento más alto de la pila y reanuda la ejecución.

Estos contextos de ejecución capturan cualquier valor necesario para su ejecución. Ellas establece también las variables y funciones disponibles dentro del alcance del en función del contexto superior y determina y configura el valor del this en el contexto de la función.

El bucle de eventos y la cola de devolución de llamada

La ejecución secuencial implica que las tareas asíncronas que incluyen devoluciones de llamada funciones, como recuperar datos de un servidor, responder a la interacción del usuario, o esperando los temporizadores establecidos con setTimeout o setInterval, se bloqueará principal hasta que se complete la tarea o interrumpir el proceso el contexto de ejecución actual en el momento en que se abre el contexto de la función de devolución de llamada se agrega a la pila. Para solucionar esto, JavaScript administra tareas asíncronas con un “modelo de simultaneidad” controlado por eventos compuesto por el "bucle de eventos" y las "cola de devolución de llamada" (también conocida como "cola de mensajes").

Cuando se ejecuta una tarea asíncrona en el subproceso principal, la devolución de llamada el contexto de ejecución de la función se coloca en la cola de devolución de llamada, no en la parte superior en la pila de llamadas. El bucle de eventos es un patrón que a veces se denomina reactor, que continuamente sondea el estado de la pila de llamadas y la cola de devolución de llamada. Si hay tareas en la cola de devolución de llamada y el bucle de eventos determina que la pila de llamadas está vacía las tareas de la cola de devolución de llamada se envían a la pila, una por vez, para que se ejecutado.