JavaScript

Explica la delegación de eventos

La delegación de eventos es una técnica que consiste en añadir oyentes de eventos a un elemento padre en lugar de añadirlos a los elementos descendientes. El oyente se activará cada vez que el evento se dispare en los elementos descendientes debido al burbujeo de eventos en el DOM. Los beneficios de esta técnica son:

  • Se reduce el consumo de memoria porque solo se necesita un único manejador en el elemento padre, en lugar de tener que adjuntar manejadores de eventos en cada descendiente.
  • No es necesario desvincular el manejador de los elementos que se eliminan y vincular el evento para los nuevos elementos.

Explica cómo funciona `this` en JavaScript

No hay una explicación sencilla para this; es uno de los conceptos más confusos en JavaScript. Una explicación aproximada es que el valor de this depende de cómo se llama la función. He leído muchas explicaciones sobre this en línea, y encontré que la explicación de [Arnav Aggrawal] es la más clara. Se aplican las siguientes reglas:

  1. Si se usa la palabra clave new al llamar a la función, this dentro de la función es un objeto completamente nuevo.
  2. Si se usan apply, call o bind para llamar/crear una función, this dentro de la función es el objeto que se pasa como argumento.
  3. Si una función se llama como un método, como obj.method(), this es el objeto del que la función es una propiedad.
  4. Si una función se invoca como una invocación de función libre, lo que significa que se invocó sin ninguna de las condiciones anteriores, this es el objeto global. En un navegador, es el objeto window. Si está en modo estricto ('use strict'), this será undefined en lugar del objeto global.
  5. Si se aplican varias de las reglas anteriores, la regla que está más arriba gana y establecerá el valor de this.
  6. Si la función es una función de flecha ES2015, ignora todas las reglas anteriores y recibe el valor this de su ámbito circundante en el momento en que se crea.

Para una explicación en profundidad, consulte su [artículo en Medium].

¿Puede dar un ejemplo de una de las formas en que el trabajo con this ha cambiado en ES6?

ES6 permite usar [funciones de flecha] que usan el [ámbito léxico envolvente]. Esto suele ser conveniente, pero impide que el llamador controle el contexto a través de .call o .apply, lo que tiene como consecuencia que una biblioteca como jQuery no vincule correctamente this en sus funciones de controlador de eventos. Por lo tanto, es importante tener esto en cuenta al refactorizar grandes aplicaciones heredadas.

Explica cómo funciona la herencia prototípica

Todos los objetos JavaScript tienen una propiedad __proto__, con la excepción de los objetos creados con Object.create(null), que es una referencia a otro objeto, que se denomina el "prototipo" del objeto. Cuando se accede a una propiedad en un objeto y la propiedad no se encuentra en ese objeto, el motor JavaScript busca en el __proto__ del objeto, y en el __proto__ del __proto__, y así sucesivamente, hasta que encuentra la propiedad definida en uno de los __proto__s o hasta que llega al final de la cadena de prototipos. Este comportamiento simula la herencia clásica, pero en realidad es más bien [delegación que herencia].

Ejemplo de Herencia Prototípica

// Constructor del objeto padre. function Animal(name) { this.name = name; } // Añadir un método al prototipo del objeto padre. Animal.prototype.makeSound = function () { console.log('El ' + this.constructor.name + ' hace un sonido.'); }; // Constructor del objeto hijo. function Dog(name) { Animal.call(this, name); // Llamar al constructor padre. } // Establecer el prototipo del objeto hijo como el prototipo del padre. Object.setPrototypeOf(Dog.prototype, Animal.prototype); // Añadir un método al prototipo del objeto hijo. Dog.prototype.bark = function () { console.log('¡Guau!'); }; // Crear una nueva instancia de Dog. const bolt = new Dog('Bolt'); // Llamar a métodos del objeto hijo. console.log(bolt.name); // "Bolt" bolt.makeSound(); // "El Dog hace un sonido." bolt.bark(); // "¡Guau!"

Cosas a tener en cuenta:

  • .makeSound no está definido en Dog, por lo que el motor sube por la cadena de prototipos y encuentra .makeSound del Animal heredado.
  • El uso de Object.create para construir la cadena de herencia ya no se recomienda. Use Object.setPrototypeOf en su lugar.

¿Qué opina de AMD vs CommonJS?

Ambos son formas de implementar un sistema de módulos, que no estaba presente de forma nativa en JavaScript hasta que llegó ES2015. CommonJS es síncrono mientras que AMD (Asynchronous Module Definition) es obviamente asíncrono. CommonJS está diseñado pensando en el desarrollo del lado del servidor, mientras que AMD, con su soporte para la carga asíncrona de módulos, está más destinado a los navegadores.

Encuentro la sintaxis de AMD bastante verbosa y CommonJS está más cerca del estilo en el que escribiría declaraciones de importación en otros lenguajes. La mayoría de las veces, encuentro que AMD es innecesario, porque si sirviera todo su JavaScript en un archivo de paquete concatenado, no se beneficiaría de las propiedades de carga asíncrona. Además, la sintaxis de CommonJS está más cerca del estilo Node de escribir módulos y hay menos sobrecarga de cambio de contexto al cambiar entre el desarrollo de JavaScript del lado del cliente y del lado del servidor.

Me alegro de que con los módulos ES2015, que tienen soporte para carga síncrona y asíncrona, finalmente podamos ceñirnos a un solo enfoque. Aunque aún no se ha implementado por completo en los navegadores y en Node, siempre podemos usar transpiladores para convertir nuestro código.

Explica por qué lo siguiente no funciona como una IIFE: `function foo(){ }();`. ¿Qué hay que cambiar para que sea una IIFE correctamente?

IIFE significa Expresiones de Función Invocadas Inmediatamente (Immediately Invoked Function Expressions). El analizador de JavaScript lee function foo(){ }(); como function foo(){ } y ();, donde lo primero es una declaración de función y lo último (un par de paréntesis) es un intento de llamar a una función pero no se especifica ningún nombre, por lo que arroja Uncaught SyntaxError: Unexpected token ).

Aquí hay dos formas de solucionarlo que implican agregar más paréntesis: (function foo(){ })() y (function foo(){ }()). Las sentencias que comienzan con function se consideran declaraciones de función; al envolver esta función entre (), se convierte en una expresión de función que luego se puede ejecutar con los () posteriores. Estas funciones no se exponen en el ámbito global e incluso puede omitir su nombre si no necesita hacer referencia a sí misma dentro del cuerpo.

También puede usar el operador void: void function foo(){ }();. Desafortunadamente, hay un problema con este enfoque. La evaluación de la expresión dada siempre es undefined, por lo que si su función IIFE devuelve algo, no puede usarlo. Un ejemplo:

const foo = void (function bar() { return 'foo'; })(); console.log(foo); // undefined

¿Cuál es la diferencia entre una variable que es: `null`, `undefined` o no declarada? ¿Cómo comprobaría cualquiera de estos estados?

Las variables no declaradas se crean cuando se asigna un valor a un identificador que no se ha creado previamente usando var, let o const. Las variables no declaradas se definirán globalmente, fuera del ámbito actual. En modo estricto, se lanzará un ReferenceError cuando intente asignar a una variable no declarada. Las variables no declaradas son malas al igual que las variables globales son malas. ¡Evítelas a toda costa! Para comprobarlas, envuelva su uso en un bloque try/catch.

