70 preguntas de la entrevista de Javascript

¡Buen dia amigos!

Espero que este artículo sea útil tanto para desarrolladores novatos como para experimentados.

En preguntas que me parecieron más difíciles que otras, se dan referencias a literatura adicional.

Agradecería los comentarios detallados. Todos los comentarios se tendrán en cuenta al editar el artículo.

Entonces vamos.

70 preguntas de la entrevista de Javascript


Preguntas:
1. ¿Cuál es la diferencia entre nulo e indefinido?
2. ¿Para qué se utiliza el operador &&?
3. ¿Para qué se utiliza el operador "||"?
4. ¿Usar el unario más (operador +) es la forma más rápida de convertir una cadena en un número?
5. ¿Qué es un DOM?
6. ¿Qué es la propagación de eventos?
7. ¿Qué es el burbujeo de eventos?
8. ¿Qué es la captura de eventos?
9. ¿Cuál es la diferencia entre los métodos event.preventDefault () y event.stopPropagation ()?
10. ¿Cómo aprender a usar el método event.preventDefault ()?
11. ¿Por qué obj.someprop.x produce un error?
12. ¿Qué es un objetivo de evento o elemento de destino (event.target)?
13. (event.currentTarget)?
14. "==" "==="?
15. false?
16. "!!"?
17. ?
18. (Hoisting)?
19. (Scope)?
20. (Closures)?
21. JS ?
22. , ?
23. «use strict»?
24. this?
25. ?
26. IIFE?
27. Function.prototype.apply?
28. Function.prototype.call?
29. call apply?
30. Function.prototype.bind?
31. JS ?
32. (Higher Order Functions)?
33. JS (First-class Objects)?
34. Array.prototype.map?
35. Array.prototype.filter?
36. Array.prototype.reduce?
37. arguments?
38. , ?
39. b ?
40. ECMAScript?
41. JS ES6 ECMAScript2015?
42. «var», «let» «const»?
43. (Arrow Functions)?
44. (Classes)?
45. (Template Literals)?
46. (Object Destructuring)?
47. (Modules)?
48. Set?
49. (Callback Function)?
50. (Promises)?
51. async/await?
52. spread- rest-?
53. (Default Parameters)?
54. (Wrapper Objects)?
55. (Implicit and Explicit Coercion)?
56. NaN? , NaN?
57. , ?
58. , , ( "%")?
59. ?
60. AJAX?
61. JS ?
62. Object.freeze Object.seal?
63. «in» hasOwnProperty?
64. ¿Qué técnicas de trabajo con código asincrónico en JS conoces?
65. ¿Cuál es la diferencia entre una función normal y una expresión funcional?
66. ¿Cómo llamar a una función en JS?
67. ¿Qué es memorización o memorización?
68. ¿Cómo implementaría la función auxiliar de memorización?
69. ¿Por qué typeof null devuelve objeto? ¿Cómo verificar si un valor es nulo?
70. ¿Para qué se utiliza la palabra clave "nueva"?

1. ¿Cuál es la diferencia entre nulo e indefinido?


Para comenzar, hablemos de lo que tienen en común.

En primer lugar, pertenecen a 7 "primitivas" JS (tipos primitivos):

let primitiveTypes = ['string', 'number', 'null', 'undefined', 'boolean', 'symbol', 'bigint']

En segundo lugar, son valores falsos, es decir. el resultado de convertirlos a booleanos usando Boolean () o el operador "!!" Es falso:

console.log(!!null) // false
console.log(!!undefined) // false

console.log(Boolean(null)) // false
console.log(Boolean(undefined)) // false

Bien, ahora sobre las diferencias.

undefined es el valor predeterminado:
  • una variable a la que no se le ha asignado ningún valor, es decir una variable declarada pero no inicializada;
  • una función que no devuelve nada explícitamente, por ejemplo, console.log (1);
  • propiedad inexistente del objeto.

En estos casos, el motor JS establece el valor en indefinido.

let _thisIsUndefined
const doNothing = () => {}
const someObj = {
    a: 'ay',
    b: 'bee',
    c: 'si'
}
console.log(_thisIsUndefined) // undefined
console.log(doNothing()) // undefined
console.log(someObj['d']) // undefined

nulo es el "valor sin valor". nulo es el valor que se asigna a la variable explícitamente. En el siguiente ejemplo, obtenemos nulo cuando el método fs.readFile funciona sin errores:

fs.readFile('path/to/file', (e, data) => {
    console.log(e) //    null
if(e) {
    console.log(e)
}
    console.log(data)
})

Al comparar nulo e indefinido, obtenemos verdadero cuando se usa el operador "==", y falso cuando se usa el operador "===". Sobre por qué sucede esto, ver más abajo.

console.log(null == undefined) // true
console.log(null === undefined) // false

2. ¿Para qué se utiliza el operador &&?


El operador && (lógico y) encuentra y devuelve el primer valor falso o el último operando cuando todos los valores son verdaderos. Utiliza un cortocircuito para evitar costos innecesarios:

console.log(false && 1 && []) // false
console.log(' ' && true && 5) // 5

Con la declaración if:

const router: Router = Router()

router.get('/endpoint', (req: Request, res: Response) => {
    let conMobile: PoolConnection
    try {
        //    
    } catch (e) {
        if (conMobile) {
            conMobile.release()
        }
    }
})

Lo mismo con el operador &&:

const router: Router = Router()

router.get('/endpoint', (req: Request, res: Response) => {
    let conMobile: PoolConnection
    try {
        //    
    } catch (e) {
        conMobile && conMobile.release()
    }
})

3. ¿Para qué se utiliza el operador "||"?


El operador "||" (boolean o) encuentra y devuelve el primer valor verdadero. También usa un cortocircuito. Este operador se usó para asignar parámetros predeterminados en funciones antes de que los parámetros predeterminados se estandarizaran en ES6.

console.log(null || 1 || undefined) // 1

function logName(name) {
    let n = name || Mark
    console.log(n)
}

logName() // Mark

4. ¿Usar el unario más (operador +) es la forma más rápida de convertir una cadena en un número?


Según MDN , el operador + es de hecho la forma más rápida de convertir una cadena en un número, ya que no realiza ninguna operación en un valor que es un número.

5. ¿Qué es un DOM?


El DOM o Document Object Model es una interfaz de programación de aplicaciones (API) para trabajar con documentos HTML y XML. Cuando el navegador lee por primera vez ("analiza") el documento HTML, forma un objeto grande, un objeto realmente grande basado en el documento: el DOM. El DOM es una estructura de árbol (árbol de documentos). El DOM se usa para interactuar y cambiar la estructura del propio DOM o de sus elementos y nodos individuales.

Digamos que tenemos este HTML:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document Object Model</title>
</head>

<body>
    <div>
        <p>
            <span></span>
        </p>
        <label></label>
        <input>
    </div>
</body>

</html>

El DOM de este HTML tiene este aspecto:



en JS, el DOM está representado por un objeto Document. El objeto Documento tiene una gran cantidad de métodos para trabajar con elementos, su creación, modificación, eliminación, etc.

6. ¿Qué es la propagación de eventos?


Cuando ocurre un evento en un elemento DOM, en realidad ocurre no solo en él. El evento "se propaga" desde el objeto Window al elemento que lo llamó (event.target). En este caso, el evento impregna (afecta) secuencialmente a todos los antepasados ​​del elemento objetivo. La propagación de un evento tiene tres etapas o fases:
  1. Fase de inmersión (captura, intercepción): se produce un evento en el objeto Ventana y desciende al objetivo del evento a través de todos sus antepasados.
  2. La fase objetivo es cuando el evento alcanza el elemento objetivo.
  3. Fase ascendente: un evento se eleva desde event.target, pasa secuencialmente a través de todos sus antepasados ​​y llega al objeto Window.



Lea más sobre la distribución de eventos aquí y aquí .

7. ¿Qué es una ventana emergente de evento?


Cuando ocurre un evento en un elemento DOM, afecta no solo a este elemento. Un evento "emerge" (como una burbuja de aire en el agua), pasa del elemento que causó el evento (event.target) a su padre, luego se eleva aún más, al padre del padre del elemento, hasta que alcanza el objeto Window.

Digamos que tenemos este marcado:

<div class="grandparent">
    <div class="parent">
        <div class="child">1</div>
    </div>
</div>

Y tal JS:

function addEvent(el, event, callback, isCapture = false) {
    if (!el || !event || !callback || typeof callback !== 'function') return

    if (typeof el === 'string') {
        el = document.querySelector(el)
    }
    el.addEventListener(event, callback, isCapture)
}

addEvent(document, 'DOMContentLoaded', () => {
    const child = document.querySelector('.child')
    const parent = document.querySelector('.parent')
    const grandparent = document.querySelector('.grandparent')

    addEvent(child, 'click', function(e) {
        console.log('child')
    })

    addEvent(parent, 'click', function(e) {
        console.log('parent')
    })

    addEvent(grandparent, 'click', function(e) {
        console.log('grandparent')
    })

    addEvent('html', 'click', function(e) {
        console.log('html')
    })

    addEvent(document, 'click', function(e) {
        console.log('document')
    })

    addEvent(window, 'click', function(e) {
        console.log('window')
    })
})

