JavaScript es un lenguaje complejo. Si usted, en cualquier nivel, participa en el desarrollo de JavaScript, esto significa que es vital que comprenda los conceptos básicos de este lenguaje. El material, cuya traducción publicamos hoy, cubre 12 conceptos críticos de JavaScript. Por supuesto, el desarrollador de JavaScript necesita saber mucho más, pero sin lo que vamos a hablar hoy, definitivamente no puede hacerlo.

1. Variables que almacenan valores y referencias
Comprender cómo se asignan exactamente los valores variables en JavaScript es extremadamente importante para aquellos que desean escribir correctamente el código de trabajo. El malentendido de este mecanismo lleva a escribir programas en los que los valores de las variables pueden cambiar inadvertidamente.
JavaScript, si una entidad tiene uno de los tipos primitivos (en particular, los tipos
Boolean
,
null
,
undefined
, de
String
y
Number
), siempre funciona con el valor de esta entidad. Es decir, el valor se escribe en la variable correspondiente. Si estamos hablando de un objeto (por ejemplo, los tipos
Object
,
Array
,
Function
), al asignarlo a una variable, se le escribe una referencia, la dirección en la que se encuentra en la memoria.
Considera un ejemplo. En el siguiente fragmento de código, se escribe una cadena en
var1
. Después de eso, el valor de
var2
escribe en la variable
var2
. Dado que la variable
var1
tiene un tipo primitivo (
String
), se escribirá una copia de la cadena disponible en
var1
en
var1
. Esto nos permite considerar
var2
como una variable que es completamente independiente de
var1
, aunque almacena el mismo valor que
var1
. Escribir un nuevo valor en
var1
no afecta a
var1
.
let var1 = 'My string'; let var2 = var1; var2 = 'My new string'; console.log(var1);
Ahora considere un ejemplo de trabajo con objetos.
let var1 = { name: 'Jim' } let var2 = var1; var2.name = 'John'; console.log(var1);
Como puede ver, aquí estamos trabajando con la variable
var2
, y lo que le sucede se refleja en la variable
var1
, ya que almacenan una referencia al mismo objeto. Es fácil imaginar a qué puede conducir esto en código real si alguien decide que las variables que almacenan objetos se comportan de la misma manera que las variables que almacenan valores de tipos primitivos. Esto es especialmente desagradable, por ejemplo, en los casos en que crean una función que está diseñada para funcionar con el valor del objeto que se le pasa, y esta función cambia inadvertidamente este valor.
2. Cortocircuitos
El cierre es un patrón de diseño importante en JavaScript que le permite organizar el trabajo protegido con variables. En el siguiente ejemplo, la función
createGreeter()
devuelve una función anónima que tiene acceso al argumento original proporcionado con el argumento de
greeting
que contiene la cadena
Hello
. Se escribe una referencia a esta función anónima en la variable
sayHello
. Después de eso, no importa cuántas veces llamemos a la función
sayHello()
, siempre tendrá acceso al valor de
greeting
. En este caso, el acceso al
greeting
solo será una función anónima, un enlace que se registra en
sayHello
.
function createGreeter(greeting) { return function(name) { console.log(greeting + ', ' + name); } } const sayHello = createGreeter('Hello'); sayHello('Joe');
Este fue un ejemplo muy simple. Si miramos algo más cercano al mundo real, podemos imaginar, por ejemplo, una función para conectarse a una determinada API (llamémosla
apiConnect()
), que, cuando se llama por primera vez, se le pasa una clave de acceso a la API. Esta función, a su vez, devuelve un objeto que contiene varios métodos que usan la clave de acceso a la API que se pasó a
apiConnect()
. En este caso, la clave se almacena en el cierre y cuando llama a estos métodos, ya no es necesario mencionarla.
function apiConnect(apiKey) { function get(route) { return fetch(`${route}?key=${apiKey}`); } function post(route, params) { return fetch(route, { method: 'POST', body: JSON.stringify(params), headers: { 'Authorization': `Bearer ${apiKey}` } }) } return { get, post } } const api = apiConnect('my-secret-key');
3. Asignación destructiva
Si aún no ha utilizado la asignación destructiva en JavaScript, entonces es hora de arreglarlo. La asignación destructiva es una forma común de recuperar propiedades de objetos utilizando una construcción de lenguaje sintáctico ordenado.
const obj = { name: 'Joe', food: 'cake' } const { name, food } = obj; console.log(name, food);
Si necesita asignar los nombres de propiedades extraídos que son diferentes de los que tienen en el objeto, puede hacer esto:
const obj = { name: 'Joe', food: 'cake' } const { name: myName, food: myFood } = obj; console.log(myName, myFood);
En el siguiente ejemplo, la desestructuración se utiliza para pasar con precisión los valores almacenados en las propiedades del objeto
person
a la función de
introduce()
. Este es un ejemplo de cómo se usa esta construcción cuando se declara una función para recuperar datos de un objeto con parámetros que se le pasan. Por cierto, si estás familiarizado con React, entonces probablemente ya lo hayas visto.
const person = { name: 'Eddie', age: 24 } function introduce({ name, age }) { console.log(`I'm ${name} and I'm ${age} years old!`); } console.log(introduce(person));
4. El operador de propagación
El operador de propagación es una construcción bastante simple que puede parecer incomprensible para una persona no preparada. El siguiente ejemplo tiene una matriz numérica, el valor máximo almacenado en el que necesitamos encontrar. Queremos usar el método
Math.max()
para esto, pero no sabe cómo trabajar con matrices. Él, como argumentos, asume valores numéricos independientes. Para extraer sus elementos de la matriz, utilizamos el operador de propagación, que se parece a tres puntos.
const arr = [4, 6, -1, 3, 10, 4]; const max = Math.max(...arr); console.log(max);
5. La declaración de descanso
El operador rest le permite convertir cualquier número de argumentos pasados a una función en una matriz.
function myFunc(...args) { console.log(args[0] + args[1]); } myFunc(1, 2, 3, 4);
6. Métodos de matriz
Los métodos de matriz a menudo brindan al desarrollador herramientas convenientes para resolver bellamente una variedad de tareas de conversión de datos. A veces respondo preguntas en StackOverflow. Entre ellos, a menudo hay aquellos que se dedican a algo como esas u otras formas de trabajar con matrices de objetos. Es en tales situaciones que los métodos de matriz son especialmente útiles.
Aquí consideraremos varios de estos métodos, unidos por el principio de su similitud entre sí. Cabe señalar que aquí no le contaré sobre todos los métodos de matrices. Puede encontrar su lista completa en
MDN (por cierto, esta es mi referencia de JavaScript favorita).
Métodos apMap (), filter () y reduce ()
Los métodos de matriz
map()
,
filter()
y
reduce()
permiten transformar matrices o reducir matrices a un solo valor (que puede ser un objeto).
El método
map()
devuelve una nueva matriz que contiene los valores transformados de la matriz procesada. La forma exacta en que se transformarán se especifica en la función que se pasa a este método.
const arr = [1, 2, 3, 4, 5, 6]; const mapped = arr.map(el => el + 20); console.log(mapped);
El método
filter()
devuelve una matriz de elementos, verificando los valores de los cuales la función pasada a este método devuelve
true
.
const arr = [1, 2, 3, 4, 5, 6]; const filtered = arr.filter(el => el === 2 || el === 4); console.log(filtered);
El método
reduce()
devuelve un cierto valor, que es el resultado del procesamiento de todos los elementos de la matriz.
const arr = [1, 2, 3, 4, 5, 6]; const reduced = arr.reduce((total, current) => total + current); console.log(reduced);
▍ Métodos find (), findIndex () e indexOf ()
Los métodos de matriz
find()
,
findIndex()
e
indexOf()
fáciles de confundir entre sí. Las siguientes son explicaciones para ayudarlo a comprender sus características.
El método
find()
devuelve el primer elemento de la matriz que coincide con los criterios especificados. Este método, al encontrar el primer elemento adecuado, no continúa la búsqueda en la matriz.
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; const found = arr.find(el => el > 5); console.log(found);
Tenga en cuenta que en nuestro ejemplo, los criterios dados corresponden a todos los elementos de la matriz que siguen al que contiene el número 5, pero solo se devuelve el primer elemento adecuado. Este método es muy útil en situaciones en las que, al usar los bucles for para enumerar y analizar matrices, dichos bucles se interrumpen cuando se encuentra el elemento deseado en la matriz, utilizando la instrucción
break
.
El método
findIndex()
es muy similar a
find()
, pero en lugar de devolver el primer elemento adecuado en la matriz, devuelve el índice de ese elemento. Para comprender mejor este método, eche un vistazo al siguiente ejemplo, que utiliza una matriz de valores de cadena.
const arr = ['Nick', 'Frank', 'Joe', 'Frank']; const foundIndex = arr.findIndex(el => el === 'Frank'); console.log(foundIndex);
El método
indexOf()
es muy similar al método
findIndex()
, pero toma como argumento no una función, sino un valor normal. Se puede usar si no se necesita una lógica compleja al buscar el elemento de matriz deseado.
const arr = ['Nick', 'Frank', 'Joe', 'Frank']; const foundIndex = arr.indexOf('Frank'); console.log(foundIndex);
▍ Métodos Push (), pop (), shift () y unshift ()
Los
unshift()
push()
,
pop()
,
shift()
y
unshift()
se utilizan para agregar nuevos elementos a las matrices y extraer elementos que ya existen en ellas. En este caso, el trabajo se realiza con elementos ubicados al principio o al final de la matriz.
El método
push()
permite agregar elementos al final de una matriz. Modifica la matriz y, al finalizar, devuelve el elemento agregado a la matriz.
let arr = [1, 2, 3, 4]; const pushed = arr.push(5); console.log(arr);
El método
pop()
elimina el último elemento de la matriz. Modifica la matriz y devuelve el elemento eliminado de ella.
let arr = [1, 2, 3, 4]; const popped = arr.pop(); console.log(arr);
El método
shift()
elimina el primer elemento de la matriz y lo devuelve. También modifica la matriz para la que se llama.
let arr = [1, 2, 3, 4]; const shifted = arr.shift(); console.log(arr);
El método
unshift()
agrega uno o más elementos al comienzo de una matriz. Él, nuevamente, modifica la matriz. Al mismo tiempo, a diferencia de los otros tres métodos discutidos aquí, devuelve la nueva longitud de la matriz.
let arr = [1, 2, 3, 4]; const unshifted = arr.unshift(5, 6, 7); console.log(arr);
Métodos liceSlice () y splice ()
Estos métodos se utilizan para modificar la matriz o para devolver alguna parte de la matriz.
El método
splice()
cambia el contenido de una matriz eliminando elementos existentes o reemplazándolos con otros elementos. Es capaz de agregar nuevos elementos a la matriz. Este método modifica la matriz.
El siguiente ejemplo, si lo describe en un lenguaje ordinario, se ve así: necesita, en la posición de matriz
1
, eliminar
0
elementos y agregar un elemento que contenga
b
.
let arr = ['a', 'c', 'd', 'e']; arr.splice(1, 0, 'b')
El método
slice()
devuelve una copia superficial de la matriz que contiene sus elementos, comenzando desde la posición inicial dada y terminando con la posición que precede a la posición final dada. Si, al llamarlo, solo se especifica la posición inicial, entonces devolverá toda la matriz, comenzando desde esta posición. Este método no modifica la matriz. Solo devuelve la parte de esta matriz descrita cuando se llamó.
let arr = ['a', 'b', 'c', 'd', 'e']; const sliced = arr.slice(2, 4); console.log(sliced);
▍ Método sort ()
El método
sort()
clasifica la matriz de acuerdo con la condición especificada por la función que se le pasa. Esta función toma dos elementos de la matriz (por ejemplo, se pueden representar como parámetros
a
y
b
) y, comparándolos, devuelve, si los elementos no necesitan intercambiarse, 0 si necesita colocarse en un índice más bajo que
b
es un número negativo, y si
b
necesita ponerse en un índice más bajo que
a
es un número positivo.
let arr = [1, 7, 3, -1, 5, 7, 2]; const sorter = (firstEl, secondEl) => firstEl - secondEl; arr.sort(sorter); console.log(arr);
Si no puede recordar estos métodos por primera vez, recordarlos está bien. Lo más importante es que ahora sabe lo que pueden hacer los métodos de matriz estándar. Por lo tanto, si no puede recordar de inmediato las características de un método en particular, lo que sepa sobre él le permitirá encontrar rápidamente lo que necesita en la documentación.
7. generadores
Los generadores de JavaScript se declaran con un asterisco. Le permiten especificar qué valor se devolverá la próxima vez que se llame al método
next()
. Los generadores pueden diseñarse para devolver un número limitado de valores. Si dicho generador devolvió todos esos valores, la siguiente llamada a
next()
regresará
undefined
. También puede crear generadores diseñados para devolver un número ilimitado de valores mediante ciclos.
Aquí hay un generador diseñado para devolver un número limitado de valores:
function* greeter() { yield 'Hi'; yield 'How are you?'; yield 'Bye'; } const greet = greeter(); console.log(greet.next().value);
Y aquí hay un generador diseñado para devolver un número infinito de valores a través de un bucle.
function* idCreator() { let i = 0; while (true) yield i++; } const ids = idCreator(); console.log(ids.next().value);
8. Operadores para verificar la igualdad (==) y la igualdad estricta (===) de valores
Es extremadamente importante para cualquier desarrollador de JS comprender la diferencia entre los operadores de igualdad (
==
) e igualdad estricta (
===
). El hecho es que el operador
==
, antes de comparar los valores, realiza la conversión de sus tipos (lo que puede conducir a extrañas, a primera vista, consecuencias), y el operador
===
no realiza la conversión de tipos.
console.log(0 == '0');
9. Comparación de objetos
De vez en cuando tengo que ver cómo los recién llegados a la programación JS cometen el mismo error. Intentan comparar objetos directamente. Las variables en las que los objetos están "almacenados" contienen referencias a ellos, y no estos objetos en sí.
Entonces, por ejemplo, en el siguiente ejemplo, los objetos se ven iguales, pero cuando se comparan directamente, se nos informa que los objetos son diferentes, ya que cada una de las variables contiene un enlace a su propio objeto y estos enlaces no son iguales entre sí.
const joe1 = { name: 'Joe' }; const joe2 = { name: 'Joe' }; console.log(joe1 === joe2);
Además, en el siguiente ejemplo, resulta que
joe1
es igual a
joe2
ya que ambas variables almacenan una referencia al mismo objeto.
const joe1 = { name: 'Joe' }; const joe2 = joe1; console.log(joe1 === joe2);
Uno de los métodos de comparación de objetos reales es su conversión preliminar al formato de cadena JSON. Es cierto que este enfoque tiene un problema, que es que en la representación de cadena obtenida del objeto no se garantiza un cierto orden de sus propiedades. Una forma más confiable de comparar objetos es usar una biblioteca especial que contenga herramientas para una comparación profunda de objetos (por ejemplo, este es el método
isEqual () de la biblioteca
lodash ).
Para comprender mejor las complejidades de comparar objetos y comprender las posibles consecuencias de escribir enlaces a los mismos objetos en diferentes variables, eche un vistazo al primer concepto JS discutido en este artículo.
10. Funciones de devolución de llamada
Las funciones de devolución de llamada son un concepto de JavaScript bastante simple con el que los novatos a veces tienen dificultades. Considere el siguiente ejemplo. Aquí, la función
console.log
(así como así, sin paréntesis) se pasa a
myFunc()
como una función de devolución de llamada. Esta función establece un temporizador, después del cual se llama a
myFunc()
y la cadena que se pasa a
myFunc()
se muestra en la consola.
function myFunc(text, callback) { setTimeout(function() { callback(text); }, 2000); } myFunc('Hello world!', console.log);
11. Promesas
Después de dominar las funciones de devolución de llamada y comenzar a usarlas en todas partes, pronto se encontrará en el llamado "infierno de devolución de llamada". Si realmente estás allí, mira las promesas. El código asincrónico se puede incluir en una promesa y, después de su ejecución exitosa, informar al sistema sobre la resolución exitosa de la promesa llamando al método apropiado y, si algo sale mal, llame al método que indica esto y rechace la promesa. Para procesar los resultados devueltos por la promesa, use el método
then()
, y para el manejo de errores, use el método
catch()
.
const myPromise = new Promise(function(res, rej) { setTimeout(function(){ if (Math.random() < 0.9) { return res('Hooray!'); } return rej('Oh no!'); }, 1000); }); myPromise .then(function(data) { console.log('Success: ' + data); }) .catch(function(err) { console.log('Error: ' + err); });
12. construcción asíncrona / espera
Después de trabajar con las promesas, entonces, posiblemente, querrás algo más. Por ejemplo, domine la construcción asíncrona / espera. Es azúcar sintáctico para promesas. En el siguiente ejemplo, creamos, usando la
async
, una función asincrónica, y en ella, usando la palabra clave wait, organizamos la espera para
greeter
bienvenida.
const greeter = new Promise((res, rej) => { setTimeout(() => res('Hello world!'), 2000); }) async function myFunc() { const greeting = await greeter; console.log(greeting); } myFunc();
Resumen
Si lo que hablamos aquí anteriormente no le era familiar, lo más probable es que, al menos un poco, haya crecido por encima de usted al leer este artículo. Si no ha encontrado nada nuevo aquí, me gustaría esperar que este material le brinde la oportunidad de practicar y fortalecer su conocimiento de JavaScript.
Estimados lectores! ¿Qué otros conceptos de JavaScript agregarías a este artículo?