function foo() { x = 1; // Lanza un ReferenceError en modo estricto } foo(); console.log(x); // 1

Una variable que es undefined es una variable que ha sido declarada, pero no se le ha asignado un valor. Es de tipo undefined. Si una función no devuelve ningún valor como resultado de su ejecución y se asigna a una variable, la variable también tiene el valor de undefined. Para comprobarlo, compare usando el operador de igualdad estricta (===) o typeof, que dará la cadena 'undefined'. Tenga en cuenta que no debe usar el operador de igualdad abstracta para comprobarlo, ya que también devolverá true si el valor es null.

var foo; console.log(foo); // undefined console.log(foo === undefined); // true console.log(typeof foo === 'undefined'); // true console.log(foo == null); // true. Incorrecto, ¡no use esto para comprobar! function bar() {} var baz = bar(); console.log(baz); // undefined

Una variable que es null se le habrá asignado explícitamente el valor null. Representa ningún valor y es diferente de undefined en el sentido de que ha sido asignada explícitamente. Para comprobar null, simplemente compare usando el operador de igualdad estricta. Tenga en cuenta que, al igual que lo anterior, no debe usar el operador de igualdad abstracta (==) para comprobar, ya que también devolverá true si el valor es undefined.

var foo = null; console.log(foo === null); // true console.log(typeof foo === 'object'); // true console.log(foo == undefined); // true. Incorrecto, ¡no use esto para comprobar!

Como hábito personal, nunca dejo mis variables no declaradas o sin asignar. Les asignaré explícitamente null después de declararlas si aún no tengo la intención de usarlas. Si utiliza un linter en su flujo de trabajo, normalmente también podrá verificar que no está haciendo referencia a variables no declaradas.

¿Qué es un cierre (closure) y cómo/por qué lo usarías?

Un cierre es la combinación de una función y el entorno léxico dentro del cual se declaró esa función. La palabra "léxico" se refiere al hecho de que el alcance léxico utiliza la ubicación donde se declara una variable dentro del código fuente para determinar dónde está disponible esa variable. Los cierres son funciones que tienen acceso a las variables de la función externa (envolvente) — cadena de alcance incluso después de que la función externa haya retornado.

¿Por qué usarías uno?

  • Privacidad de datos / emulación de métodos privados con cierres. Comúnmente utilizado en el [patrón de módulo].
  • [Aplicaciones parciales o currying].

¿Puede describir la principal diferencia entre un bucle `.forEach` y un bucle `.map()` y por qué elegiría uno u otro?

Para entender las diferencias entre ambos, veamos qué hace cada función.

forEach

  • Itera a través de los elementos de un array.
  • Ejecuta una función de callback para cada elemento.
  • No devuelve un valor.
const a = [1, 2, 3]; const doubled = a.forEach((num, index) => { // Haz algo con num y/o index. }); // doubled = undefined

map

  • Itera a través de los elementos de un array.
  • "Mapea" cada elemento a un nuevo elemento llamando a la función en cada elemento, creando un nuevo array como resultado.
const a = [1, 2, 3]; const doubled = a.map((num) => { return num * 2; }); // doubled = [2, 4, 6]

La principal diferencia entre .forEach y .map() es que .map() devuelve un nuevo array. Si necesita el resultado, pero no desea mutar el array original, .map() es la elección obvia. Si simplemente necesita iterar sobre un array, forEach es una buena elección.

¿Cuál es un caso de uso típico para las funciones anónimas?

Se pueden usar en IIFE para encapsular algo de código dentro de un ámbito local para que las variables declaradas en él no se filtren al ámbito global.

(function () { // Algo de código aquí. })();

Como una función de devolución de llamada que se usa una vez y no necesita ser usada en ningún otro lugar. El código parecerá más autónomo y legible cuando los manejadores se definen justo dentro del código que los llama, en lugar de tener que buscar en otro lugar para encontrar el cuerpo de la función.

setTimeout(function () { console.log('¡Hola mundo!'); }, 1000);

Argumentos para construcciones de programación funcional o Lodash (similar a las devoluciones de llamada).

const arr = [1, 2, 3]; const double = arr.map(function (el) { return el * 2; }); console.log(double); // [2, 4, 6]

¿Cómo organiza su código? (patrón de módulo, herencia clásica?)

En el pasado, he usado Backbone para mis modelos, lo que fomenta un enfoque más orientado a objetos, creando modelos de Backbone y adjuntando métodos a ellos.

El patrón de módulo sigue siendo excelente, pero hoy en día, uso React/Redux que utiliza un flujo de datos unidireccional basado en la arquitectura Flux. Representaría los modelos de mi aplicación usando objetos simples y escribiría funciones puras de utilidad para manipular estos objetos. El estado se manipula usando acciones y reductores como en cualquier otra aplicación Redux.

Evito usar la herencia clásica siempre que sea posible. Cuando lo hago, me ciño a [estas reglas].

¿Cuál es la diferencia entre objetos host y objetos nativos?

Los objetos nativos son objetos que forman parte del lenguaje JavaScript definidos por la especificación ECMAScript, como String, Math, RegExp, Object, Function, etc.

Los objetos host son proporcionados por el entorno de ejecución (navegador o Node), como window, XMLHTTPRequest, etc.

Diferencia entre: `function Person(){}`, `var person = Person()`, y `var person = new Person()`?

Esta pregunta es bastante vaga. Mi mejor suposición de su intención es que pregunta sobre los constructores en JavaScript. Técnicamente hablando, function Person(){} es solo una declaración de función normal. La convención es usar PascalCase para las funciones que se pretenden usar como constructores.

var person = Person() invoca a Person como una función, y no como un constructor. Invocarla así es un error común si la función está destinada a ser usada como constructor. Típicamente, el constructor no devuelve nada, por lo tanto, invocar el constructor como una función normal devolverá undefined y eso se asignará a la variable destinada como la instancia.

var person = new Person() crea una instancia del objeto Person usando el operador new, que hereda de Person.prototype. Una alternativa sería usar Object.create, como: Object.create(Person.prototype).

function Person(name) { this.name = name; } var person = Person('John'); console.log(person); // undefined console.log(person.name); // Uncaught TypeError: Cannot read property 'name' of undefined var person = new Person('John'); console.log(person); // Person { name: "John" } console.log(person.name); // "john"

¿Cuál es la diferencia entre `.call` y `.apply`?

Tanto .call como .apply se utilizan para invocar funciones y el primer parámetro se usará como el valor de this dentro de la función. Sin embargo, .call toma argumentos separados por comas como los siguientes argumentos, mientras que .apply toma una matriz de argumentos como el siguiente argumento. Una forma fácil de recordar esto es C de call y separados por comas, y A de apply y una matriz de argumentos.

function add(a, b) { return a + b; } console.log(add.call(null, 1, 2)); // 3 console.log(add.apply(null, [1, 2])); // 3

Explique `Function.prototype.bind`.

Tomado palabra por palabra de [MDN]:

El método bind() crea una nueva función que, cuando se llama, tiene su palabra clave this establecida en el valor proporcionado, con una secuencia dada de argumentos que preceden a cualquier argumento proporcionado cuando se llama a la nueva función.

En mi experiencia, es más útil para vincular el valor de this en métodos de clases que desea pasar a otras funciones. Esto se hace con frecuencia en componentes de React.

¿Cuándo usaría `document.write()`?

document.write() escribe una cadena de texto en un flujo de documentos abierto por document.open(). Cuando document.write() se ejecuta después de que la página se ha cargado, llamará a document.open, lo que borrará todo el documento (¡se eliminarán <head> y <body>!) y reemplazará el contenido con el valor del parámetro dado. Por lo tanto, generalmente se considera peligroso y propenso a un uso indebido.

Hay algunas respuestas en línea que explican que document.write() se usa en código de análisis o [cuando se desea incluir estilos que solo deben funcionar si JavaScript está habilitado]. ¡Incluso se usa en HTML5 boilerplate para [cargar scripts en paralelo y preservar el orden de ejecución]! Sin embargo, sospecho que esas razones podrían estar desactualizadas y que hoy en día se pueden lograr sin usar document.write(). Por favor, corríjame si me equivoco en esto.

¿Cuál es la diferencia entre detección de características, inferencia de características y el uso de la cadena UA?

Detección de características

La detección de características implica averiguar si un navegador admite un cierto bloque de código y ejecutar un código diferente según si lo hace (o no), para que el navegador siempre pueda proporcionar una experiencia funcional en lugar de bloquearse/generar errores en algunos navegadores. Por ejemplo:

if ('geolocation' in navigator) { // Se puede usar navigator.geolocation } else { // Manejar la falta de la característica }

[Modernizr] es una excelente biblioteca para manejar la detección de características.

Inferencia de características

La inferencia de características comprueba una característica al igual que la detección de características, pero usa otra función porque asume que también existirá, por ejemplo:

if (document.getElementsByTagName) { element = document.getElementById(id); }

Esto no es realmente recomendable. La detección de características es más infalible.

Cadena UA

Es una cadena reportada por el navegador que permite a los pares del protocolo de red identificar el tipo de aplicación, sistema operativo, proveedor de software o versión de software del agente de usuario de software solicitante. Se puede acceder a través de navigator.userAgent. Sin embargo, la cadena es difícil de analizar y puede ser falsificada. Por ejemplo, Chrome se reporta como Chrome y Safari. Así que para detectar Safari, tienes que buscar la cadena Safari y la ausencia de la cadena Chrome. Evite este método.

Explica Ajax con el mayor detalle posible.

Ajax (JavaScript asíncrono y XML) es un conjunto de técnicas de desarrollo web que utilizan muchas tecnologías web del lado del cliente para crear aplicaciones web asíncronas. Con Ajax, las aplicaciones web pueden enviar y recuperar datos de un servidor de forma asíncrona (en segundo plano) sin interferir con la visualización y el comportamiento de la página existente. Al desacoplar la capa de intercambio de datos de la capa de presentación, Ajax permite que las páginas web, y por extensión las aplicaciones web, cambien el contenido dinámicamente sin necesidad de recargar toda la página. En la práctica, las implementaciones modernas suelen utilizar JSON en lugar de XML, debido a las ventajas de que JSON es nativo de JavaScript.

La API XMLHttpRequest se utiliza con frecuencia para la comunicación asíncrona o, hoy en día, la API fetch().

¿Cuáles son las ventajas y desventajas de usar Ajax?

Ventajas

  • Mejor interactividad. El nuevo contenido del servidor se puede cambiar dinámicamente sin necesidad de recargar toda la página.
  • Reduce las conexiones al servidor, ya que los scripts y las hojas de estilo solo deben solicitarse una vez.
  • El estado se puede mantener en una página. Las variables de JavaScript y el estado del DOM persistirán porque la página del contenedor principal no se recargó.
  • Básicamente, la mayoría de las ventajas de una SPA.

Desventajas

  • Las páginas web dinámicas son más difíciles de marcar como favoritas.
  • No funciona si JavaScript se ha deshabilitado en el navegador.
  • Algunos rastreadores web no ejecutan JavaScript y no verían el contenido que se ha cargado mediante JavaScript.
  • Las páginas web que utilizan Ajax para obtener datos probablemente tendrán que combinar los datos remotos obtenidos con plantillas del lado del cliente para actualizar el DOM. Para que esto suceda, JavaScript deberá ser analizado y ejecutado en el navegador, y los dispositivos móviles de gama baja podrían tener dificultades con esto.
  • Básicamente, la mayoría de las desventajas de una SPA.

Explique cómo funciona JSONP (y cómo no es realmente Ajax).

JSONP (JSON with Padding) es un método comúnmente utilizado para eludir las políticas de origen cruzado en los navegadores web, ya que las solicitudes Ajax desde la página actual a un dominio de origen cruzado no están permitidas.

JSONP funciona realizando una solicitud a un dominio de origen cruzado a través de una etiqueta <script> y, generalmente, con un parámetro de consulta callback, por ejemplo: https://example.com?callback=printData. El servidor luego envolverá los datos dentro de una función llamada printData y los devolverá al cliente.

<script> function printData(data) { console.log(`¡Mi nombre es ${data.name}!`); } </script> <script src="[https://example.com?callback=printData](https://example.com?callback=printData)"></script>
printData({ name: 'Yang Shun' });

El cliente debe tener la función printData en su ámbito global y la función será ejecutada por el cliente cuando se reciba la respuesta del dominio de origen cruzado.

JSONP puede ser inseguro y tiene algunas implicaciones de seguridad. Como JSONP es realmente JavaScript, puede hacer todo lo que JavaScript puede hacer, por lo que debe confiar en el proveedor de los datos JSONP.

Hoy en día, [CORS] es el enfoque recomendado y JSONP se considera una especie de truco.

¿Ha utilizado alguna vez plantillas JavaScript? Si es así, ¿qué bibliotecas ha utilizado?

Sí. Handlebars, Underscore, Lodash, AngularJS y JSX. No me gustaba el uso de plantillas en AngularJS porque hacía un uso intensivo de cadenas en las directivas y los errores tipográficos pasaban desapercibidos. JSX es mi nuevo favorito, ya que está más cerca de JavaScript y apenas hay sintaxis que aprender. Hoy en día, incluso se pueden usar literales de cadena de plantilla de ES2015 como una forma rápida de crear plantillas sin depender de código de terceros.

const template = `<div>Mi nombre es: ${name}</div>`;

Sin embargo, tenga en cuenta una posible XSS en el enfoque anterior, ya que el contenido no se escapa por usted, a diferencia de las bibliotecas de plantillas.

Explica el "hoisting".

Hoisting es un término utilizado para explicar el comportamiento de las declaraciones de variables en tu código. Las variables declaradas o inicializadas con la palabra clave var tendrán su declaración "movida" a la parte superior de su ámbito de módulo/función, lo que conocemos como hoisting. Sin embargo, solo se eleva la declaración; la asignación (si la hay) se mantiene donde está.

Ten en cuenta que la declaración no se mueve realmente: el motor JavaScript analiza las declaraciones durante la compilación y se da cuenta de las declaraciones y sus ámbitos. Simplemente es más fácil entender este comportamiento visualizando las declaraciones como si fueran elevadas a la parte superior de su ámbito. Expliquemos con algunos ejemplos.

console.log(foo); // undefined var foo = 1; console.log(foo); // 1

Las declaraciones de funciones tienen el cuerpo elevado, mientras que las expresiones de función (escritas en forma de declaraciones de variables) solo tienen la declaración de variable elevada.

// Declaración de función console.log(foo); // [Function: foo] foo(); // 'FOOOOO' function foo() { console.log('FOOOOO'); } console.log(foo); // [Function: foo] // Expresión de función console.log(bar); // undefined bar(); // Uncaught TypeError: bar is not a function var bar = function () { console.log('BARRRR'); }; console.log(bar); // [Function: bar]

Las variables declaradas mediante let y const también se elevan. Sin embargo, a diferencia de var y function, no se inicializan y acceder a ellas antes de la declaración resultará en una excepción ReferenceError. La variable se encuentra en una "zona muerta temporal" desde el inicio del bloque hasta que se procesa la declaración.

Describe el burbujeo de eventos (event bubbling).

Cuando un evento se dispara en un elemento del DOM, intentará manejar el evento si hay un oyente adjunto, luego el evento se propaga a su padre y sucede lo mismo. Este burbujeo ocurre a través de los ancestros del elemento hasta el document. El burbujeo de eventos es el mecanismo detrás de la delegación de eventos.

¿Cuál es la diferencia entre un "atributo" y una "propiedad"?

Los atributos se definen en el marcado HTML, pero las propiedades se definen en el DOM. Para ilustrar la diferencia, imagine que tenemos este campo de texto en nuestro HTML: <input type="text" value="Hello">.

const input = document.querySelector('input'); console.log(input.getAttribute('value')); // Hello console.log(input.value); // Hello

Pero después de cambiar el valor del campo de texto añadiéndole "World!", esto se convierte en:

console.log(input.getAttribute('value')); // Hello console.log(input.value); // Hello World!

¿Por qué no es una buena idea extender objetos JavaScript incorporados?

Extender un objeto JavaScript incorporado/nativo significa agregar propiedades/funciones a su prototype. Si bien esto puede parecer una buena idea al principio, es peligroso en la práctica. Imagine que su código utiliza algunas bibliotecas que extienden Array.prototype agregando el mismo método contains; las implementaciones se sobrescribirán entre sí y su código se romperá si el comportamiento de estos dos métodos no es el mismo.

La única vez que es posible que desee extender un objeto nativo es cuando desea crear un polyfill, esencialmente proporcionando su propia implementación para un método que forma parte de la especificación de JavaScript pero que podría no existir en el navegador del usuario debido a que es un navegador más antiguo.

¿Diferencia entre el evento `load` del documento y el evento `DOMContentLoaded` del documento?

El evento DOMContentLoaded se dispara cuando el documento HTML inicial se ha cargado y analizado por completo, sin esperar a que las hojas de estilo, las imágenes y los subframes terminen de cargarse.

El evento load de window solo se dispara después de que el DOM y todos los recursos y activos dependientes se hayan cargado.

¿Cuál es la diferencia entre `==` y `===`?

== es el operador de igualdad abstracta, mientras que === es el operador de igualdad estricta. El operador == comparará la igualdad después de realizar las conversiones de tipo necesarias. El operador === no realizará conversiones de tipo, por lo que si dos valores no son del mismo tipo, === simplemente devolverá false. Al usar ==, pueden ocurrir cosas extrañas, como:

1 == '1'; // true 1 == [1]; // true 1 == true; // true 0 == ''; // true 0 == '0'; // true 0 == false; // true

Mi consejo es nunca usar el operador ==, excepto por conveniencia al comparar con null o undefined, donde a == null devolverá true si a es null o undefined.

var a = null; console.log(a == null); // true console.log(a == undefined); // true

Explica la política de mismo origen con respecto a JavaScript.

La política de mismo origen impide que JavaScript realice solicitudes a través de los límites de dominio. Un origen se define como una combinación de esquema URI, nombre de host y número de puerto. Esta política evita que un script malicioso en una página obtenga acceso a datos sensibles en otra página web a través del Modelo de Objeto de Documento de esa página.

Haz que esto funcione:

duplicate([1, 2, 3, 4, 5]); // [1,2,3,4,5,1,2,3,4,5]
function duplicate(arr) { return arr.concat(arr); } duplicate([1, 2, 3, 4, 5]); // [1,2,3,4,5,1,2,3,4,5]

O con ES6:

const duplicate = (arr) => [...arr, ...arr]; duplicate([1, 2, 3, 4, 5]); // [1,2,3,4,5,1,2,3,4,5]

¿Por qué se llama expresión Ternaria, qué indica la palabra "Ternaria"?

"Ternario" indica tres, y una expresión ternaria acepta tres operandos: la condición de prueba, la expresión "entonces" y la expresión "sino". Las expresiones ternarias no son específicas de JavaScript y no estoy seguro de por qué está en esta lista.

¿Qué es `"use strict";`? ¿Cuáles son las ventajas y desventajas de usarlo?

'use strict' es una declaración utilizada para habilitar el modo estricto en scripts completos o funciones individuales. El modo estricto es una forma de optar por una variante restringida de JavaScript.

Ventajas:

  • Hace imposible crear variables globales accidentalmente.
  • Hace que las asignaciones que de otro modo fallarían silenciosamente lancen una excepción.
  • Hace que los intentos de eliminar propiedades no eliminables lancen una excepción (donde antes el intento simplemente no tendría efecto).
  • Requiere que los nombres de los parámetros de la función sean únicos.
  • this es undefined en el contexto global.
  • Detecta algunos errores de codificación comunes, lanzando excepciones.
  • Deshabilita características confusas o mal pensadas.

Desventajas:

  • Muchas características faltantes a las que algunos desarrolladores podrían estar acostumbrados.
  • Ya no hay acceso a function.caller y function.arguments.
  • La concatenación de scripts escritos en diferentes modos estrictos podría causar problemas.

En general, creo que los beneficios superan las desventajas, y nunca he tenido que depender de las características que el modo estricto bloquea. Recomendaría usar el modo estricto.

Cree un bucle `for` que itere hasta `100` mientras muestra **"fizz"** en múltiplos de `3`, **"buzz"** en múltiplos de `5` y **"fizzbuzz"** en múltiplos de `3` y `5`.

Echa un vistazo a esta versión de FizzBuzz de [Paul Irish].

for (let i = 1; i <= 100; i++) { let f = i % 3 == 0, b = i % 5 == 0; console.log(f ? (b ? 'FizzBuzz' : 'Fizz') : b ? 'Buzz' : i); }

Sin embargo, no le aconsejaría que escribiera lo anterior durante las entrevistas. Simplemente quédese con el enfoque largo pero claro. Para versiones más extravagantes de FizzBuzz, consulte el enlace de referencia a continuación.

¿Por qué es, en general, una buena idea dejar el ámbito global de un sitio web tal cual y nunca tocarlo?

Cada script tiene acceso al ámbito global, y si todos usan el espacio de nombres global para definir sus variables, es probable que ocurran colisiones. Use el patrón de módulo (IIFE) para encapsular sus variables dentro de un espacio de nombres local.

¿Por qué usaría algo como el evento `load`? ¿Este evento tiene desventajas? ¿Conoce alguna alternativa y por qué las usaría?

El evento load se dispara al final del proceso de carga del documento. En este punto, todos los objetos del documento están en el DOM, y todas las imágenes, scripts, enlaces y subframes han terminado de cargarse.

El evento DOMContentLoaded del DOM se disparará después de que se haya construido el DOM de la página, pero no esperará a que terminen de cargarse otros recursos. Esto se prefiere en ciertos casos cuando no necesita que la página completa se cargue antes de la inicialización.

Explica qué es una aplicación de una sola página y cómo hacerla compatible con SEO.

Lo siguiente está tomado de la excelente [Guía de Front End de Grab], que, casualmente, ¡está escrita por mí!

Los desarrolladores web de hoy en día se refieren a los productos que construyen como aplicaciones web, en lugar de sitios web. Si bien no hay una diferencia estricta entre los dos términos, las aplicaciones web tienden a ser altamente interactivas y dinámicas, permitiendo al usuario realizar acciones y recibir una respuesta a su acción. Tradicionalmente, el navegador recibe HTML del servidor y lo renderiza. Cuando el usuario navega a otra URL, se requiere una actualización completa de la página y el servidor envía HTML nuevo a la nueva página. Esto se llama renderizado del lado del servidor.

Sin embargo, en las SPA modernas, se utiliza en su lugar el renderizado del lado del cliente. El navegador carga la página inicial del servidor, junto con los scripts (frameworks, bibliotecas, código de la aplicación) y las hojas de estilo necesarias para toda la aplicación. Cuando el usuario navega a otras páginas, no se activa una actualización de la página. La URL de la página se actualiza a través de la [API de historial de HTML5]. Los nuevos datos necesarios para la nueva página, generalmente en formato JSON, son recuperados por el navegador a través de solicitudes [AJAX] al servidor. Luego, la SPA actualiza dinámicamente la página con los datos a través de JavaScript, que ya se descargó en la carga inicial de la página. Este modelo es similar a cómo funcionan las aplicaciones móviles nativas.

Los beneficios:

  • La aplicación se siente más receptiva y los usuarios no ven el parpadeo entre las navegaciones de página debido a las actualizaciones completas de la página.
  • Se realizan menos solicitudes HTTP al servidor, ya que los mismos activos no tienen que descargarse de nuevo para cada carga de página.
  • Clara separación de las preocupaciones entre el cliente y el servidor; se pueden construir fácilmente nuevos clientes para diferentes plataformas (por ejemplo, móviles, chatbots, relojes inteligentes) sin tener que modificar el código del servidor. También se puede modificar la stack tecnológica en el cliente y el servidor de forma independiente, siempre que no se rompa el contrato de la API.

Las desventajas:

  • Mayor carga inicial de la página debido a la carga del framework, el código de la aplicación y los activos necesarios para varias páginas.
  • Hay un paso adicional que debe realizarse en su servidor, que es configurarlo para que enrute todas las solicitudes a un único punto de entrada y permita que el enrutamiento del lado del cliente se encargue a partir de ahí.
  • Las SPA dependen de JavaScript para renderizar contenido, pero no todos los motores de búsqueda ejecutan JavaScript durante el rastreo, y pueden ver contenido vacío en su página. Esto perjudica inadvertidamente la optimización de motores de búsqueda (SEO) de su aplicación. Sin embargo, la mayoría de las veces, cuando se están construyendo aplicaciones, el SEO no es el factor más importante, ya que no todo el contenido necesita ser indexable por los motores de búsqueda. Para superar esto, se puede renderizar la aplicación del lado del servidor o usar servicios como [Prerender] para "renderizar su JavaScript en un navegador, guardar el HTML estático y devolverlo a los rastreadores".

¿Cuál es la extensión de su experiencia con Promesas y/o sus _polyfills_?

Poseo conocimientos prácticos de ello. Una promesa es un objeto que puede producir un único valor en algún momento en el futuro: ya sea un valor resuelto o una razón por la que no se resolvió (por ejemplo, ocurrió un error de red). Una promesa puede estar en uno de 3 estados posibles: cumplida, rechazada o pendiente. Los usuarios de promesas pueden adjuntar callbacks para manejar el valor cumplido o la razón del rechazo.

Algunos polyfills comunes son $.deferred, Q y Bluebird, pero no todos cumplen con la especificación. ES2015 admite promesas de forma nativa y los polyfills normalmente no son necesarios hoy en día.

¿Cuáles son las ventajas y desventajas de usar Promesas en lugar de _callbacks_?

Ventajas

  • Evita el infierno de los callbacks, que puede ser ilegible.
  • Facilita la escritura de código asíncrono secuencial que es legible con .then().
  • Facilita la escritura de código asíncrono paralelo con Promise.all().
  • Con las promesas, estos escenarios presentes en la codificación solo con callbacks no ocurrirán:
    • Llamar al callback demasiado pronto
    • Llamar al callback demasiado tarde (o nunca)
    • Llamar al callback muy pocas o demasiadas veces
    • No pasar ningún entorno/parámetros necesarios
    • Tragar cualquier error/excepción que pueda ocurrir

Desventajas

  • Código ligeramente más complejo (discutible).
  • En navegadores antiguos donde ES2015 no es compatible, es necesario cargar un polyfill para poder usarlo.

¿Cuáles son algunas de las ventajas/desventajas de escribir código JavaScript en un lenguaje que compila a JavaScript?

Algunos ejemplos de lenguajes que compilan a JavaScript incluyen CoffeeScript, Elm, ClojureScript, PureScript y TypeScript.

Ventajas:

  • Soluciona algunos de los problemas de larga data en JavaScript y desaconseja los anti-patrones de JavaScript.
  • Le permite escribir código más corto, al proporcionar algo de azúcar sintáctico además de JavaScript, que creo que le falta a ES5, pero ES2015 es increíble.
  • Los tipos estáticos son increíbles (en el caso de TypeScript) para proyectos grandes que necesitan mantenerse con el tiempo.

Desventajas:

  • Requiere un proceso de construcción/compilación, ya que los navegadores solo ejecutan JavaScript y su código deberá compilarse a JavaScript antes de ser servido a los navegadores.
  • La depuración puede ser un problema si sus source maps no se asignan bien a su código fuente precompilado.
  • La mayoría de los desarrolladores no están familiarizados con estos lenguajes y deberán aprenderlos. Hay un costo inicial involucrado para su equipo si lo usa para sus proyectos.
  • Comunidad más pequeña (depende del lenguaje), lo que significa que los recursos, tutoriales, bibliotecas y herramientas serían más difíciles de encontrar.
  • El soporte IDE/editor podría ser deficiente.
  • Estos lenguajes siempre estarán por detrás del último estándar de JavaScript.
  • Los desarrolladores deben ser conscientes de a qué se compila su código, porque eso es lo que realmente se ejecutará, y eso es lo que importa al final.

Prácticamente, ES2015 ha mejorado enormemente JavaScript y lo ha hecho mucho más agradable de escribir. Realmente no veo la necesidad de CoffeeScript hoy en día.

¿Qué herramientas y técnicas utiliza para depurar código JavaScript?

  • React y Redux
    • [React Devtools]
    • [Redux Devtools]
  • Vue
    • [Vue Devtools]
  • JavaScript
    • [Chrome Devtools]
    • Sentencia debugger
    • La buena y vieja depuración con console.log

¿Qué construcciones de lenguaje utiliza para iterar sobre propiedades de objetos y elementos de arrays?

Para objetos:

  • Bucles for-in: for (var property in obj) { console.log(property); }. Sin embargo, esto también iterará a través de sus propiedades heredadas, y deberá agregar una verificación obj.hasOwnProperty(property) antes de usarlo.
  • Object.keys(): Object.keys(obj).forEach(function (property) { ... }). Object.keys() es un método estático que lista todas las propiedades enumerables del objeto que se le pasa.
  • Object.getOwnPropertyNames(): Object.getOwnPropertyNames(obj).forEach(function (property) { ... }). Object.getOwnPropertyNames() es un método estático que lista todas las propiedades enumerables y no enumerables del objeto que se le pasa.

Para arrays:

  • Bucles for: for (var i = 0; i < arr.length; i++). El error común aquí es que var está en el ámbito de la función y no en el ámbito del bloque, y la mayoría de las veces querrá una variable iteradora con ámbito de bloque. ES2015 introduce let que tiene ámbito de bloque y se recomienda usarlo en su lugar. Así que esto se convierte en: for (let i = 0; i < arr.length; i++).
  • forEach: arr.forEach(function (el, index) { ... }). Esta construcción puede ser más conveniente a veces porque no tiene que usar el index si todo lo que necesita son los elementos del array. También existen los métodos every y some que le permitirán terminar la iteración antes.
  • Bucles for-of: for (let elem of arr) { ... }. ES6 introduce un nuevo bucle, el bucle for-of, que le permite iterar sobre objetos que cumplen con el protocolo iterable, como String, Array, Map, Set, etc. Combina las ventajas del bucle for y el método forEach(). La ventaja del bucle for es que puede salirse de él, y la ventaja de forEach() es que es más conciso que el bucle for porque no necesita una variable de contador. Con el bucle for-of, obtiene tanto la capacidad de salirse de un bucle como una sintaxis más concisa.

La mayoría de las veces, preferiría el método .forEach, pero realmente depende de lo que esté tratando de hacer. Antes de ES6, usábamos bucles for cuando necesitábamos terminar prematuramente el bucle usando break. Pero ahora con ES6, podemos hacerlo con bucles for-of. Usaría bucles for cuando necesite aún más flexibilidad, como incrementar el iterador más de una vez por bucle.

Además, al usar el bucle for-of, si necesita acceder tanto al índice como al valor de cada elemento del array, puede hacerlo con el método entries() de Array de ES6 y la desestructuración:

const arr = ['a', 'b', 'c']; for (let [index, elem] of arr.entries()) { console.log(index, ': ', elem); }

Explica la diferencia entre objetos mutables e inmutables.

La inmutabilidad es un principio fundamental en la programación funcional, y también tiene mucho que ofrecer a los programas orientados a objetos. Un objeto mutable es un objeto cuyo estado se puede modificar después de su creación. Un objeto inmutable es un objeto cuyo estado no se puede modificar después de su creación.

¿Cuál es un ejemplo de un objeto inmutable en JavaScript?

En JavaScript, algunos tipos incorporados (números, cadenas) son inmutables, pero los objetos personalizados son generalmente mutables.

Algunos objetos JavaScript inmutables incorporados son Math, Date.

Aquí hay algunas formas de agregar/simular inmutabilidad en objetos JavaScript simples.

Propiedades constantes de objetos

Al combinar writable: false y configurable: false, esencialmente puede crear una constante (que no se puede cambiar, redefinir o eliminar) como una propiedad de objeto, como:

let myObject = {}; Object.defineProperty(myObject, 'number', { value: 42, writable: false, configurable: false, }); console.log(myObject.number); // 42 myObject.number = 43; console.log(myObject.number); // 42

Evitar extensiones

Si desea evitar que se agreguen nuevas propiedades a un objeto, pero por lo demás dejar las demás propiedades del objeto intactas, llame a Object.preventExtensions(...):

var myObject = { a: 2, }; Object.preventExtensions(myObject); myObject.b = 3; myObject.b; // undefined

En modo no estricto, la creación de b falla silenciosamente. En modo estricto, arroja un TypeError.

Sellado (Seal)

Object.seal() crea un objeto "sellado", lo que significa que toma un objeto existente y esencialmente llama a Object.preventExtensions() en él, pero también marca todas sus propiedades existentes como configurable: false.

Así, no solo no se pueden agregar más propiedades, sino que tampoco se pueden reconfigurar ni eliminar propiedades existentes (aunque aún se pueden modificar sus valores).

Congelar (Freeze)

Object.freeze() crea un objeto congelado, lo que significa que toma un objeto existente y esencialmente llama a Object.seal() en él, pero también marca todas las propiedades de "accesador de datos" como writable:false, para que sus valores no puedan modificarse.

Este enfoque es el nivel más alto de inmutabilidad que se puede lograr para un objeto en sí mismo, ya que evita cualquier cambio en el objeto o en cualquiera de sus propiedades directas (aunque, como se mencionó anteriormente, el contenido de cualquier otro objeto referenciado no se ve afectado).

var immutable = Object.freeze({});

Congelar un objeto no permite agregar nuevas propiedades a un objeto y evita eliminar o alterar las propiedades existentes. Object.freeze() conserva la enumerabilidad, configurabilidad, escritura y el prototipo del objeto. Devuelve el objeto pasado y no crea una copia congelada.

¿Cuáles son las ventajas y desventajas de la inmutabilidad?

Ventajas

  • Detección de cambios más sencilla: la igualdad de objetos se puede determinar de manera eficiente y sencilla a través de la igualdad referencial. Esto es útil para comparar diferencias de objetos en React y Redux.
  • Los programas con objetos inmutables son menos complicados de entender, ya que no hay que preocuparse de cómo un objeto puede evolucionar con el tiempo.
  • Las copias defensivas ya no son necesarias cuando los objetos inmutables se devuelven o se pasan a funciones, ya que no hay posibilidad de que un objeto inmutable sea modificado por ellas.
  • Fácil compartición a través de referencias: una copia de un objeto es tan buena como otra, por lo que se pueden cachear objetos o reutilizar el mismo objeto varias veces.
  • Seguros para hilos: los objetos inmutables se pueden usar de forma segura entre hilos en un entorno multihilo, ya que no hay riesgo de que sean modificados en otros hilos que se ejecuten simultáneamente.
  • Usando librerías como ImmutableJS, los objetos se modifican usando compartición estructural y se necesita menos memoria para tener múltiples objetos con estructuras similares.

Contras

  • Las implementaciones ingenuas de estructuras de datos inmutables y sus operaciones pueden resultar en un rendimiento extremadamente pobre porque se crean nuevos objetos cada vez. Se recomienda usar bibliotecas para estructuras de datos inmutables eficientes y operaciones que aprovechen la compartición estructural.
  • La asignación (y desasignación) de muchos objetos pequeños en lugar de modificar los existentes puede causar un impacto en el rendimiento. La complejidad del asignador o del recolector de basura suele depender del número de objetos en el heap.
  • Las estructuras de datos cíclicas como los grafos son difíciles de construir. Si tienes dos objetos que no se pueden modificar después de la inicialización, ¿cómo puedes hacer que se apunten entre sí?

¿Cómo se puede lograr la inmutabilidad en su propio código?

La alternativa es usar declaraciones const combinadas con las técnicas mencionadas anteriormente para la creación. Para objetos "mutables", use el operador de propagación, Object.assign, Array.concat(), etc., para crear nuevos objetos en lugar de mutar el objeto original.

Ejemplos:

// Ejemplo de Array const arr = [1, 2, 3]; const newArr = [...arr, 4]; // [1, 2, 3, 4] // Ejemplo de Objeto const human = Object.freeze({ race: 'human' }); const john = { ...human, name: 'John' }; // {race: "human", name: "John"} const alienJohn = { ...john, race: 'alien' }; // {race: "alien", name: "John"}

Explica la diferencia entre funciones síncronas y asíncronas.

Las funciones síncronas son bloqueantes mientras que las asíncronas no lo son. En las funciones síncronas, las sentencias se completan antes de que se ejecute la siguiente. En este caso, el programa se evalúa exactamente en el orden de las sentencias y la ejecución del programa se pausa si una de las sentencias tarda mucho tiempo.

Las funciones asíncronas suelen aceptar un callback como parámetro y la ejecución continúa en la siguiente línea inmediatamente después de que se invoca la función asíncrona. El callback solo se invoca cuando la operación asíncrona se completa y la pila de llamadas está vacía. Las operaciones pesadas, como la carga de datos de un servidor web o la consulta de una base de datos, deben realizarse de forma asíncrona para que el hilo principal pueda seguir ejecutando otras operaciones en lugar de bloquearse hasta que esa operación larga se complete (en el caso de los navegadores, la interfaz de usuario se congelará).

¿Qué es el _event loop_? ¿Cuál es la diferencia entre la pila de llamadas y la cola de tareas?

El event loop (bucle de eventos) es un bucle de un solo hilo que monitorea la pila de llamadas (call stack) y verifica si hay trabajo por hacer en la cola de tareas (task queue). Si la pila de llamadas está vacía y hay funciones de callback en la cola de tareas, se saca una función de la cola y se empuja a la pila de llamadas para ser ejecutada.

Si aún no ha visto la charla de Philip Robert sobre el Event Loop, debería hacerlo. Es uno de los videos más vistos sobre JavaScript.

Explica las diferencias en el uso de `foo` entre `function foo() {}` y `var foo = function() {}`

La primera es una declaración de función, mientras que la segunda es una expresión de función. La diferencia clave es que las declaraciones de función tienen su cuerpo elevado (hoisted), pero los cuerpos de las expresiones de función no lo están (tienen el mismo comportamiento de elevación que las variables). Para obtener más explicaciones sobre el hoisting, consulte la pregunta anterior sobre el hoisting. Si intenta invocar una expresión de función antes de que se defina, obtendrá un error Uncaught TypeError: XXX is not a function.

Declaración de función

foo(); // 'FOOOOO' function foo() { console.log('FOOOOO'); }

Expresión de función

foo(); // Uncaught TypeError: foo is not a function var foo = function () { console.log('FOOOOO'); };

¿Cuáles son las diferencias entre las variables creadas usando `let`, `var` o `const`?

Las variables declaradas usando la palabra clave var tienen un ámbito limitado a la función en la que se crean, o si se crean fuera de cualquier función, al objeto global. let y const tienen un ámbito de bloque, lo que significa que solo son accesibles dentro del conjunto más cercano de llaves (función, bloque if-else o bucle for).

function foo() { // Todas las variables son accesibles dentro de las funciones. var bar = 'bar'; let baz = 'baz'; const qux = 'qux'; console.log(bar); // bar console.log(baz); // baz console.log(qux); // qux } console.log(bar); // ReferenceError: bar no está definido console.log(baz); // ReferenceError: baz no está definido console.log(qux); // ReferenceError: qux no está definido
if (true) { var bar = 'bar'; let baz = 'baz'; const qux = 'qux'; } // Las variables declaradas con var son accesibles en cualquier parte del ámbito de la función. console.log(bar); // bar // Las variables definidas con let y const no son accesibles fuera del bloque en el que fueron definidas. console.log(baz); // ReferenceError: baz no está definido console.log(qux); // ReferenceError: qux no está definido

var permite que las variables se eleven (hoisted), lo que significa que pueden ser referenciadas en el código antes de ser declaradas. let y const no permitirán esto, sino que lanzarán un error.

console.log(foo); // undefined var foo = 'foo'; console.log(baz); // ReferenceError: no se puede acceder a la declaración léxica 'baz' antes de la inicialización let baz = 'baz'; console.log(bar); // ReferenceError: no se puede acceder a la declaración léxica 'bar' antes de la inicialización const bar = 'bar';

Redeclarar una variable con var no lanzará un error, pero let y const sí lo harán.

var foo = 'foo'; var foo = 'bar'; console.log(foo); // "bar" let baz = 'baz'; let baz = 'qux'; // Uncaught SyntaxError: El identificador 'baz' ya ha sido declarado

let y const difieren en que let permite reasignar el valor de la variable, mientras que const no.

// Esto está bien. let foo = 'foo'; foo = 'bar'; // Esto causa una excepción. const baz = 'baz'; baz = 'qux';

¿Cuáles son las diferencias entre la clase ES6 y los constructores de funciones ES5?

Primero veamos un ejemplo de cada uno:

// Constructor de función ES5 function Person(name) { this.name = name; } // Clase ES6 class Person { constructor(name) { this.name = name; } }

Para constructores simples, se ven bastante similares.

La principal diferencia en el constructor surge al usar la herencia. Si queremos crear una clase Student que herede de Person y agregar un campo studentId, esto es lo que debemos hacer además de lo anterior.

// Constructor de función ES5 function Student(name, studentId) { // Llamar al constructor de la superclase para inicializar los miembros derivados de la superclase. Person.call(this, name); // Inicializar los propios miembros de la subclase. this.studentId = studentId; } Student.prototype = Object.create(Person.prototype); Student.prototype.constructor = Student; // Clase ES6 class Student extends Person { constructor(name, studentId) { super(name); this.studentId = studentId; } }

Es mucho más verboso usar la herencia en ES5 y la versión ES6 es más fácil de entender y recordar.

¿Puedes ofrecer un caso de uso para la nueva sintaxis de función de flecha =>? ¿En qué se diferencia esta nueva sintaxis de otras funciones?

Un beneficio obvio de las funciones de flecha es simplificar la sintaxis necesaria para crear funciones, sin necesidad de la palabra clave function. El this dentro de las funciones de flecha también está vinculado al ámbito envolvente, lo cual es diferente en comparación con las funciones regulares donde el this se determina por el objeto que lo llama. El this con alcance léxico es útil al invocar callbacks, especialmente en componentes de React.

¿Qué ventaja tiene usar la sintaxis de flecha para un método en un constructor?

La principal ventaja de usar una función de flecha como método dentro de un constructor es que el valor de this se establece en el momento de la creación de la función y no puede cambiar después de eso. Así, cuando el constructor se usa para crear un nuevo objeto, this siempre se referirá a ese objeto. Por ejemplo, digamos que tenemos un constructor Person que toma un nombre como argumento y tiene dos métodos para console.log ese nombre, uno como función regular y otro como función de flecha:

const Person = function (firstName) { this.firstName = firstName; this.sayName1 = function () { console.log(this.firstName); }; this.sayName2 = () => { console.log(this.firstName); }; }; const john = new Person('John'); const dave = new Person('Dave'); john.sayName1(); // John john.sayName2(); // John // El valor 'this' de la función regular puede cambiar, pero el de la función de flecha no. john.sayName1.call(dave); // Dave (porque "this" ahora es el objeto dave) john.sayName2.call(dave); // John john.sayName1.apply(dave); // Dave (porque 'this' ahora es el objeto dave) john.sayName2.apply(dave); // John john.sayName1.bind(dave)(); // Dave (porque 'this' ahora es el objeto dave) john.sayName2.bind(dave)(); // John var sayNameFromWindow1 = john.sayName1; sayNameFromWindow1(); // undefined (porque 'this' ahora es el objeto window) var sayNameFromWindow2 = john.sayName2; sayNameFromWindow2(); // John

Lo principal a destacar aquí es que this puede cambiar para una función normal, pero el contexto siempre permanece igual para una función de flecha. Por lo tanto, incluso si está pasando su función de flecha a diferentes partes de su aplicación, no tendría que preocuparse de que el contexto cambie.

Esto puede ser particularmente útil en los componentes de clase de React. Si define un método de clase para algo como un controlador de clics usando una función normal, y luego pasa ese controlador de clics a un componente hijo como una prop, también deberá vincular this en el constructor del componente padre. Si en su lugar usa una función de flecha, no es necesario vincular también "this", ya que el método obtendrá automáticamente su valor "this" de su contexto léxico envolvente.

¿Cuál es la definición de una función de orden superior?

Una función de orden superior es cualquier función que toma una o más funciones como argumentos, las cuales utiliza para operar sobre algunos datos, y/o devuelve una función como resultado. Las funciones de orden superior están destinadas a abstraer alguna operación que se realiza repetidamente. El ejemplo clásico de esto es map, que toma un array y una función como argumentos. map luego usa esta función para transformar cada elemento en el array, devolviendo un nuevo array con los datos transformados. Otros ejemplos populares en JavaScript son forEach, filter y reduce. Una función de orden superior no solo necesita manipular arrays, ya que hay muchos casos de uso para devolver una función de otra función. Function.prototype.bind es uno de esos ejemplos en JavaScript.

Map

Digamos que tenemos un array de nombres que necesitamos transformar cada cadena a mayúsculas.

const names = ['irish', 'daisy', 'anna'];

La forma imperativa sería la siguiente:

const transformNamesToUppercase = function (names) { const results = []; for (let i = 0; i < names.length; i++) { results.push(names[i].toUpperCase()); } return results; }; transformNamesToUppercase(names); // ['IRISH', 'DAISY', 'ANNA']

El uso de .map(transformerFn) hace que el código sea más corto y más declarativo.

const transformNamesToUppercase = function (names) { return names.map((name) => name.toUpperCase()); }; transformNamesToUppercase(names); // ['IRISH', 'DAISY', 'ANNA']

¿Puedes dar un ejemplo de desestructuración de un objeto o un array?

La desestructuración es una expresión disponible en ES6 que permite una forma concisa y conveniente de extraer valores de Objetos o Arrays y colocarlos en variables distintas.

Desestructuración de arrays

// Asignación de variables. const foo = ['one', 'two', 'three']; const [one, two, three] = foo; console.log(one); // "one" console.log(two); // "two" console.log(three); // "three"
// Intercambio de variables let a = 1; let b = 3; [a, b] = [b, a]; console.log(a); // 3 console.log(b); // 1

Desestructuración de objetos

// Asignación de variables. const o = { p: 42, q: true }; const { p, q } = o; console.log(p); // 42 console.log(q); // true

Los literales de plantilla de ES6 ofrecen mucha flexibilidad para generar cadenas, ¿puedes dar un ejemplo?

Los literales de plantilla ayudan a simplificar la interpolación de cadenas o la inclusión de variables en una cadena. Antes de ES2015, era común hacer algo como esto:

var person = { name: 'Tyler', age: 28 }; console.log( 'Hola, mi nombre es ' + person.name + ' y tengo ' + person.age + ' años!', ); // 'Hola, mi nombre es Tyler y tengo 28 años!'

Con los literales de plantilla, ahora puedes crear la misma salida de esta manera:

const person = { name: 'Tyler', age: 28 }; console.log(`Hola, mi nombre es ${person.name} y tengo ${person.age} años!`); // 'Hola, mi nombre es Tyler y tengo 28 años!'

Tenga en cuenta que se utilizan tildes (``), no comillas, para indicar que se está utilizando un literal de plantilla y que se pueden insertar expresiones dentro de los marcadores de posición ${}.

Un segundo caso de uso útil es la creación de cadenas de varias líneas. Antes de ES2015, se podía crear una cadena de varias líneas así:

console.log('Esta es la línea uno.\nEsta es la línea dos.'); // Esta es la línea uno. // Esta es la línea dos.

O si quería dividirla en varias líneas en su código para no tener que desplazarse hacia la derecha en su editor de texto para leer una cadena larga, también podía escribirla así:

console.log('Esta es la línea uno.\n' + 'Esta es la línea dos.'); // Esta es la línea uno. // Esta es la línea dos.

Sin embargo, los literales de plantilla conservan cualquier espaciado que les añada. Por ejemplo, para crear la misma salida de varias líneas que creamos anteriormente, simplemente puede hacer:

console.log(`Esta es la línea uno. Esta es la línea dos.`); // Esta es la línea uno. // Esta es la línea dos.

Otro caso de uso de los literales de plantilla sería utilizarlos como sustituto de las bibliotecas de plantillas para interpolaciones de variables simples:

const person = { name: 'Tyler', age: 28 }; document.body.innerHTML = ` <div> <p>Nombre: ${person.name}</p> <p>Edad: ${person.age}</p> </div> `;

¡Tenga en cuenta que su código puede ser susceptible a XSS al usar .innerHTML. ¡Sanitice sus datos antes de mostrarlos si provienen de un usuario!

¿Puedes dar un ejemplo de una función _curry_ y por qué esta sintaxis ofrece una ventaja?

Currying es un patrón donde una función con más de un parámetro se divide en múltiples funciones que, cuando se llaman en serie, acumularán todos los parámetros requeridos uno a la vez. Esta técnica puede ser útil para hacer que el código escrito en un estilo funcional sea más fácil de leer y componer. Es importante tener en cuenta que para que una función sea curried, necesita comenzar como una sola función, luego dividirse en una secuencia de funciones que cada una acepta un parámetro.

function curry(fn) { if (fn.length === 0) { return fn; } function _curried(depth, args) { return function (newArgument) { if (depth - 1 === 0) { return fn(...args, newArgument); } return _curried(depth - 1, [...args, newArgument]); }; } return _curried(fn.length, []); } function add(a, b) { return a + b; } var curriedAdd = curry(add); var addFive = curriedAdd(5); var result = [0, 1, 2, 3, 4, 5].map(addFive); // [5, 6, 7, 8, 9, 10]

¿Cuáles son los beneficios de usar la sintaxis de propagación (_spread syntax_) y en qué se diferencia de la sintaxis _rest_?

La sintaxis de propagación de ES6 es muy útil al codificar en un paradigma funcional, ya que podemos crear fácilmente copias de arreglos u objetos sin recurrir a Object.create, slice o una función de biblioteca. Esta característica del lenguaje se usa a menudo en proyectos de Redux y RxJS.

function putDookieInAnyArray(arr) { return [...arr, 'dookie']; } const result = putDookieInAnyArray(['Realmente', 'no', 'me', 'gusta']); // ["Realmente", "no", "me", "gusta", "dookie"] const person = { name: 'Todd', age: 29, }; const copyOfTodd = { ...person };

La sintaxis rest de ES6 ofrece una abreviatura para incluir un número arbitrario de argumentos que se pasarán a una función. Es como una inversa de la sintaxis de propagación, tomando datos y metiéndolos en un array en lugar de desempaquetar un array de datos, y funciona en argumentos de función, así como en asignaciones de desestructuración de arrays y objetos.

function addFiveToABunchOfNumbers(...numbers) { return numbers.map((x) => x + 5); } const result = addFiveToABunchOfNumbers(4, 5, 6, 7, 8, 9, 10); // [9, 10, 11, 12, 13, 14, 15] const [a, b, ...rest] = [1, 2, 3, 4]; // a: 1, b: 2, rest: [3, 4] const { e, f, ...others } = { e: 1, f: 2, g: 3, h: 4, }; // e: 1, f: 2, others: { g: 3, h: 4 }

¿Cómo se puede compartir código entre archivos?

Esto depende del entorno JavaScript.

En el cliente (entorno del navegador), siempre que las variables/funciones se declaren en el ámbito global (window), todos los scripts pueden referirse a ellas. Alternativamente, se puede adoptar la Definición de Módulos Asíncronos (AMD) a través de RequireJS para un enfoque más modular.

En el servidor (Node.js), la forma común ha sido usar CommonJS. Cada archivo se trata como un módulo y puede exportar variables y funciones adjuntándolas al objeto module.exports.

ES2015 define una sintaxis de módulo que tiene como objetivo reemplazar tanto AMD como CommonJS. Esto eventualmente será compatible tanto en entornos de navegador como de Node.

¿Por qué querría crear miembros de clase estáticos?

Los miembros estáticos de la clase (propiedades/métodos) no están vinculados a una instancia específica de una clase y tienen el mismo valor independientemente de la instancia que se refiera a ellos. Las propiedades estáticas suelen ser variables de configuración y los métodos estáticos suelen ser funciones de utilidad pura que no dependen del estado de la instancia.