El método addEventListener tiene un tercer parámetro opcional: useCapture. Cuando su valor es falso (el valor predeterminado), el evento comienza con la fase de ascenso. Cuando su valor es verdadero, el evento comienza con la fase de inmersión (para los "oyentes" de los eventos adjuntos al objetivo del evento, el evento está en la fase objetivo, y no en las fases de inmersión o ascenso. Los eventos en la fase objetivo son activados por todos los oyentes en el elemento en el en el que se registraron independientemente del parámetro useCapture - aprox. Si hacemos clic en el elemento hijo, la consola mostrará: hijo, padre, abuelo, html, documento, ventana. Esto es lo que es una ventana emergente de evento.

8. ¿Qué es un evento de inmersión?


Cuando un evento ocurre en un elemento DOM, ocurre no solo en él. En la fase de inmersión, el evento desciende del objeto Ventana al objetivo del evento a través de todos sus antepasados.

Margen:

<div class="grandparent">
    <div class="parent">
        <div class="child">1</div>
    </div>
</div>

JS:

function addEvent(el, event, callback, isCapture = false) {
    if (!el || !event || !callback || typeof callback !== 'function') return

    if (typeof el === 'string') {
        el = document.querySelector(el);
    }
    el.addEventListener(event, callback, isCapture)
}

addEvent(document, 'DOMContentLoaded', () => {
    const child = document.querySelector('.child')
    const parent = document.querySelector('.parent')
    const grandparent = document.querySelector('.grandparent')

    addEvent(child, 'click', function(e) {
        console.log('child');
    }, true)

    addEvent(parent, 'click', function(e) {
        console.log('parent')
    }, true)

    addEvent(grandparent, 'click', function(e) {
        console.log('grandparent')
    }, true)

    addEvent('html', 'click', function(e) {
        console.log('html')
    }, true)

    addEvent(document, 'click', function(e) {
        console.log('document')
    }, true)

    addEvent(window, 'click', function(e) {
        console.log('window')
    }, true)
})

El método addEventListener tiene un tercer parámetro opcional: useCapture. Cuando su valor es falso (el valor predeterminado), el evento comienza con la fase de ascenso. Cuando su valor es verdadero, el evento comienza con la fase de inmersión. Si hacemos clic en el elemento hijo, veremos lo siguiente en la consola: ventana, documento, html, abuelo, padre, hijo. Esta es la inmersión del evento.

9. ¿Cuál es la diferencia entre los métodos event.preventDefault () y event.stopPropagation ()?


El método event.preventDefault () deshabilita el comportamiento predeterminado de un elemento. Si usa este método en el elemento de formulario, evitará que el formulario se envíe. Si lo usa en el menú contextual, el menú contextual se deshabilitará (este método se usa a menudo en el teclado para redefinir el teclado, por ejemplo, al crear un reproductor de música / video o editor de texto - aprox. El método event.stopPropagation () deshabilita la propagación del evento (su ascenso o inmersión).

10. ¿Cómo aprender a usar el método event.preventDefault ()?


Para hacer esto, podemos usar la propiedad event.defaulPrevented, que devuelve un valor booleano que sirve como indicador de aplicación al elemento del método event.preventDefault.

11. ¿Por qué obj.someprop.x produce un error?



const obj = {}
console.log(obj.someprop.x)

La respuesta es obvia: estamos intentando acceder a la propiedad x de la propiedad someprop, que no está definida. obj .__ proto __.__ proto = nulo, por lo que se devuelve undefined y undefined no tiene la propiedad x.

12. ¿Qué es un objetivo de evento o elemento de destino (event.target)?


En palabras simples, event.target es el elemento en el que ocurre el evento, o el elemento que provocó el evento.

Tenemos el siguiente marcado:

<div onclick="clickFunc(event)" style="text-align: center; margin: 15px;
border: 1px solid red; border-radius: 3px;">
    <div style="margin: 25px; border: 1px solid royalblue; border-radius: 3px;">
        <div style="margin: 25px; border: 1px solid skyblue; border-radius: 3px;">
            <button style="margin: 10px">
                Button
            </button>
        </div>
    </div>
</div>

Y un JS tan simple:

function clickFunc(event) {
    console.log(event.target)
}

Adjuntamos un "oyente" al div externo. Sin embargo, si hacemos clic en el botón, obtenemos el diseño de este botón en la consola. Esto nos permite concluir que el elemento que causó el evento es el botón en sí, y no los divs internos o externos.

13. ¿Cuál es el propósito actual del evento (event.currentTarget)?


Event.currentTarget es el elemento al que está conectado el detector de eventos.

Marcado similar:

<div onclick="clickFunc(event)" style="text-align: center;margin:15px;
border:1px solid red;border-radius:3px;">
    <div style="margin: 25px; border:1px solid royalblue;border-radius:3px;">
        <div style="margin:25px;border:1px solid skyblue;border-radius:3px;">
            <button style="margin:10px">
                Button
            </button>
        </div>
    </div>
</div>

Y un JS ligeramente modificado:

function clickFunc(event) {
    console.log(event.currentTarget)
}

Adjuntamos al oyente al div externo. Dondequiera que hagamos clic, ya sea un botón o uno de los div internos, en la consola siempre obtenemos el diseño del div externo. Esto nos permite concluir que event.currentTarget es el elemento al que está conectado el detector de eventos.

14. ¿Cuál es la diferencia entre los operadores "==" y "==="?


La diferencia entre el operador == (igualdad abstracta o no estricta) y el operador === (igualdad estricta) es que el primero compara los valores después de que se convierten o se convierten a un tipo (Coersion), y el segundo, sin dicha conversión .

Vamos a cavar más profundo. Y primero, hablemos de la transformación.

Una conversión es un proceso de convertir un valor a otro tipo, o más bien, un proceso de convertir los valores comparados a un tipo. Al comparar, el operador "==" produce la llamada comparación implícita. El operador "==" realiza algunas operaciones antes de comparar dos valores.

Supongamos que comparamos x e y.

El algoritmo es como sigue:

  1. Si x e y son del mismo tipo, la comparación se realiza utilizando el operador "===".
  2. x = null y = undefined true.
  3. x = undefined y = null true.
  4. x = , y = , x == toNumber(y) ( y ).
  5. x = , y = , toNumber(x) == y ( x ).
  6. x = , toNumber(x) == y.
  7. y = , x == toNumber(y).
  8. x = , , y = , x == toPrimitive(y) ( y ).
  9. x = , y = , , toPrimitive(x) == y.
  10. false.

Recuerde: para convertir un objeto en un "primitivo", el método toPrimitive primero usa el método valueOf, luego el método toString.

Ejemplos:



todos los ejemplos devuelven verdadero.

El primer ejemplo es la primera condición del algoritmo.
El segundo ejemplo es la cuarta condición.
El tercero es el segundo.
El cuarto es el séptimo.
Quinto - octavo.
Y el último es el décimo.



Si utilizamos el operador "===", todos los ejemplos, excepto el primero, devolverán falso, ya que los valores en estos ejemplos son de diferentes tipos.

15. ¿Por qué el resultado de comparar dos objetos similares es falso?


let a = {
    a: 1
}
let b = {
    a: 1
}
let c = a

console.log(a === b) // false
console.log(a === c) // true ...

En JS, los objetos y las primitivas se comparan de manera diferente. Las primitivas se comparan por valor. Objetos: por referencia o dirección en la memoria donde se almacena la variable. Es por eso que el primer console.log devuelve falso y el segundo devuelve verdadero. Las variables "a" y "c" se refieren al mismo objeto, mientras que las variables "a" y "b" se refieren a diferentes objetos con las mismas propiedades y valores.

16. ¿Para qué se usa el operador "!!"?


El operador "!!" (doble negación) lleva el valor a su derecho a un valor lógico.

console.log(!!null) // false
console.log(!!undefined) // false
console.log(!!'') // false
console.log(!!0) // false
console.log(!!NaN) // false
console.log(!!' ') // true
console.log(!!{}) // true
console.log(!![]) // true
console.log(!!1) // true
console.log(!![].length) // false

17. ¿Cómo escribir múltiples expresiones en una línea?


Para esto podemos usar el operador "," (coma). Este operador "se mueve" de izquierda a derecha y devuelve el valor de la última expresión u operando.

let x = 5

x = (x++, x = addFive(x), x *= 2, x -= 5, x += 10)

function addFive(num) {
    return num + 5
}

Si imprimimos el valor de x en la consola, obtenemos 27. Primero, aumentamos el valor de x en uno (x = 6). Luego llamamos a la función addFive () con el parámetro 6, al que agregamos 5 (x = 11). Después de eso, multiplicamos el valor de x por 2 (x = 22). Luego reste 5 (x = 17). Y finalmente, suma 10 (x = 27).

18. ¿Qué es izar?


Elevación es un término que describe el surgimiento de una variable o función en un ámbito global o funcional.

Para comprender qué es la elevación, debe comprender cuál es el contexto de ejecución.

El contexto de ejecución es el entorno en el que se ejecuta el código. El contexto de ejecución tiene dos fases: compilación y ejecución en sí.

Compilacion. En esta fase, las expresiones funcionales y las variables declaradas usando la palabra clave "var" con el valor indefinido se elevan a la parte superior del alcance global (o funcional) (como si se moviera al comienzo de nuestro código. Esto explica por qué podemos llamar a las funciones antes de que anuncios - aprox.

Actuación. En esta fase, las variables son valores asignados, y las funciones (o métodos de objetos) se llaman o ejecutan.

Recuerde: solo se generan expresiones funcionales y variables declaradas con la palabra clave "var". Las funciones ordinarias y las funciones de flecha, así como las variables declaradas con las palabras clave "let" y "const", no se muestran.

Supongamos que tenemos un código como este:

console.log(y)
y = 1
console.log(y)
console.log(greet('Mark'))

function greet(name) {
    return 'Hello ' + name + '!'
}

var y

Nos quedan indefinidos, 1 y '¡Hola Mark!'.

Así es como se ve la fase de compilación:

function greet(name) {
    return 'Hello ' + name + '!'
}

var y //  undefined

//    

//    
/*
console.log(y)
y = 1
console.log(y)
console.log(greet('Mark'))
*/

Una vez completada la fase de compilación, la fase de ejecución comienza cuando se asignan valores a las variables y se llaman funciones.

Puede encontrar más información sobre la elevación aquí .

19. ¿Qué es un alcance?


Un ámbito es un lugar donde (o desde donde) tenemos acceso a variables o funciones. JS tenemos tres tipos de ámbitos: global, funcional y de bloque (ES6).

Alcance global: las variables y funciones declaradas en el espacio de nombres global tienen un alcance global y son accesibles desde cualquier parte del código.

//   
var g = 'global'

function globalFunc() {
    function innerFunc() {
        console.log(g) //     g,    
    }
    innerFunc()
}

Alcance funcional (alcance de una función): las variables, funciones y parámetros declarados dentro de una función solo están disponibles dentro de esta función.

function myFavouriteFunc(a) {
    if (true) {
        var b = 'Hello ' + a
    }
    return b
}
myFavouriteFunc('World')

console.log(a) // Uncaught ReferenceError: a is not defined
console.log(b) //  

Alcance del bloque: las variables (declaradas con las palabras clave "let" y "const") dentro del bloque ({}) solo están disponibles dentro de él.

function testBlock() {
    if (true) {
        let z = 5
    }
    return z
}

testBlock() // Uncaught ReferenceError: z is not defined

Un ámbito también es un conjunto de reglas mediante el cual se busca una variable. Si la variable no existe en el alcance actual, su búsqueda se realiza más arriba en la visibilidad externa del alcance actual. Si no hay una variable en el alcance externo, su búsqueda continúa hasta el alcance global. Si se encuentra una variable en el ámbito global, la búsqueda se detiene; de ​​lo contrario, se genera una excepción. La búsqueda se lleva a cabo por las áreas de visibilidad más cercanas a las actuales y se detiene al encontrar la variable. Esto se llama la cadena de alcance.

//   
//    ->    ->   

//   
var variable1 = 'Comrades'
var variable2 = 'Sayonara'

function outer() {
    //   
    var variable1 = 'World'

    function inner() {
        //   
        var variable2 = 'Hello'
        console.log(variable2 + ' ' + variable1)
    }
    inner()
}
outer()
//    'Hello World',
//   variable2 = 'Hello'  variable1 = 'World'  
//     



20. ¿Qué es un cierre (cierres)?


Esta es probablemente la pregunta más difícil de la lista. Trataré de explicar cómo entiendo el cierre.

De hecho, el cierre es la capacidad de una función para crear enlaces a variables y parámetros que están en el ámbito actual, en el ámbito de la función principal, en el ámbito del principal de la función principal, y así sucesivamente en el ámbito global utilizando la cadena de ámbitos en el momento de la creación. Normalmente, el alcance se determina cuando se crea una función.

Los ejemplos son una excelente manera de explicar el cierre:

//   
var globalVar = 'abc'

function a() {
    //   
    console.log(globalVar)
}

a() // 'abc'
//   
//    a ->   

En este ejemplo, cuando declaramos una función, el alcance global es parte del cierre.



La variable "globalVar" no importa en la imagen, porque su valor puede cambiar dependiendo de dónde y cuándo se llamará a la función. Pero en el ejemplo anterior, globalVar tendrá el valor "abc".

Ahora el ejemplo es más complicado:

var globalVar = 'global'
var outerVar = 'outer'

function outerFunc(outerParam) {
    function innerFunc(innerParam) {
        console.log(globalVar, outerParam, innerParam)
    }
    return innerFunc
}

const x = outerFunc(outerVar)
outerVar = 'outer-2'
globalVar = 'guess'
x('inner')



El resultado es "adivinar exterior interior". La explicación es esta: cuando llamamos a la función externalFunc y establecemos la variable "x" en el valor devuelto por la función innerFunc, el parámetro "externalParam" es igual a "exterior". A pesar del hecho de que asignamos la variable "externalVar" a "external-2", esto sucedió después de llamar a la función externalFunc, que "logró" encontrar el valor de la variable "outsideVar" en la cadena de alcance, este valor fue "externo". Cuando llamamos a "x", que se refiere a innerFunc, el valor de "innerParam" es "interno", porque pasamos este valor como parámetro cuando llamamos a "x". globalVar tiene un valor de "adivinar" porque le asignamos ese valor antes de llamar a "x".

Un ejemplo de un malentendido de un circuito.

const arrFunc = []
for (var i = 0; i < 5; i++) {
    arrFunc.push(function() {
        return i
    })
}
console.log(i) // 5

for (let i = 0; i < arrFunc.length; i++) {
    console.log(arrFunc[i]()) //  5
}

Este código no funciona como se esperaba. Declarar una variable usando la palabra clave var hace que esta variable sea global. Después de agregar funciones a la matriz arrFunc, el valor de la variable global "i" se convierte en "5". Por lo tanto, cuando llamamos a la función, devuelve el valor de la variable global "i". Un cierre almacena una referencia a una variable, no su valor en el momento de la creación. Este problema puede resolverse usando IIFE o declarando una variable usando la palabra clave "let".

Lea más sobre el cierre aquí y aquí .

21. ¿Qué valores en JS son falsos?


const falsyValues = ['', 0, null, undefined, NaN, false]

Falso son valores cuya conversión a un valor booleano es falsa.

22. ¿Cómo verificar si un valor es falso?


Utilice la función booleana o el operador "!!" (dos veces no).

23. ¿Para qué se usa la directiva estricta de uso?


"Usar estricto" es una directiva ES5 que obliga a ejecutar todo nuestro código o el código de una función individual en modo estricto. El modo estricto introduce algunas restricciones en la escritura de código, evitando así errores en las primeras etapas.

Aquí están las limitaciones del modo estricto.

No puede asignar valores ni acceder a variables no declaradas:

function returnY() {
    'use strict'
    y = 123
    return y
}
returnY() // Uncaught ReferenceError: y is not defined

Está prohibido asignar valores globales a variables de solo lectura o de solo escritura:

'use strict'
var NaN = NaN // Uncaught TypeError: Cannot assign to read only property 'NaN' of object '#<Window>'
var undefined = undefined
var Infinity = 'and beyond'

No puede eliminar la propiedad "indeleble" de un objeto:

'use strict'
const obj = {}

Object.defineProperties(obj, 'x', {
    value: 1
})

delete obj.x // Uncaught TypeError: Property description must be an object: x

La duplicación de parámetros está prohibida:

'use strict'

function someFunc(a, b, b, c) {} // Uncaught SyntaxError: Duplicate parameter name not allowed in this context

No puede crear funciones con la función eval:

'use strict'

eval('var x = 1')

console.log(x) // Uncaught ReferenceError: x is not defined

El valor predeterminado para esto no está definido:

'use strict'

function showMeThis() {
    return this
}

showMeThis() // undefined

... etc.

24. ¿Qué significa esto?


Esto generalmente se refiere al valor del objeto que actualmente está ejecutando o llamando a la función. "En este momento" significa que el valor de esto varía según el contexto de ejecución, donde usamos esto.

const carDetails = {
    name: 'Ford Mustang',
    yearBought: 2005,
    getName() {
        return this.name
    }
    isRegistered: true
}

console.log(carDetails.getName()) // Ford Mustang

En este caso, el método getName devuelve this.name, y esto se refiere a carDetails, el objeto en el que se ejecuta getName, que es su "propietario".

Agregue tres líneas después de console.log:

var name = 'Ford Ranger'
var getCarName = carDetails.getName

console.log(getCarName()) // Ford Ranger

El segundo console.log produce un Ford Ranger, y esto es extraño. La razón de este comportamiento es que el "propietario" de getCarName es el objeto de la ventana. Las variables declaradas con la palabra clave var en el ámbito global se escriben en las propiedades del objeto de ventana. esto en el ámbito global se refiere al objeto de la ventana (a menos que sea un modo estricto).

console.log(getCarName === window.getCarName) // true
console.log(getCarName === this.getCarName) // true

En este ejemplo, this y window se refieren al mismo objeto.

Una forma de resolver este problema es usar la llamada o aplicar métodos:

console.log(getCarName.apply(carDetails)) // Ford Mustang
console.log(getCarName.call(carDetails)) // Ford Mustang

Llame y aplique tomar como primer argumento un objeto que será el valor de esto dentro de la función.

En IIFE, funciones que se crean en el ámbito global, funciones anónimas y funciones internas de los métodos de un objeto, el valor predeterminado para esto es el objeto de ventana.

(function() {
    console.log(this)
})() // window

function iHateThis() {
    console.log(this)
}
iHateThis() // window

const myFavouriteObj = {
    guessThis() {
        function getName() {
            console.log(this.name)
        }
        getName()
    },
    name: 'Marko Polo',
    thisIsAnnoying(callback) {
        callback()
    }
}

myFavouriteObj.guessThis() // window
myFavouriteObj.thisIsAnnoying(function() {
    console.log(this) // window
})

Hay dos formas de obtener Marko Polo.

Primero, podemos almacenar el valor de esto en una variable:

const myFavoriteObj = {
    guessThis() {
        const self = this //   this   self
        function getName() {
            console.log(self.name)
        }
        getName()
    },
    name: 'Marko Polo',
    thisIsAnnoying(callback) {
        callback()
    }
}

En segundo lugar, podemos usar la función de flecha:

const myFavoriteObj = {
    guessThis() {
        const getName = () => {
            //   this   
            console.log(this.name)
        }
        getName()
    },
    name: 'Marko Polo',
    thisIsAnnoying(callback) {
        callback()
    }
}

Las funciones de flecha no tienen este valor propio. Copian el significado de esto del entorno léxico externo.

25. ¿Qué es un prototipo de un objeto?


En pocas palabras, un prototipo es un plan (diagrama o proyecto) de un objeto. Se utiliza como reserva para las propiedades y métodos existentes en este objeto. También es una de las formas de intercambiar propiedades y funcionalidades entre objetos. Este es el concepto básico de herencia prototipo en JS.

const o = {}
console.log(o.toString()) // [object Object]

Aunque el objeto "o" no tiene la propiedad toString, acceder a esta propiedad no causa un error. Si una propiedad específica no está en el objeto, su búsqueda se lleva a cabo primero en el prototipo del objeto, luego en el prototipo del prototipo del objeto, y así sucesivamente hasta que se encuentre la propiedad. Esto se llama la cadena prototipo. En la parte superior de la cadena de prototipos se encuentra Object.prototype.

console.log(o.toString === Object.prototype.toString) // true

Lea más sobre prototipos y herencia aquí y aquí .

26. ¿Qué es el IIFE?


IIFE o Expresión de función invocada inmediatamente es una función que se llama o ejecuta inmediatamente después de la creación o declaración. Para crear IIFE, debe ajustar la función entre paréntesis (el operador de agrupación), convertirla en una expresión y luego llamarla usando otros paréntesis. Se ve así: (function () {}) ().

(function( ) { }( ))

(function( ) { })( )

(function named(params) { })( )

(( ) => { })

(function(global) { })(window)

const utility = (function( ) {
    return {
        // 
    }
})

Todos estos ejemplos son válidos. El penúltimo ejemplo muestra que podemos pasar parámetros a IIFE. El último ejemplo muestra que podemos almacenar el resultado de IIFE en una variable.

El mejor uso de IIFE es realizar funciones de configuración de inicialización y evitar conflictos de nombres con otras variables en el ámbito global (contaminación del espacio de nombres global). Damos un ejemplo.

<script src="https://cdnurl.com/somelibrary.js"></script>

Tenemos un enlace a la biblioteca somelibrary.js que proporciona algunas funciones globales que podemos usar en nuestro código, pero hay dos métodos en esta biblioteca, createGraph y drawGraph, que no usamos porque contienen errores. Y queremos implementar estas funciones por nuestra cuenta.

Una forma de resolver este problema es cambiar la estructura de nuestros scripts:

<script src="https://cdnurl.com/somelibrary.js"></script>
<script>
    function createGraph() {
        // 
    }

    function drawGraph() {
        // 
    }
</script>

Por lo tanto, redefinimos los métodos proporcionados por la biblioteca.

La segunda forma es cambiar los nombres de nuestras funciones:

<script src="https://cdnurl.com/somelibrary.js"></script>
<script>
    function myCreateGraph() {
        // 
    }

    function myDrawGraph() {
        // 
    }
</script>

La tercera forma es usar IIFE:

<script>
    const graphUtility = (function() {
        function createGraph() {
            // 
        }

        function drawGraph() {
            // 
        }
        return {
            createGraph,
            drawGraph
        }
    })
</script>

En este ejemplo, creamos una variable de utilidad que contiene el resultado IIFE, que devuelve un objeto que contiene los métodos createGraph y drawGraph.

Aquí hay otro problema que se puede resolver con IIFE:

val li = document.querySelectorAll('.list-group > li')
for (var i - 0, len = li.length; i < len; i++) {
    li[i].addEventListener('click', function(e) {
        console.log(i)
    })
}

Supongamos que tenemos un elemento ul con una clase de grupo de lista que contiene 5 elementos secundarios li. Y queremos mostrar el valor "i" en la consola al hacer clic en un "li" separado. Sin embargo, en cambio, la consola siempre muestra 5. La falla es toda la culpa.

Una solución es IIFE:

var li = document.querySelectorAll('.list-group > li')
for (var i = 0, len = li.length; i < len; i++) {
    (function(currentIndex) {
        li[currentIndex].addEventListener('click', function(e) {
            console.log(currentIndex)
        })
    })(i)
}

La razón por la que este código funciona según lo previsto es porque IIFE crea un nuevo alcance en cada iteración, y escribimos el valor "i" en currentIndex.

27. ¿Para qué se utiliza el método Function.prototype.apply?


Aplicar se utiliza para vincular un objeto específico al valor de este de la función llamada.

const details = {
    message: 'Hello World!'
}

function getMessage() {
    return this.message
}

getMessage.apply(details) // Hello World!

Este método es similar a Function.prototype.call. La única diferencia es que en apply, los argumentos se pasan como una matriz.

const person = {
    name: 'Marko Polo'
}

function greeting(greetingMessage) {
    return `${greetingMessage} ${this.name}`
}

greeting.apply(person, ['Hello']) // Hello Marko Polo

28. ¿Para qué se utiliza el método function.prototype.call?


La llamada se utiliza para vincular un objeto específico al valor de esta función que se llama.

const details = {
    message: 'Hello World!'
};

function getMessage() {
    return this.message;
}

getMessage.call(details); // Hello World!

Este método es similar a Function.prototype.apply. La diferencia es que en la llamada los argumentos se pasan separados por comas.

const person = {
    name: 'Marko Polo'
};

function greeting(greetingMessage) {
    return `${greetingMessage} ${this.name}`;
}

greeting.call(person, 'Hello'); // Hello Marko Polo

29. ¿Cuál es la diferencia entre llamar y aplicar métodos?


La diferencia entre llamar y aplicar es cómo pasamos argumentos en la función llamada. En apply, los argumentos se pasan como una matriz, en llamada, separados por comas.

const obj1 = {
    result: 0
}

const obj2 = {
    result: 0
}

function reduceAdd() {
    let result = 0
    for (let i = 0, len = arguments.length; i < len; i++) {
        result += arguments[i]
    }
    this.result = result
}

reduceAdd.apply(obj1, [1, 2, 3, 4, 5]) // 15
reduceAdd.call(obj2, 1, 2, 3, 4, 5) // 15

30. ¿Para qué se utiliza el método function.prototype.bind?


Bind devuelve una nueva función cuyo valor es el objeto especificado como primer parámetro. A diferencia de bind, call y apply llama inmediatamente a la función.

import React from 'react'

class MyComponent extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            value: ''
        }
        this.handleChange = this.handleChange.bind(this)
        //   handleChange   MyComponent
    }

    handleChange(e) {
        // 
    }

    render() {
        return ( < >
            <
            input type = {
                this.props.type
            }
            value = {
                this.state.value
            }
            onChange = {
                this.handleChange
            }
            /> </ >
        )
    }
}

