4.1 El lenguaje JavaScript comienza rápidamente

El autor:La bondad, Creado: 2019-04-26 11:46:12, Actualizado: 2019-04-27 11:53:43

Antecedentes

Esta sección ofrece un poco de conocimiento sobre JavaScript para ayudarle a entender por qué es de la forma en que es.

JavaScript frente a ECMAScript

ECMAScript es el nombre oficial de JavaScript. Un nuevo nombre se hizo necesario porque hay una marca registrada en JavaScript (tenida originalmente por Sun, ahora por Oracle). En este momento, Mozilla es una de las pocas compañías autorizadas a usar oficialmente el nombre JavaScript porque recibió una licencia hace mucho tiempo. Para el uso común, se aplican estas reglas:

  • JavaScript significa el lenguaje de programación.
  • ECMAScript es el nombre utilizado por la especificación del lenguaje. Por lo tanto, cuando se refiere a las versiones del lenguaje, la gente dice ECMAScript. La versión actual de JavaScript es ECMAScript 5; ECMAScript 6 se está desarrollando actualmente.

Influencias y naturaleza del lenguaje

El creador de JavaScript, Brendan Eich, no tuvo más remedio que crear el lenguaje muy rápidamente (o otras tecnologías, peores, habrían sido adoptadas por Netscape). Tomó prestado de varios lenguajes de programación: Java (sintáctica, valores primitivos versus objetos), Scheme y AWK (funciones de primera clase), Self (herencia de prototipos) y Perl y Python (cuerdas, matrices y expresiones regulares).

JavaScript no tenía manejo de excepciones hasta ECMAScript 3, lo que explica por qué el lenguaje con tanta frecuencia convierte valores automáticamente y con tanta frecuencia falla silenciosamente: inicialmente no podía lanzar excepciones.

Por un lado, JavaScript tiene peculiaridades y le falta bastante funcionalidad (variables de alcance de bloques, módulos, soporte para subclases, etc.). Por otro lado, tiene varias características poderosas que le permiten evitar estos problemas. En otros lenguajes, aprende características del lenguaje. En JavaScript, a menudo aprende patrones en su lugar.

Dadas sus influencias, no es de extrañar que JavaScript permita un estilo de programación que es una mezcla de programación funcional (funciones de orden superior; mapa incorporado, reducir, etc.) y programación orientada a objetos (objetos, herencia).

La sintaxis

Esta sección explica los principios sintácticos básicos de JavaScript.

Una visión general de la sintaxis

Algunos ejemplos de sintaxis:

// Two slashes start single-line comments

var x;  // declaring a variable

x = 3 + y;  // assigning a value to the variable `x`

foo(x, y);  // calling function `foo` with parameters `x` and `y`
obj.bar(3);  // calling method `bar` of object `obj`

// A conditional statement
if (x === 0) {  // Is `x` equal to zero?
    x = 123;
}

// Defining function `baz` with parameters `a` and `b`
function baz(a, b) {
    return a + b;
}

Observe los dos usos diferentes del signo igual:

  • Un solo signo igual (=) se utiliza para asignar un valor a una variable.
  • Un signo triple igual (===) se utiliza para comparar dos valores (ver Operadores de igualdad).

Las declaraciones frente a las expresiones

Para entender la sintaxis de JavaScript, debe saber que tiene dos categorías sintácticas principales: declaraciones y expresiones:

  • Un programa es una secuencia de instrucciones. Aquí hay un ejemplo de una instrucción, que declara (crea) una variable foo:
var foo;
  • Las expresiones producen valores. Son argumentos de función, el lado derecho de una asignación, etc. Aquí hay un ejemplo de una expresión:
3 * 7

La distinción entre instrucciones y expresiones se ilustra mejor por el hecho de que JavaScript tiene dos formas diferentes de hacer if-then-else como una instrucción:

var x;
if (y >= 0) {
    x = y;
} else {
    x = -y;
}

o como una expresión:

var x = y >= 0 ? y : -y;

Puede usar este último como un argumento de función (pero no el primero):

myFunction(y >= 0 ? y : -y)

Finalmente, donde JavaScript espera una declaración, también puede usar una expresión; por ejemplo:

foo(7, 1);