31. ¿Qué es la programación funcional y qué características de JS nos permiten hablar de ella como lenguaje de programación funcional?


La programación funcional es un concepto de programación declarativa o un ejemplo (patrón) de cómo se crean las aplicaciones, cómo se utilizan las funciones que contienen expresiones que calculan valores sin cambiar los argumentos que se les pasan.

El objeto Array contiene los métodos de mapa, filtro y reducción, que son las funciones más famosas en el mundo de la programación funcional debido a su utilidad y también porque no modifican la matriz, lo que hace que estas funciones sean "limpias". JS también tiene un cierre y funciones de orden superior que son características de un lenguaje de programación funcional.

El método map devuelve una nueva matriz con resultados de devolución de llamada para cada elemento de la matriz:

const words = ['Functional', 'Procedural', 'Object-Oriented']

const wordsLength = words.map(word => word.length)

El método de filtro crea una nueva matriz con todos los elementos que satisfacen la condición especificada en la devolución de llamada:

const data = {
    {
        name: 'Mark',
        isRegistered: true
    } {
        name: 'Mary',
        isRegistered: false
    } {
        name: 'Mae',
        isRegistered: true
    }
}

const registeredUsers = data.filter(user => user.isRegistered)

El método reduce realiza una devolución de llamada una vez para cada elemento de la matriz, con la excepción de los vacíos, tomando cuatro argumentos: el valor inicial (o el valor de la devolución de llamada anterior), el valor del elemento actual, el índice actual y la matriz iterada:

const strs = ['I', ' ', 'am', ' ', 'Iron', ' ', 'Man']

const result = strs.reduce((acc, currentStr) => acc + str, '')

32. ¿Qué son las funciones de orden superior?


Una función de orden superior es una función que devuelve otra función o acepta otra función como argumento.

function higherOrderFunction(param, callback) {
    return callback(param)
}

33. ¿Por qué las funciones en JS se denominan objetos de primera clase?


Las funciones se denominan objetos de primera clase porque se procesan como cualquier otro valor en JS. Se pueden asignar a variables, ser una propiedad de un objeto (método), un elemento de una matriz, un argumento para otra función, el valor devuelto por la función. La única diferencia entre una función y cualquier otro valor en JS es que la función se puede ejecutar o invocar.

34. ¿Cómo implementaría el método Array.prototype.map?


function map(arr, mapCallback) {
    //   
    if (!Array.isArray(arr) || !arr.length || typeof mapCallback !== 'function') {
        return []
    } else {
        let result = []
        //         
        //       
        for (let i = 0, len = arr.length; i < len; i++) {
            result.push(mapCallback(arr[i], i, arr))
            //   mapCallback  result
        }
        return result
    }
}

El método map crea una nueva matriz con el resultado de llamar a la función especificada para cada elemento de la matriz.