Toda la línea es una instrucción (una llamada instrucción de expresión), pero la llamada de la función foo ((7, 1) es una expresión.

Los puntos y coma

Los puntos y coma son opcionales en JavaScript. Sin embargo, recomiendo incluirlos siempre, porque de lo contrario JavaScript puede adivinar mal sobre el final de una declaración. Los detalles se explican en la inserción automática de puntos y coma.

Los puntos y coma terminan en las instrucciones, pero no en los bloques. Hay un caso en el que verás un punto y coma después de un bloque: una expresión de función es una expresión que termina en un bloque. Si tal expresión viene al final de una instrucción, es seguida por un punto y coma:

// Pattern: var _ = ___;
var x = 3 * 7;
var f = function () { };  // function expr. inside var decl.

Comentarios de la Comisión

JavaScript tiene dos tipos de comentarios: comentarios de una sola línea y comentarios de varias líneas. Los comentarios de una sola línea comienzan con // y se terminan al final de la línea:

x++; // single-line comment

Los comentarios de varias líneas están delimitados por /* y */:

/* This is
   a multiline
   comment.
 */

Variables y asignación

Las variables en JavaScript se declaran antes de ser utilizadas:

var foo;  // declare variable `foo`

Asesoramiento

Puede declarar una variable y asignar un valor al mismo tiempo:

var foo = 6;

También puede asignar un valor a una variable existente:

foo = 4;  // change variable `foo`

Operadores de asignaciones compuestas

Existen operadores de asignación compuestos como +=. Las siguientes dos asignaciones son equivalentes:

x += 1;
x = x + 1;

Identificadores y nombres de variables

Los identificadores son nombres que desempeñan varios roles sintácticos en JavaScript. Por ejemplo, el nombre de una variable es un identificador. Los identificadores son sensibles a mayúsculas y minúsculas.

Aproximadamente, el primer carácter de un identificador puede ser cualquier letra de Unicode, un signo de dólar ($), o un punto bajo (_).

arg0
_tmp
$elem
π

Los siguientes identificadores son palabras reservadas, son parte de la sintaxis y no pueden utilizarse como nombres de variables (incluidos los nombres de funciones y nombres de parámetros):

img

Los siguientes tres identificadores no son palabras reservadas, pero debe tratarlos como si lo fueran:

img

Por último, también debe mantenerse alejado de los nombres de las variables globales estándar.

Los valores

JavaScript tiene muchos valores que hemos llegado a esperar de los lenguajes de programación: booleans, números, cadenas, matrices, y así sucesivamente. Todos los valores en JavaScript tienen propiedades. Cada propiedad tiene una clave (o nombre) y un valor.

value.propKey

Por ejemplo, la cadena abc tiene la longitud de propiedad:

> var str = 'abc';
> str.length
3

Lo anterior también se puede escribir como:

> 'abc'.length
3
The dot operator is also used to assign a value to a property:

> var obj = {};  // empty object
> obj.foo = 123; // create property `foo`, set it to 123
123
> obj.foo
123

Y puedes usarlo para invocar métodos:

> 'hello'.toUpperCase()
'HELLO'

En el ejemplo anterior, hemos invocado el método toUpperCase() en el valor hello.

Los valores primitivos frente a los objetos

JavaScript hace una distinción algo arbitraria entre los valores:

  • Los valores primitivos son booleanos, números, cadenas, nulo y indefinido.
  • Todos los demás valores son objetos. Una diferencia importante entre los dos es la forma en que se comparan; cada objeto tiene una identidad única y solo es (estrictamente) igual a sí mismo:
> var obj1 = {};  // an empty object
> var obj2 = {};  // another empty object
> obj1 === obj2
false
> obj1 === obj1
true

En contraste, todos los valores primitivos que codifican el mismo valor se consideran iguales:

> var prim1 = 123;
> var prim2 = 123;
> prim1 === prim2
true

Las siguientes dos secciones explican los valores y objetos primitivos con más detalle.

Valores primitivos

Los siguientes son todos los valores primitivos (o primitivos para abreviar):

  • Booleans: verdadero, falso (ver Booleans)
  • Números: 1736, 1.351 (véase Números)
  • Cuerdas: abc, abc (ver Cuerdas)
  • Dos no valores: indefinido, nulo (ver indefinido y nulo)

Los primitivos tienen las siguientes características:

Comparado por valor

El contenido se compara:

> 3 === 3
true
> 'abc' === 'abc'
true

Siempre inmutable Las propiedades no se pueden cambiar, añadir o eliminar:

> var str = 'abc';

> str.length = 1; // try to change property `length`
> str.length      // ⇒ no effect
3

> str.foo = 3; // try to create property `foo`
> str.foo      // ⇒ no effect, unknown property
undefined

(La lectura de una propiedad desconocida siempre devuelve indefinido.)

Objetos

Todos los valores no primitivos son objetos. Los tipos más comunes de objetos son:

  • Objetos simples, que pueden ser creados por objetos literales (ver Objetos únicos):
{
    firstName: 'Jane',
    lastName: 'Doe'
}

El objeto anterior tiene dos propiedades: el valor de la propiedad firstName es Jane y el valor de la propiedad lastName es Doe.

  • Arrays, que pueden ser creados por literales de matriz (ver Arrays):
[ 'apple', 'banana', 'cherry' ]

La matriz anterior tiene tres elementos a los que se puede acceder a través de índices numéricos.

  • Expresiones regulares, que pueden ser creadas por letras de expresión regular (ver Expresiones regulares):
/^a+b+$/

Los objetos tienen las siguientes características:

Comparado por referencia

Las identidades se comparan; cada valor tiene su propia identidad:

> ({} === {})  // two different empty objects
false

> var obj1 = {};
> var obj2 = obj1;
> obj1 === obj2
true

Mutable por defecto

Normalmente puede cambiar, agregar y eliminar propiedades libremente (ver Objetos únicos):

> var obj = {};
> obj.foo = 123; // add property `foo`
> obj.foo
123

indefinido y nulo

La mayoría de los lenguajes de programación tienen valores que denotan información que falta.

  • undefined significa no hay valor. Las variables no inicializadas no están definidas:
> var foo;
> foo
undefined

Los parámetros faltantes no están definidos:

> function f(x) { return x }
> f()
undefined

Si usted lee una propiedad inexistente, se obtiene indefinido:

> var obj = {}; // empty object
> obj.foo
undefined
  • null significa sin objeto. Se usa como un no valor cuando se espera un objeto (parámetros, último en una cadena de objetos, etc.).

Advertencia

undefined y null no tienen propiedades, ni siquiera métodos estándar como toString().

Verificación de no definido o nulo

Las funciones normalmente le permiten indicar un valor que falta a través de indefinido o nulo.

if (x === undefined || x === null) {
    ...
}

También puede explotar el hecho de que tanto indefinido como nulo se consideran falsos:

if (!x) {
    ...
}

Advertencia

false, 0, NaN y también se consideran falsos (ver Verdadero y Falso).

Categorizar los valores utilizando el tipo y la instancia de

Existen dos operadores para categorizar valores: typeof se utiliza principalmente para valores primitivos, mientras que instanceof se utiliza para objetos. El tipo de aspecto es el siguiente:

typeof value

Devuelve una cadena que describe el type de valor.

> typeof true
'boolean'
> typeof 'abc'
'string'
> typeof {} // empty object literal
'object'
> typeof [] // empty array literal
'object'

En la tabla siguiente se enumeran todos los resultados del tipo:

img

typeof return null object es un error que no puede ser corregido, porque rompería el código existente.

Un ejemplo de esto es:

value instanceof Constr

Devuelve true si el valor es un objeto que ha sido creado por el constructor Constr (ver Constructores: fábricas para objetos).

> var b = new Bar();  // object created by constructor Bar
> b instanceof Bar
true

> {} instanceof Object
true
> [] instanceof Array
true
> [] instanceof Object  // Array is a subconstructor of Object
true

> undefined instanceof Object
false
> null instanceof Object
false

Los Booleanos

El tipo booleano primitivo comprende los valores verdadero y falso.

  • Operadores lógicos binarios: && (Y),
  • Operador lógico con prefijo:! (No)
  • Operadores de comparación:Operadores de igualdad: ===,!==, ==,!=
  • Operadores de orden (para cadenas y números): >, >=, <, <=

Verdaderos y falsos

Cuando JavaScript espera un valor booleano (por ejemplo, para la condición de una instrucción if), se puede usar cualquier valor. Se interpretará como verdadero o falso.

  • no definido, nulo
  • Boolean: falso
  • Número: 0, NaN
  • Cuadrícula:

Todos los demás valores (incluidos todos los objetos!) se consideran verdaderos. Los valores interpretados como falsos se llaman falsos, y los valores interpretados como verdaderos se llaman verdaderos.

> Boolean(undefined)
false
> Boolean(0)
false
> Boolean(3)
true
> Boolean({}) // empty object
true
> Boolean([]) // empty array
true

Operadores lógicos binarios

Los operadores lógicos binarios en JavaScript son de cortocircuito. Es decir, si el primer operando es suficiente para determinar el resultado, el segundo operando no se evalúa. Por ejemplo, en las siguientes expresiones, la función foo() nunca se llama:

false && foo()
true  || foo()

Además, los operadores lógicos binarios devuelven cualquiera de sus operandos, que pueden o no ser booleanos.

Y (&&)

Si el primer operando es falso, devuélvelo. De lo contrario, devuelve el segundo operando:

> NaN && 'abc'
NaN
> 123 && 'abc'
'abc'

O también...

Si el primer operando es verdadero, devuélvelo. De lo contrario, devuelve el segundo operando:

> 'abc' || 123
'abc'
> '' || 123
123

Operadores de la igualdad

JavaScript tiene dos tipos de igualdad:

  • Normal, o leniente, (en) igualdad: == y!=
  • Estricto (en) igualdad: === y!==

La igualdad normal considera (demasiado) muchos valores iguales (los detalles se explican en la igualdad normal (==,!=)), lo que puede ocultar errores. Por lo tanto, siempre se recomienda usar igualdad estricta.

Las cifras

Todos los números en JavaScript son con coma flotante:

> 1 === 1.0
true

Los números especiales incluyen los siguientes:

NaN (no es un número) Un valor de error:

> Number('xyz')  // 'xyz' can’t be converted to a number
NaN

El infinito También en su mayoría un valor de error:

> 3 / 0
Infinity
> Math.pow(2, 1024)  // number too large
Infinity

Infinity es más grande que cualquier otro número (excepto NaN). Del mismo modo, -Infinity es más pequeño que cualquier otro número (excepto NaN). Eso hace que estos números sean útiles como valores predeterminados (por ejemplo, cuando estás buscando un mínimo o un máximo).

Operadores

JavaScript tiene los siguientes operadores aritméticos (ver Operadores aritméticos):

  • Adición: número1 + número2
  • Sustracción: número 1 - número 2
  • Multiplicación: número1 * número2
  • División: número 1 / número 2
  • El resto: número1 % número2
  • Incremento: ++variable, variable++
  • Decremento: variable, variable
  • Negativo: -valor
  • Conversión a número: +valor

El objeto global Math (ver Math) proporciona más operaciones aritméticas, a través de funciones.

JavaScript también tiene operadores para operaciones bitwise (por ejemplo, bitwise And; ver Bitwise Operators).

Las cuerdas

Las cadenas se pueden crear directamente a través de literales de cadena. Esos literales están delimitados por comillas simples o dobles. La barra de retroceso () escapa de caracteres y produce algunos caracteres de control.

'abc'
"abc"

'Did she say "Hello"?'
"Did she say \"Hello\"?"

'That\'s nice!'
"That's nice!"

'Line 1\nLine 2'  // newline
'Backlash: \\'

Se accede a los caracteres individuales a través de corchetes cuadrados:

> var str = 'abc';
> str[1]
'b'

La longitud de la propiedad cuenta el número de caracteres en la cadena:

> 'abc'.length
3

Al igual que todos los primitivos, las cadenas son inmutables; necesitas crear una nueva cadena si quieres cambiar una existente.

Operadores de cadena

Las cadenas se concatenan a través del operador más (+), que convierte el otro operando en una cadena si uno de los operando es una cadena:

> var messageCount = 3;
> 'You have ' + messageCount + ' messages'
'You have 3 messages'

Para concatenar cadenas en múltiples pasos, use el operador +=:

> var str = '';
> str += 'Multiple ';
> str += 'pieces ';
> str += 'are concatenated.';
> str
'Multiple pieces are concatenated.'

Métodos de cadena

Las cadenas tienen muchos métodos útiles (ver Métodos de prototipos de cadenas).

> 'abc'.slice(1)  // copy a substring
'bc'
> 'abc'.slice(1, 2)
'b'

> '\t xyz  '.trim()  // trim whitespace
'xyz'

> 'mjölnir'.toUpperCase()
'MJÖLNIR'

> 'abc'.indexOf('b')  // find a string
1
> 'abc'.indexOf('x')
-1

Las declaraciones

Las condiciones y los bucles en JavaScript se introducen en las siguientes secciones.

Las condiciones

La instrucción if tiene una cláusula then y una cláusula else opcional que se ejecutan dependiendo de una condición booleana:

if (myvar === 0) {
    // then
}

if (myvar === 0) {
    // then
} else {
    // else
}

if (myvar === 0) {
    // then
} else if (myvar === 1) {
    // else-if
} else if (myvar === 2) {
    // else-if
} else {
    // else
}

Yo recomiendo usar siempre corchetes (que denotan bloques de cero o más instrucciones). Pero usted no tiene que hacerlo si una cláusula es sólo una sola instrucción (lo mismo vale para las instrucciones de flujo de control para y mientras):

if (x < 0) return -x;

El siguiente es un comando de conmutación. El valor de fruta decide qué caso se ejecuta:

switch (fruit) {
    case 'banana':
        // ...
        break;
    case 'apple':
        // ...
        break;
    default:  // all other cases
        // ...
}

El operand después del caso puede ser cualquier expresión; se compara a través de === con el parámetro de switch.

Los bucles

El bucle for tiene el siguiente formato:

for (⟦«init»⟧; ⟦«condition»⟧; ⟦«post_iteration»⟧)
    «statement»

init se ejecuta al comienzo del bucle. la condición se comprueba antes de cada iteración del bucle; si se vuelve falsa, entonces el bucle se termina. post_iteration se ejecuta después de cada iteración del bucle.

Este ejemplo imprime todos los elementos de la matriz arr en la consola:

for (var i=0; i < arr.length; i++) {
    console.log(arr[i]);
}

El bucle mientras continúa bucleando sobre su cuerpo mientras su condición se mantiene:

// Same as for loop above:
var i = 0;
while (i < arr.length) {
    console.log(arr[i]);
    i++;
}

El bucle do-while continúa recorriendo su cuerpo mientras se mantiene su condición.

do {
    // ...
} while (condition);

En todos los bucles:

  • La ruptura deja el bucle.
  • Continuar comienza una nueva iteración del bucle.

Funciones

Una forma de definir una función es a través de una declaración de función:

function add(param1, param2) {
    return param1 + param2;
}

El código anterior define una función, suma, que tiene dos parámetros, param1 y param2, y devuelve la suma de ambos parámetros.

> add(6, 1)
7
> add('a', 'b')
'ab'

Otra forma de definir add() es asignar una expresión de función a una variable add:

var add = function (param1, param2) {
    return param1 + param2;
};

Una expresión de función produce un valor y, por lo tanto, se puede usar para pasar directamente funciones como argumentos a otras funciones:

someOtherFunction(function (p1, p2) { ... });

Las declaraciones de funciones son izadas

Las declaraciones de funciones se elevan se mueven en su totalidad al comienzo del alcance actual.

function foo() {
    bar();  // OK, bar is hoisted
    function bar() {
        ...
    }
}

Tenga en cuenta que, aunque las declaraciones var también se elevan (ver Variables Are Hoisted), las asignaciones realizadas por ellas no son:

function foo() {
    bar();  // Not OK, bar is still undefined
    var bar = function () {
        // ...
    };
}

Los argumentos de las variables especiales

Puede llamar a cualquier función en JavaScript con una cantidad arbitraria de argumentos; el lenguaje nunca se quejará. Sin embargo, hará que todos los parámetros estén disponibles a través de los argumentos de variables especiales. argumentos se parece a una matriz, pero no tiene ninguno de los métodos de matriz:

> function f() { return arguments }
> var args = f('a', 'b', 'c');
> args.length
3
> args[0]  // read element at index 0
'a'

Demasiados o muy pocos argumentos

Usemos la siguiente función para explorar cómo se manejan demasiados o muy pocos parámetros en JavaScript (la función toArray( se muestra en Convertir argumentos a una matriz):

function f(x, y) {
    console.log(x, y);
    return toArray(arguments);
}

Se ignorarán otros parámetros (excepto los de los argumentos):

> f('a', 'b', 'c')
a b
[ 'a', 'b', 'c' ]

Los parámetros faltantes darán el valor no definido:

> f('a')
a undefined
[ 'a' ]
> f()
undefined undefined
[]

Parámetros opcionales

El siguiente es un patrón común para asignar valores predeterminados a los parámetros:

function pair(x, y) {
    x = x || 0;  // (1)
    y = y || 0;
    return [ x, y ];
}

En la línea (1), el operador de referencia devuelve x si es verdadero (no nulo, no definido, etc.).

> pair()
[ 0, 0 ]
> pair(3)
[ 3, 0 ]
> pair(3, 5)
[ 3, 5 ]

Imponiendo una aridad

Si desea hacer cumplir una aridad (un número específico de parámetros), puede comprobar argumentos.length:

function pair(x, y) {
    if (arguments.length !== 2) {
        throw new Error('Need exactly 2 arguments');
    }
    ...
}

Convertir argumentos en una matriz

los argumentos no son una matriz, sino que solo son similares a una matriz (ver Objetos similares a una matriz y métodos genéricos). Tiene una longitud de propiedad, y puede acceder a sus elementos a través de índices entre paréntesis cuadrados. Sin embargo, no puede eliminar elementos o invocar ninguno de los métodos de matriz en él. Por lo tanto, a veces necesita convertir argumentos a una matriz, que es lo que hace la siguiente función (se explica en Objetos similares a una matriz y métodos genéricos):

function toArray(arrayLikeObject) {
    return Array.prototype.slice.call(arrayLikeObject);
}

Manejo de excepciones

La forma más común de gestionar las excepciones (véase el capítulo 14) es la siguiente:

function getPerson(id) {
    if (id < 0) {
        throw new Error('ID must not be negative: '+id);
    }
    return { id: id }; // normally: retrieved from database
}

function getPersons(ids) {
    var result = [];
    ids.forEach(function (id) {
        try {
            var person = getPerson(id);
            result.push(person);
        } catch (exception) {
            console.log(exception);
        }
    });
    return result;
}

La cláusula try rodea el código crítico, y la cláusula catch se ejecuta si se arroja una excepción dentro de la cláusula try.

> getPersons([2, -5, 137])
[Error: ID must not be negative: -5]
[ { id: 2 }, { id: 137 } ]

Modo estricto

El modo estricto (ver Modo estricto) permite más advertencias y hace que JavaScript sea un lenguaje más limpio (el modo no estricto a veces se llama modo flojo).

'use strict';

También puede habilitar el modo estricto por función:

function functionInStrictMode() {
    'use strict';
}

Alcance variable y cierre

En JavaScript, se declaran variables a través de var antes de usarlas:

> var x;
> x
undefined
> y
ReferenceError: y is not defined

Puede declarar e iniciar varias variables con una sola instrucción var:

var x = 1, y = 2, z = 3;

Pero yo recomiendo usar una declaración por variable (la razón se explica en la sintaxis).

var x = 1;
var y = 2;
var z = 3;

Debido a la elevación (ver Variables Are Hoisted), generalmente es mejor declarar variables al comienzo de una función.

Las variables tienen un alcance funcional

El alcance de una variable es siempre la función completa (en lugar del bloque actual).

function foo() {
    var x = -512;
    if (x < 0) {  // (1)
        var tmp = -x;
        ...
    }
    console.log(tmp);  // 512
}

Podemos ver que la variable tmp no está restringida al bloque que comienza en la línea (1); existe hasta el final de la función.

Se elevan las variables

Cada declaración de variable se eleva: la declaración se mueve al comienzo de la función, pero las asignaciones que hace permanecen puestas.

function foo() {
    console.log(tmp); // undefined
    if (false) {
        var tmp = 3;  // (1)
    }
}

Internamente, la función anterior se ejecuta así:

function foo() {
    var tmp;  // hoisted declaration
    console.log(tmp);
    if (false) {
        tmp = 3;  // assignment stays put
    }
}

Cierre de operaciones

Cada función permanece conectada a las variables de las funciones que la rodean, incluso después de que abandona el ámbito en el que fue creada.

function createIncrementor(start) {
    return function () {  // (1)
        start++;
        return start;
    }
}

La función que comienza en la línea (1) deja el contexto en el que fue creada, pero permanece conectada a una versión en vivo de start:

> var inc = createIncrementor(5);
> inc()
6
> inc()
7
> inc()
8

Un cierre es una función más la conexión a las variables de sus ámbitos circundantes.

El modelo IIFE: introducción de un nuevo ámbito de aplicación

Algunas veces se quiere introducir un nuevo ámbito de una variable, por ejemplo, para evitar que una variable se vuelva global. En JavaScript, no se puede usar un bloque para hacerlo; se debe usar una función. Pero hay un patrón para usar una función de una manera similar a un bloque. Se llama IIFE (expresión de función invocada inmediatamente, pronunciada iffy):

(function () {  // open IIFE
    var tmp = ...;  // not a global variable
}());  // close IIFE

Asegúrese de escribir el ejemplo anterior exactamente como se muestra (aparte de los comentarios). Un IIFE es una expresión de función que se llama inmediatamente después de definirla. Dentro de la función, existe un nuevo alcance, lo que evita que tmp se vuelva global. Consulte Introducir un nuevo alcance a través de un IIFE para obtener detalles sobre los IIFEs.

Caso de uso del IIFE: uso compartido involuntario mediante cierres

Los cierres mantienen sus conexiones con variables externas, que a veces no es lo que quieres:

var result = [];
for (var i=0; i < 5; i++) {
    result.push(function () { return i });  // (1)
}
console.log(result[1]()); // 5 (not 1)
console.log(result[3]()); // 5 (not 3)

El valor devuelto en la línea (1) es siempre el valor actual de i, no el valor que tenía cuando se creó la función. Después de que se termine el bucle, i tiene el valor 5, por lo que todas las funciones en la matriz devuelven ese valor. Si desea que la función en la línea (1) reciba una instantánea del valor actual de i, puede usar un IIFE:

for (var i=0; i < 5; i++) {
    (function () {
        var i2 = i; // copy current i
        result.push(function () { return i2 });
    }());
}

Objetos y constructores

Esta sección cubre dos mecanismos básicos orientados a objetos de JavaScript: objetos individuales y constructores (que son fábricas para objetos, similares a las clases en otros lenguajes).

Objetivos únicos

Como todos los valores, los objetos tienen propiedades. De hecho, podrías considerar un objeto como un conjunto de propiedades, donde cada propiedad es un par (clave, valor). La clave es una cadena, y el valor es cualquier valor de JavaScript.

En JavaScript, puedes crear objetos simples directamente, a través de objetos literales:

'use strict';
var jane = {
    name: 'Jane',

    describe: function () {
        return 'Person named '+this.name;
    }
};

El objeto anterior tiene las propiedades name y describe.

> jane.name  // get
'Jane'
> jane.name = 'John';  // set
> jane.newProperty = 'abc';  // property created automatically

Las propiedades con valor de función como describe se llaman métodos.

> jane.describe()  // call method
'Person named John'
> jane.name = 'Jane';
> jane.describe()
'Person named Jane'

El operador in comprueba si existe una propiedad:

> 'newProperty' in jane
true
> 'foo' in jane
false

Si se lee una propiedad que no existe, se obtiene el valor undefined. Por lo tanto, las dos comprobaciones anteriores también podrían realizarse de esta manera:

> jane.newProperty !== undefined
true
> jane.foo !== undefined
false

El operador borrar elimina una propiedad:

> delete jane.newProperty
true
> 'newProperty' in jane
false

Claves de propiedad arbitrarias

Una clave de propiedad puede ser cualquier cadena. Hasta ahora, hemos visto claves de propiedad en literales de objetos y después del operador de puntos. Sin embargo, solo puedes usarlas de esa manera si son identificadores (ver Identificadores y nombres de variables). Si quieres usar otras cadenas como claves, debes citarlas en una literal de objetos y usar paréntesis cuadrados para obtener y establecer la propiedad:

> var obj = { 'not an identifier': 123 };
> obj['not an identifier']
123
> obj['not an identifier'] = 456;

Los paréntesis cuadrados también permiten calcular la clave de una propiedad:

> var obj = { hello: 'world' };
> var x = 'hello';

> obj[x]
'world'
> obj['hel'+'lo']
'world'

Métodos de extracción

Si extrae un método, pierde su conexión con el objeto. por sí sola, la función ya no es un método, y esto tiene el valor indefinido (en modo estricto).

Como ejemplo, volvamos al objeto anterior Jane:

'use strict';
var jane = {
    name: 'Jane',

    describe: function () {
        return 'Person named '+this.name;
    }
};

Queremos extraer el método describe de jane, ponerlo en una función variable, y llamarlo.

> var func = jane.describe;
> func()
TypeError: Cannot read property 'name' of undefined

La solución es usar el método bind() que todas las funciones tienen.

> var func2 = jane.describe.bind(jane);
> func2()
'Person named Jane'

Funciones dentro de un método

Cada función tiene su propia variable especial this. Esto es inconveniente si anida una función dentro de un método, porque no puede acceder al método this desde la función. El siguiente es un ejemplo donde llamamos a forEach con una función para iterar sobre una matriz:

var jane = {
    name: 'Jane',
    friends: [ 'Tarzan', 'Cheeta' ],
    logHiToFriends: function () {
        'use strict';
        this.friends.forEach(function (friend) {
            // `this` is undefined here
            console.log(this.name+' says hi to '+friend);
        });
    }
}

Llamando al registro HiToFriends se produce un error:

> jane.logHiToFriends()
TypeError: Cannot read property 'name' of undefined

Primero, podríamos almacenar esto en una variable diferente:

logHiToFriends: function () {
    'use strict';
    var that = this;
    this.friends.forEach(function (friend) {
        console.log(that.name+' says hi to '+friend);
    });
}

O, forEach tiene un segundo parámetro que le permite proporcionar un valor para esto:

logHiToFriends: function () {
    'use strict';
    this.friends.forEach(function (friend) {
        console.log(this.name+' says hi to '+friend);
    }, this);
}

Las expresiones de función a menudo se utilizan como argumentos en las llamadas de función en JavaScript.

Constructores: fábricas de objetos

Hasta ahora, puede pensar que los objetos JavaScript son solo mapas de cadenas a valores, una noción sugerida por los literales de objetos de JavaScript, que se parecen a los literales de mapa / diccionario de otros lenguajes. Sin embargo, los objetos JavaScript también admiten una característica que es verdaderamente orientada a objetos: herencia.

Además de ser funciones y métodos reales, las funciones juegan otro papel en JavaScript: se convierten en constructores si se invocan a través del nuevo operador.

// Set up instance data
function Point(x, y) {
    this.x = x;
    this.y = y;
}
// Methods
Point.prototype.dist = function () {
    return Math.sqrt(this.x*this.x + this.y*this.y);
};

Podemos ver que un constructor tiene dos partes. Primero, la función Point establece los datos de la instancia. Segundo, la propiedad Point.prototype contiene un objeto con los métodos. Los datos anteriores son específicos de cada instancia, mientras que los datos posteriores se comparten entre todas las instancias.

Para usar Point, lo invocamos a través del nuevo operador:

> var p = new Point(3, 5);
> p.x
3
> p.dist()
5.830951894845301

p es una instancia de Point:

> p instanceof Point
true

Las matrices

Las matrices son secuencias de elementos a los que se puede acceder a través de índices enteros que comienzan en cero.

Las letras de la matriz

Los literales de matriz son útiles para crear matrices:

> var arr = [ 'a', 'b', 'c' ];

La matriz anterior tiene tres elementos: las cadenas a, b y c. Puede acceder a ellas a través de índices enteros:

> arr[0]
'a'
> arr[0] = 'x';
> arr
[ 'x', 'b', 'c' ]

La propiedad length indica cuántos elementos tiene una matriz.

> var arr = ['a', 'b'];
> arr.length
2

> arr[arr.length] = 'c';
> arr
[ 'a', 'b', 'c' ]
> arr.length
3

> arr.length = 1;
> arr
[ 'a' ]

El operador in también funciona para matrices:

> var arr = [ 'a', 'b', 'c' ];
> 1 in arr // is there an element at index 1?
true
> 5 in arr // is there an element at index 5?
false

Tenga en cuenta que las matrices son objetos y por lo tanto pueden tener propiedades de objetos:

> var arr = [];
> arr.foo = 123;
> arr.foo
123

Métodos de matriz

Las matrices tienen muchos métodos (ver Métodos de prototipos de matrices).

> var arr = [ 'a', 'b', 'c' ];

> arr.slice(1, 2)  // copy elements
[ 'b' ]
> arr.slice(1)
[ 'b', 'c' ]

> arr.push('x')  // append an element
4
> arr
[ 'a', 'b', 'c', 'x' ]

> arr.pop()  // remove last element
'x'
> arr
[ 'a', 'b', 'c' ]

> arr.shift()  // remove first element
'a'
> arr
[ 'b', 'c' ]

> arr.unshift('x')  // prepend an element
3
> arr
[ 'x', 'b', 'c' ]

> arr.indexOf('b')  // find the index of an element
1
> arr.indexOf('y')
-1

> arr.join('-')  // all elements in a single string
'x-b-c'
> arr.join('')
'xbc'
> arr.join()
'x,b,c'

Iteración sobre matriz

Hay varios métodos de matriz para la iteración sobre elementos (ver Iteración (no destructiva)). Los dos más importantes son forEach y map.

forEach se repite en una matriz y entrega el elemento actual y su índice a una función:

[ 'a', 'b', 'c' ].forEach(
    function (elem, index) {  // (1)
        console.log(index + '. ' + elem);
    });

El código anterior produce la siguiente salida:

0. a
1. b
2. c

Tenga en cuenta que la función en la línea (1) es libre de ignorar argumentos.

map crea una nueva matriz aplicando una función a cada elemento de una matriz existente:

> [1,2,3].map(function (x) { return x*x })
[ 1, 4, 9 ]

Expresiones regulares

JavaScript tiene soporte incorporado para expresiones regulares.

/^abc$/
/[A-Za-z0-9]+/

Prueba de método: ¿Hay coincidencia?

> /^a+b+$/.test('aaab')
true
> /^a+b+$/.test('aaa')
false

Método exec ((): Grupos de coincidencia y captura

> /a(b+)a/.exec('_abbba_aba_')
[ 'abbba', 'bbb' ]

La matriz devuelta contiene la coincidencia completa en el índice 0, la captura del primer grupo en el índice 1, y así sucesivamente. Hay una manera (discutido en RegExp.prototype.exec: Capture Groups) de invocar este método repetidamente para obtener todas las coincidencias.

Método replace ((): Buscar y reemplazar

> '<a> <bbb>'.replace(/<(.*?)>/g, '[$1]')
'[a] [bbb]'

El primer parámetro de replace debe ser una expresión regular con una bandera /g; de lo contrario, solo se reemplaza la primera ocurrencia.

Matemáticas

La matemática es un objeto con funciones aritméticas.

> Math.abs(-2)
2

> Math.pow(3, 2)  // 3 to the power of 2
9

> Math.max(2, -1, 5)
5

> Math.round(1.9)
2

> Math.PI  // pre-defined constant for π
3.141592653589793

> Math.cos(Math.PI)  // compute the cosine for 180°
-1

Más.