35. ¿Cómo implementaría el método Array.prototype.filter?


function filter(arr, filterCallback) {
    //   
    if (!Array.isArray(arr) || !arr.length || typeof filterCallback !== 'function') {
        return []
    } else {
        let result = []
        // ...
        for (let i = 0, len = arr.length; i < len; i++) {
            //      
            if (filterCallback(arr[i], i, arr)) {
                //  ,  ,  result
                result.push(arr[i])
            }
        }
        return result
    }
}

El método de filtro crea una nueva matriz con todos los elementos que pasaron la prueba especificada en la función que se pasa.

36. ¿Cómo implementaría el método Array.prototype.reduce?


function reduce(arr, reduceCallbak, initialValue) {
    // ..
    if (!Array.isArray(arr) || !arr.length || typeof filterCallback !== 'function') {
        return []
    } else {
        //        initialValue, 
        let hasInitialValue = initialValue !== undefined
        let value = hasInitialValue ? initialValue : arr[0]
        //      initialValue

        //    ,   1,       initialValue,   0,    
        for (let i = hasInitialValue ? 0 : 1, len = arr.length; i < len; i++) {
            //         reduceCallback 
            value = reduceCallback(value, arr[i], i, arr)
        }
        return value
    }
}

El método reduce aplica la función reductora a cada elemento de la matriz (de izquierda a derecha), devolviendo un valor resultante.

37. ¿Qué es un objeto de argumentos?


Argumentos es una colección de argumentos pasados ​​a una función. Este es un objeto tipo matriz, tiene la propiedad de longitud, podemos acceder a un valor específico usando argumentos [i], pero no tiene los métodos forEach, reduce, filter y map. Le permite averiguar el número de parámetros de función.

Puede convertir argumentos en una matriz usando Array.prototype.slice:

Array.prototype.slice.call(arguments)

Recuerde: en las funciones de flecha, el objeto de argumentos no funciona.

function one() {
    return arguments
}
const two = function() {
    return arguments
}
const three = function three({
    return arguments
})
const four = () => arguments

four() // arguments is not defined

Llamar a las cuatro funciones da como resultado un Error de referencia: los argumentos no son un error definido. Este problema se puede resolver utilizando la declaración rest:

const four = (...args) => args

Esto colocará automáticamente todos los parámetros en una matriz.

38. ¿Cómo crear un objeto que no tiene un prototipo?


Esto se puede hacer usando Object.create:

const o1 = {}
console.log(o1.toString) // [object Object]

const o2 = Object.create(null) //      Object-create  -
//    -,   null
console.log(o2.toString) // o2.toString is not a function

39. ¿Por qué en el código presentado la variable b se vuelve global cuando se llama a la función?



function myFunc(){
    let a = b = 0
}
myFunc()

Esto sucede porque el operador de asignación ("=") tiene asociatividad diestra, es decir asigna valores de derecha a izquierda. Por lo tanto, el código toma la siguiente forma:

function myFunc(){
    let a = (b = 0)
}
myFunc()

Primero, el valor 0 se asigna a la variable "b", que no se declara. El motor JS lo hace global. El valor (0) devuelto por b = 0 se asigna a la variable local "a".

Este problema se puede resolver declarando primero las variables locales y luego asignándoles valores:

function myFunc(){
    let a, b
    a = b = 0
}
myFunc()

40. ¿Qué es ECMAScript?


ECMAScript es una especificación, un lenguaje de programación de secuencias de comandos estándar, es la base de JS, por lo que cualquier cambio en ECMAScript se refleja en JS.

La última versión de la especificación ECMA-262 se puede ver aquí .

41. ¿Qué cosas nuevas trajo ES6 o ECMAScript2015 a JS?


  • Funciones de flecha
  • Clases
  • Cadenas de plantilla.
  • Literales de objetos mejorados
  • Desestructuración (Desestructuración de objetos).
  • Promesas promesas).
  • Generadores
  • Módulos
  • Símbolo.
  • Proxies
  • Conjuntos
  • Las opciones por defecto.
  • Descansar y extender operadores.
  • Alcance del bloque (palabras clave "let" y "const").

42. ¿Cuál es la diferencia entre las palabras clave "var", "let" y "const"?


Las variables declaradas con la palabra clave var son globales. Esto significa que son accesibles desde cualquier parte del código:

function giveMeX(showX){
    if(showX){
        var x = 5
    }
    return x
}

console.log(giveMeX(false))
console.log(giveMeX(true))

El resultado del primer console.log será indefinido, el segundo - 5. Tenemos acceso a la variable "x" debido a su aparición en el ámbito global. El código del ejemplo anterior se interpreta de la siguiente manera:

function giveMeX(showX){
    var x //   undefined
    if(showX){
        x = 5
    }
    return x
}

El resultado del primer console.log no está definido, porque las variables declaradas a las que no se les asigna un valor no están definidas de manera predeterminada.

Las variables declaradas usando las palabras clave "let" y "const" tienen un alcance de bloque. Esto significa que solo están disponibles dentro del bloque ({}):

function giveMeX(showX){
    if(showX){
        let x = 5
    }
    return x
}

function giveMeY(showY){
    if(showY){
        let y = 5
    }
    return y
}

Llamar a estas funciones con el parámetro falso dará como resultado un error de Error de referencia, porque las variables "x" e "y" no son accesibles fuera del bloque y sus valores no se devuelven (no aparece).

La diferencia entre "let" y "const" es que en el primer caso podemos cambiar el valor de la variable, y en el segundo - no (constante). Al mismo tiempo, podemos cambiar el valor de la propiedad de un objeto declarado usando const, pero no la propiedad en sí (variable).

43. ¿Qué son las funciones de flecha (Funciones de flecha)?


La función de flecha es una forma relativamente nueva de crear funciones en JS. Las funciones de flecha son más rápidas y tienen una sintaxis más legible que las expresiones funcionales. En las funciones de flecha, se omite la palabra "función":

// ES5
var getCurrentDate = function(){
    return new Date()
}

// ES6
const getCurrentDate = () => new Date()

En una expresión funcional, usamos la palabra clave return para devolver un valor. En la función de flecha, no hacemos esto, ya que las funciones de flecha devuelven valores implícitamente, siempre que devolvamos una sola expresión o valor:

// ES5
function greet(name){
    return 'Hello ' + name + '!' 
}

// ES6
const greet = (name) => `Hello ${name}`
const greet2 = name = > `Hello ${name}`

También podemos pasar parámetros a las funciones de flecha. Si pasamos un parámetro, no tenemos que ponerlo entre paréntesis:

const getArgs = () => arguments

const getArgs2 = (...rest) => rest

Las funciones de flecha no tienen acceso al objeto de argumentos. Por lo tanto, llamar a la primera función dará como resultado un error. Para que los parámetros pasen a la función, podemos usar el operador rest.

const data = {
    result: 0
    nums: [1,2,3,4,5]
    computeResult(){
        // this    data
        const addAll = () => {
        //     this   
        return this.nums.reduce((total, cur) => total + cur, 0)
        }
    this.result = addAll()
    }
}

44. ¿Qué son las clases?


Las clases son una forma relativamente nueva de escribir funciones de constructor en JS. Este es el azúcar sintáctico para las funciones de constructor. Las clases se basan en los mismos prototipos y herencia del prototipo:

// ES5
function Person(firstName, lastName, age, address){
    this.firstName = firstName
    this.lastName = lastName
    this.age = age
    this.address = address
}

Person.self = function(){
    return this
}

Person.prototype.toString = function(){
    return '[object Person]'
}

Person.prototype.getFullName = function(){
    return this.firstName + ' ' + this.lastName
}

// ES6
class Person{
    constructor(firstName, lastName, age, address){
        this.firstName = firstName
        this.lastName = lastName
        this.age = age
        this.address = address
    }

    static self(){
        return this
    }

    toString(){
        return '[object Person]'
    }

    getFullName(){
        return `${this.firstName} ${this.lastName}`
    }
}

El método reemplaza y hereda de otra clase:

// ES5
Employee.prototype = Object.create(Person.prototype)

function Employee(firstName, lastName, age, address, jobTitle, yearStarted){
    Person.call(this, firstName, lastName, age, address)
    this.jobTitle = jobTitle
    this.yearStarted = yearStarted
}

Employee.prototype.describe = function(){
    return `I am ${this.getFullName()} and I have a position of #{this.jobTitle} and I started at ${this.yearStarted}}`
}

Employee.prototype.toString = function(){
    return '[object Employee]'
}

// ES6
class Employee extends Person{ //   Person
    constructor(firstName, lastName, age, address, jobTitle, yearStarted){
        super(firstName, lastName, age, address)
        this.jobTitle = jobTitle
        this.yearStarted = yearStarted
    }

    describe(){
       return `I am ${this.getFullName()} and I have a position of #{this.jobTitle} and I started at ${this.yearStarted}}` 
    }

    toString(){ //   toString  Person
        return '[object Employee]'
    }
}

¿Cómo aprender sobre el uso de prototipos?

class Something{ }

function AnotherSomething(){ }

const as = new AnotherSomething()
const s = new Something()

console.log(typeof Something) // function
console.log(typeof AnotherSomething) // function
console.log(as.toString()) // [object Object]
console.log(a.toString()) // [object Object]
console.log(as.toString === Object.prototype.toString)
console.log(a.toString === Object.prototype.toString)
//     true
// Object.prototype     
// Something  AnotherSomething   Object.prototype

45. ¿Qué son las plantillas literales?


Los literales de plantilla son una forma relativamente nueva de crear cadenas en JS. Los literales de plantilla se crean utilizando dobles comillas (``):

// ES5
var greet = 'Hi I\'m Mark'

// ES6
let greet = `Hi I'm Mark`

En los literales de plantilla, no necesitamos escapar de comillas simples.
// ES5
var lastWords = '\n'
    + ' I \n'
    + ' am \n'
    + 'Iron Man \n'

// ES6
let lastWords = `
    I
    am
    Iron Man
`

En ES6, no necesitamos usar la secuencia de escape "\ n" para alimentar la línea.

// ES5
function greet(name){
    return 'Hello ' + name + '!'
}

// ES6
function greet(name){
    return `Hello ${name}!`
}

En ES6, no necesitamos usar la concatenación de cadenas para combinar texto con una variable: podemos usar la expresión $ {expr} para obtener el valor de la variable.

46. ​​¿Qué es la desestructuración de objetos?


La desestructuración es una forma relativamente nueva de obtener (recuperar) los valores de un objeto o matriz.

Digamos que tenemos un objeto como este:

const employee = {
    firstName: 'Marko',
    lastName: 'Polo',
    position: 'Software Developer',
    yearHired: 2017
}

Anteriormente, para crear las propiedades de un objeto, creamos variables para cada propiedad. Fue muy aburrido y muy molesto:

var firstName = employee.firstName
var lastName = employee.lastName
var position = employee.position
var yearHired = employee.yearHired

Usar la desestructuración hace que el código sea más limpio y lleva menos tiempo. La sintaxis de desestructuración es la siguiente: encerramos las propiedades del objeto que queremos recibir entre llaves ({}), y si estamos hablando de una matriz, entre corchetes ([]):

let { firstName, lastName, position, yearHired } = employee

Para cambiar el nombre de la variable, use "propertyName: newName":

let { firstName: fName, lastName: lName, position, yearHired } = employee

Para asignar valores predeterminados a las variables, use "propertyName = 'defaultValue'":

let { firstName = 'Mark', lastName: lName, position, yearHired } = employee

47. ¿Qué son los módulos?


Los módulos le permiten combinar (usar) el código de diferentes archivos y evitar que tengamos que guardar todo el código en un archivo grande. Antes de que los módulos aparecieran en JS, había dos sistemas de módulos populares para soportar código:

  • CommonJS - Nodejs
  • AMD (AsyncronousModuleDefinition) - Navegadores

La sintaxis de los módulos es muy simple: utilizamos importar para importar funcionalidades o valores de otro archivo o archivos, y exportar para exportar.

Funcionalidad de exportación a otro archivo (denominado exportación):

// ES5 CommonJS - helpers.js
exports.isNull = function(val){
    return val === null
}

exports.isUndefined = function(val){
    return val === undefined
}

exports.isNullOrUndefined = function(val){
    return exports.isNull(val) || exports.isUndefined(val)
}

// ES6 
export function isNull(val){
    return val === null;
}

export function isUndefined(val) {
    return val === undefined;
}

export function isNullOrUndefined(val) {
    return isNull(val) || isUndefined(val);
}

Importar funcionalidad a otro archivo:

// ES5 CommonJS - index.js
const helpers = require('./helpers.js')
const isNull = helpers.isNull
const isUndefined = helpers.isUndefined
const isNullOrUndefined = helpers.isNullOrUndefined

//    
const { isNull, isUndefined, isNullOrUndefined } = require('./helpers.js')

// ES6 
import * as helpers from './helpers.js' // helpers -  

// 
import { isNull, isUndefined, isNullOrUndefined as isValid} from './helpers.js' //  "as"  

Exportación predeterminada:

// ES5 CommonJS - index.js
class Helpers {
    static isNull(val){
        return val === null
    }

    static isUndefined(val){
        return val === undefined
    }

    static isNullOrUndefined(val){
        return this.isNull(val) || this.isUndefined(val)
    }
}

module.exports = Helpers

// ES6 
class Helpers {
    static isNull(val){
        return val === null
    }

    static isUndefined(val){
        return val === undefined
    }

    static isNullOrUndefined(val){
        return this.isNull(val) || this.isUndefined(val)
    }
}

export default Helpers

Importar:

// ES5 CommonJS - index.js
const Helpers = require('./helpers.js')
console.log(Helpers.isNull(null))

// ES6 
import Helpers from './helpers.js'
console.log(Helpers.isNull(null))

Este es el uso básico de los módulos. No entré en detalles porque mi publicación ya es demasiado grande.

48. ¿Qué es un objeto Set?


El objeto Set le permite almacenar valores únicos, primitivas y referencias de objetos. Una vez más: solo se pueden agregar valores únicos a Set. Comprueba los valores almacenados en él utilizando el algoritmo SameZeroValue.

Se crea una instancia de Set utilizando el constructor Set. También podemos pasarle algunos valores al crear:

const set1 = new Set()
const set2 = new Set(['a','b','c','d','d','e']) //  "d"  

Podemos agregar valores a Set usando el método add. Como el método add es retornable, podemos usar una cadena de llamadas:

set2.add('f')
set2.add('g').add('h').add('i').add('j').add('k').add('k') //  "k"  

Podemos eliminar valores de Set usando el método delete:

set2.delete('k') // true
set2.delete('z') // false,    set2   

Podemos verificar una propiedad en Set usando el método has:

set2.has('a') // true
set2.has('z') // false

Para obtener la longitud de Set, use el método de tamaño:

set2.size // 10

El método claro borra Set:

set2.clear() // 

Podemos usar Set para eliminar valores duplicados en una matriz:

const nums = [1,2,3,4,5,6,6,7,8,8,5]
const uniqNums = [...new Set(nums)] // [1,2,3,4,5,6,7,8]

49. ¿Qué es una función de devolución de llamada?


La función de devolución de llamada es una función cuya llamada se pospone para el futuro (ocurre bajo ciertas condiciones, por ejemplo, cuando ocurre un evento).

const btnAdd = document.getElementById('btnAdd')

btnAdd.addEventListener('click', function clickCallback(e)){
    //   
}

En el ejemplo, estamos esperando un evento de "clic" en un elemento con el identificador "btnAdd". Al hacer clic, se llama a la función clickCallback. La función de devolución de llamada agrega alguna funcionalidad a los datos o eventos. Los métodos reducir, filtrar y asignar pasan una función de devolución de llamada como segundo argumento. Una buena analogía con la devolución de llamada es la siguiente situación: llama a alguien, él no responde, le deja un mensaje y espera a que vuelva a llamar. Una llamada o mensaje es un evento o un dato, y una devolución de llamada es la expectativa (anticipación) de una devolución de llamada.

50. ¿Qué son las promesas?


Las promesas son una forma de trabajar con código asincrónico en JS. Devuelven el resultado de una operación asincrónica. Se inventaron promesas para resolver el problema de las llamadas funciones infernales de devolución de llamada.

fs.readFile('somefile.txt', function(e, data){
    if(e){
        console.log(e)
    }
    console.log(data)
})

Los problemas con este enfoque comienzan cuando necesitamos agregar otra operación asincrónica a la primera (dentro de la primera), luego a otra, etc. Como resultado, obtenemos un código desordenado e ilegible:

fs.readFile('somefile.txt', function(e,data){
    // 
    fs.readFile('directory', function(e, files){
        // 
        fs.mkdir('directory', function(e){
            // 
        })
    })
})

Y así es como se ve con las promesas:

promReadFile('file/path')
.then(data => {
    return promReaddir('directory')
})
.then(data => {
    return promMkdir('directory')
})
.catch(e => {
    console.error(e)
})

Promise tiene cuatro condiciones:

  • Esperar es el estado inicial de una promesa. El resultado de la promesa es desconocido porque la operación no se ha completado.
  • Hecho: operación asincrónica completada, hay un resultado.
  • Rechazado: la operación asincrónica falló, hay una razón.
  • Completado: completado o rechazado.

El constructor Promise acepta resolver y rechazar como parámetros. En resolución, el resultado de la operación se registra, en rechazo, la razón de la falla de la operación. El resultado puede procesarse en el método .then, el error puede procesarse en el método .catch. El método .then también devuelve una promesa, por lo que podemos usar una cadena que consta de varios .then.

const myPromiseAsync = (...args) => {
    return new Promise((resolve, reject) => {
        doSomeAsync(...args, (error, data) => {
            if(error){
                reject(error)
            } else{
                resolve(data)
            }
        })
    })
}

myPromiseAsync()
.then(result => {
    console.log(result)
})
.catch(reason => {
    console.error(reason)
})

Podemos crear una función auxiliar para convertir una operación asincrónica de devolución de llamada a una promesa. Funcionará como util de Node.js ("promisificación"):

const toPromise = (asyncFuncWithCallback) => {
    return (...args) => {
        return new Promise((res, rej) => {
            asyncFuncWithCallback(...args, (e, result) => {
                return e ? rej(e) : res(result)
            })
        })
    }
}

const promiseReadFile = toPromise(fs.readFile)

promiseReadFile('file/path')
.then((data) => {
    console.log(data)
})
.catch(e => console.error(e))

Puede leer más sobre las promesas aquí y aquí .

51. ¿Qué es async / wait?


Async / await es una forma relativamente nueva de escribir código asincrónico (sin bloqueo) en JS. Están envueltos en una promesa. Hace que el código sea más legible y limpio que las promesas y las funciones de devolución de llamada. Sin embargo, para usar async / await, debe conocer bien las promesas.

// 
function callApi(){
    return fetch('url/to/api/endpoint')
    .then(resp => resp.json())
    .then(data => {
        //   
    }).catch(err => {
        //   
    })
}

// async/await
//     try/catch
async function callApi(){
    try{
        const resp = await fetch('url/to/api/endpoint')
        const data = await res.json()
        //   
    } catch(e){
        //   
    }
}

Recuerde: usar la palabra clave asíncrona antes de que una función la obligue a devolver una promesa:

const giveMeOne = async () = 1

giveMeOne()
.then((num) => {
    console.log(num) // 1
})

La palabra clave await solo se puede usar dentro de una función asincrónica. Usar wait dentro de otra función dará como resultado un error. Aguardar espera a que la expresión termine a la derecha para devolver su valor antes de la siguiente línea de código.

const giveMeOne = async() => 1

function getOne(){
    try{
        const num = await giveMeOne()
        console.log(num)
    } catch(e){
        console.log(e)
    }
}
// Uncaught SyntaxError: await is only valid in an async function

async function getTwo(){
    try{
        const num1 = await giveMeOne()
        const nm2 = await giveMeOne()
        return num1 + num2
    } catch(e){
        console.log(e)
    }
}

await getTwo() // 2

Lea más sobre async / wait aquí y aquí .

52. ¿Cuál es la diferencia entre un operador de propagación y un operador de reposo?


Las sentencias spread y rest tienen la misma sintaxis ("..."). La diferencia radica en el hecho de que con la ayuda de propagación transferimos o distribuimos los datos de la matriz a otros datos, y con la ayuda del resto obtenemos todos los parámetros de la función y los colocamos en la matriz (o extraemos algunos de los parámetros).

function add(a, b){
    return a + b
}

const nums = [5, 6]
const sum = add(...nums)
console.log(sum) // 11

En este ejemplo, usamos spread cuando llamamos a la función add con los datos de la matriz nums. El valor de la variable "a" será 5, b = 6, suma = 11.

function add(...rest){
    return rest.reduce((total, current) => total + current)
}

console.log(add(1, 2)) // 3
console.log(add(1, 2, 3, 4, 5)) // 15

Aquí llamamos a la función add con cualquier número de argumentos. Agregar devuelve la suma de estos argumentos.

const [first, ...others] = [1, 2, 3, 4, 5]
console.log(first) // 1
console.log(others) // [2, 3, 4, 5]

En este ejemplo, usamos rest para poner cualquier número de parámetros, excepto el primero, en la matriz de otros.

53. ¿Cuáles son los parámetros predeterminados?


Esta es una forma relativamente nueva de definir los valores de las variables predeterminadas.

// ES5
function add(a,b){
    a = a || 0
    b = b || 0
    return a + b
}

// ES6
function add(a = 0, b = 0){
    return a + b
}
//      "a"  "b" - ,    0
add(1) // 1

Puedes usar la desestructuración:

function getFirst([first, ...rest] = [0, 1]){
    return first
}

getFirst() // 0
getFirst([10,20,30]) // 10

function getArr({ nums } = { nums: [1,2,3,4] }){
    return nums
}

getArr // [1,2,3,4]
getArr({nums:[5,4,3,2,1]}) // [5,4,3,2,1]

Incluso podemos usar los parámetros predeterminados declarados en el mismo lugar:

function doSomethingWithValue(value = 'Hello World', callback = () => { console.log(value) }){
    callback()
}
doSomethingWithValue() // Hello World

54. ¿Qué es un contenedor de objetos (objetos de envoltura)?


Las primitivas cadena, número y booleano tienen propiedades y métodos, a pesar de que no son objetos:

let name = 'marko'

console.log(typeof name) // string
console.log(name.toUpperCase()) // MARKO

Nombre es una cadena (tipo primitivo) que no tiene propiedades ni métodos, pero cuando llamamos al método toUpperCase (), esto no conduce a un error, sino a "MARKO".

La razón de este comportamiento es que el nombre se convierte temporalmente en un objeto. Cada primitivo, excepto nulo e indefinido, tiene un objeto contenedor. Tales objetos son String, Number, Boolean, Symbol y BigInt. En nuestro caso, el código toma la siguiente forma:

console.log(new String(name).toUpperCase()) // MARKO

Un objeto temporal se descarta al finalizar el trabajo con una propiedad o método.

55. ¿Cuál es la diferencia entre la coerción implícita y explícita?


La conversión implícita es una forma de transmitir un valor a otro tipo sin nuestro conocimiento (participación).

Supongamos que tenemos lo siguiente:

console.log(1 + '6')
console.log(false + true)
console.log(6 * '2')

El resultado del primer console.log será 16. En otros idiomas, esto conduciría a un error, pero en JS 1 se convierte en una cadena y se concatena (adjunta) desde 6. No hicimos nada, la conversión se produjo automáticamente.

El resultado del segundo console.log será 1. False se convirtió a 0, verdadero a 1. 0 + 1 = 1.

El resultado del tercer console.log será 12. La línea 2 se convirtió a un número antes de multiplicar por 6.

La conversión explícita implica nuestra participación en convertir el valor a otro tipo:

console.log(1 + parseInt('6'))

En este ejemplo, usamos parseInt para convertir la cadena 6 en un número, luego sumamos los dos números y obtenemos 7.

56. ¿Qué es NaN? ¿Cómo verificar si el valor es NaN?


NaN o no un número (no un número) es el valor obtenido como resultado de realizar una operación numérica en un valor no numérico:

let a

console.log(parseInt('abc'))
console.log(parseInt(null))
console.log(parseInt(undefined))
console.log(parseInt(++a))
console.log(parseInt({} * 10))
console.log(parseInt('abc' - 2))
console.log(parseInt(0 / 0))
console.log(parseInt('10a' * 10))

JS tiene un método isNaN incorporado que le permite verificar si el valor es NaN, pero se comporta de manera bastante extraña:

console.log(isNaN()) // true
console.log(isNaN(undefined)) // true
console.log(isNaN({})) // true
console.log(isNaN(String('a'))) // true
console.log(isNaN(() => { })) // true

El resultado de todo console.log es verdadero, a pesar del hecho de que ninguno de los valores es NaN.

ES6 recomienda usar el método Number.isNaN para verificar si el valor es NaN. También podemos escribir una función auxiliar para resolver el problema de la "desigualdad de NaN por sí misma":

function checkIsNan(value){
    return value !== value
}

57. ¿Cómo verificar si un valor es una matriz?


Para hacer esto, use el método Array.isArray:

console.log(Array.isArray(5)) // false
console.log(Array.isArray('')) // false
console.log(Array.isArray()) // false
console.log(Array.isArray(null)) // false
console.log(Array.isArray( {length: 5 })) // false
console.log(Array.isArray([])) // true

Si el entorno en el que trabaja no es compatible con este método, puede usar el siguiente archivo polivinílico:

function isArray(value){
    return Object.prototype.toString.call(value) === '[object Array]'
}

58. ¿Cómo verificar que un número sea par, sin usar la división de módulo o la división con el resto (operador "%")?


Para resolver este problema, puede usar el operador "&" (binario y). El operador & compara operandos como valores binarios.

function isEven(num){
    if(num & 1){
        return false
    } else{
        return true
    }
}

0 en la notación binaria es 000
1 - esto es 001
2 - 010
3 - 011
4 - 100
5 - 101
6 - 110
7 - 111
, etc.



Console.log (5 y 1) devolverá 1. Primero, el operador & convierte ambos números a valores binarios, 5 se convierte en 101, 1 se convierte en 001. Luego se realiza una comparación a nivel de bits:



Compare 1 y 0, obtenemos 0.
Compare 0 y 0 , obtenemos 0.
Compara 1 y 1, obtenemos 1.
Convierte el valor binario en un entero, obtenemos 1.

Si esta información te parece demasiado complicada, podemos resolver el problema usando la función recursiva:

function isEven(num){
    if(num < 0 || num === 1) return false
    if(num == 0) return true
    return isEven(num - 2)
}

59. ¿Cómo determinar la presencia de una propiedad en un objeto?


hay tres maneras de hacer esto.

La primera forma es usar el operador in:

const o = {
    'prop': 'bwahahah',
    'prop2': 'hweasa'
}

console.log('prop' in o) // true
console.log('prop1' in o) // false

El segundo es usar el método hasOwnProperty:

console.log(o.hasOwnProperty('prop2')) // true
console.log(o.hasOwnProperty('prop1')) // false

El tercero es la notación de índice de la matriz:

console.log(o['prop']) // bwahahah
console.log(o['prop1']) // undefined

60. ¿Qué es AJAX?


AJAX o JavaScript asíncrono y XML es un conjunto de tecnologías interconectadas que le permiten trabajar con datos en modo asíncrono. Esto significa que podemos enviar datos al servidor y recibir datos del mismo sin volver a cargar la página web.

AJAX utiliza las siguientes tecnologías:
HTML: estructura de la página web.
CSS - estilos de página web.
JavaScript: comportamiento de la página y trabajo con el DOM.
API XMLHttpRequest: envío y recepción de datos desde el servidor.
PHP, Python, Nodejs: algún tipo de lenguaje de servidor.

61. ¿Cómo crear un objeto en JS?


Literal del objeto:

const o = {
    name: 'Mark',
    greeting(){
        return `Hi, I'm ${this.name}`
    }
}

o.greeting // Hi, I'm Mark

Función del constructor:

function Person(name){
    this.name = name
}

Person.prototype.greeting = function(){
    return `Hi, I'm ${this.name}`
}

const mark = new Person('Mark')

mark.greeting() // Hi, I'm Mark

Método Object.create:

const n = {
    greeting(){
        return `Hi, I'm ${this.name}`
    }
}

const o = Object.create(n)

o.name = 'Mark'

console.log(o.greeting) // Hi, I'm Mark

62. ¿Cuál es la diferencia entre los métodos Object.freeze y Object.seal?


La diferencia es que cuando usamos el método Object.freeze, no podemos cambiar o editar las propiedades del objeto, y cuando usamos Object.seal tenemos esa oportunidad.

63. ¿Cuál es la diferencia entre el operador in y el método hasOwnProperty?


La diferencia es que el operador "in" comprueba la presencia de una propiedad no solo en el objeto en sí, sino también en sus prototipos, y el método hasOwnProperty, solo en el objeto.

console.log('prop' in o) // true
console.log('toString' in o) // true

console.log(o.hasOwnProperty('prop')) // true
console.log(o.hasOwnProperty('toString')) // false

64. ¿Qué técnicas de trabajo con código asincrónico en JS conoces?


  • Devoluciones de llamada
  • Promesas promesas).
  • Asíncrono / espera.
  • Bibliotecas como async.js, blueprint, q, co.

65. ¿Cuál es la diferencia entre una función normal y una expresión funcional?


Digamos que tenemos lo siguiente:

hoistedFunc()
notHoistedFunc()

function hoistedFunc(){
    console.log('I am hoisted')
}

var notHoistedFunc = function(){
    console.log('I will not be hoisted!')
}

Una llamada a notHoistedFunc dará como resultado un error, pero una llamada a hoistedFunc no, porque hoistedFunc "aparece", se eleva al alcance global, pero notHoistedFunc no.

66. ¿Cómo llamar a una función en JS?


En JS, hay 4 formas de llamar a una función. La llamada define el valor de esto o el "propietario" de la función.

Llamada como una función. Si se llama a una función como método, constructor, o usando los métodos apply o call, entonces se llama como una función. El propietario de dicha función es el objeto de ventana:

function add(a,b){
    console.log(this)
    return a + b
}

add(1,5) // window, 6

const o = {
    method(callback){
        callback()
    }
}

o.method(function(){
    console.log(this) // window
})

Llamar como método. Cuando una función es una propiedad de un objeto, lo llamamos método. Cuando se llama a un método, este objeto se convierte en el objeto de este método:

const details = {
    name: 'Marko',
    getName(){
        return this.name
    }
}

details.getName() // Marko,  this   details

Llamada como constructor. Cuando se llama a una función usando la palabra clave "nueva", llamamos a esa función constructor. Esto crea un objeto vacío, que es el valor de esto:

function Employee(name, position, yearHired){
    //   ,   this
    // this = {}
    this.name = name
    this.position = position
    this.yearHired = yearHired
    //   Employee.prototype   this,    
}

const emp = new Employee('Marko Polo', 'Software Development', 2017)

Una llamada usando los métodos de solicitud o llamada. Usamos estos métodos cuando queremos determinar explícitamente el valor de esto o el propietario de una función:

const obj1 = {
    result: 0
}

const obj2 = {
    result: 0
}

function reduceAdd(){
    let result = 0
    for(let i = 0, len = arguments.length; i < len; i++){
        result += arguments[i]
    }
    this.result = result
}

reduceAdd.apply(obj1, [1,2,3,4,5]) //  this  obj1
reduceAdd.call(obj2, 1,2,3,4,5) //  this  obj2

67. ¿Qué es memorización o memorización?


La memorización es la técnica de crear una función que puede recordar resultados o valores calculados previamente. La ventaja de la memorización es que evitamos volver a ejecutar una función con los mismos argumentos. La desventaja es que nos vemos obligados a asignar memoria adicional para guardar los resultados.

68. ¿Cómo implementaría la función auxiliar de memorización?


function memoize(fn){
    const cache = {}
    return function(param){
        if(cache[param]){
            console.log('cached')
            return cache[param]
        } else{
            let result = fn(param)
            cache[param] = result
            console.log('not cached')
            return result
        }
    }
}

const toUpper = (str = '') => str.toUpperCase()

const toUpperMemoized = memoize(toUpper)

toUpperMemoized('abcdef')
toUpperMemoized('abcdef') //  

Implementamos la función de memorización con un argumento. Hagámoslo "multi-argumento":

const slice = Array.prototype.slice
function memoize(fn){
    const cache = {}
    return (...args) => {
        const params = slice.call(args)
        console.log(params)
        if(cache[params]){
            console.log('cached')
            return cache[params]
        } else{
            let result = fn(...args)
            cache[params] = result
            console.log('not cached')
            return result
        }
    }
}
const makeFullName = (fName, lName) => `${fName} ${lName}`
const reduceAdd = (numbers, startValue = 0) => numbers.reduce((total, cur) => total + cur, startValue)

const memoizedFullName = memoize(makeFullName)
const memoizeReduceAdd = memoize(reduceAdd)

memoizedFullName('Marko', 'Polo')
memoizedFullName('Marko', 'Polo') //  

memoizeReduceAdd([1,2,3,4],5)
memoizeReduceAdd([1,2,3,4],5) //  

69. ¿Por qué typeof null devuelve objeto? ¿Cómo verificar si un valor es nulo?


typeof null == 'objeto' siempre devolverá verdadero por razones históricas. Hubo una propuesta para corregir este error cambiando typeof null = 'object' a typeof null = 'null', pero fue rechazado en aras de mantener la compatibilidad con versiones anteriores (dicho cambio implicaría una gran cantidad de errores).

Para verificar si el valor es nulo, puede usar el operador de igualdad estricta (===):

function isNull(value){
    return value === null
}

70. ¿Para qué se utiliza la palabra clave "nueva"?


La palabra clave "nuevo" se utiliza en las funciones de constructor para crear un nuevo objeto (una nueva instancia de la clase).

Digamos que tenemos un código como este:

function Employee(name, position, yearHired){
    this.name = name
    this.position = position
    this.yearHired = yearHired
}

const emp = new Employee('Marko Polo', 'Software Development', 2017)

La palabra clave "nuevo" hace 4 cosas:

  1. Crea un objeto vacío.
  2. Vincula el valor de este a él.
  3. Una función hereda de functionName.prototype.
  4. Devuelve esto a menos que se especifique lo contrario.

Source: https://habr.com/ru/post/undefined/


All Articles