70 perguntas da entrevista sobre Javascript

Bom dia amigos

Espero que este artigo seja útil para desenvolvedores iniciantes e experientes.

Em perguntas que me pareciam mais difíceis do que outras, são fornecidas referências a literatura adicional.

Ficaria muito grato pelos comentários detalhados. Todos os comentários serão levados em consideração ao editar o artigo.

Então vamos.

70 perguntas da entrevista sobre Javascript


Perguntas:
1. Qual é a diferença entre nulo e indefinido?
2. Para que é utilizado o operador &&?
3. Para que é utilizado o operador "||"?
4. O uso do sinal de mais unário (+) é a maneira mais rápida de converter uma string em um número?
5. O que é um DOM?
6. O que é Propogação de Eventos?
7. O que é o Bubbling de Eventos?
8. O que é captura de eventos?
9. Qual é a diferença entre os métodos event.preventDefault () e event.stopPropagation ()?
10. Como aprender sobre o uso do método event.preventDefault ()?
11. Por que obj.someprop.x causa um erro?
12. O que é um destino de evento ou 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. Quais técnicas de trabalho com código assíncrono em JS você conhece?
65. Qual é a diferença entre uma função normal e uma expressão funcional?
66. Como chamar uma função em JS?
67. O que é memorização ou memorização?
68. Como você implementaria a função auxiliar de memorização?
69. Por que typeof null retorna objeto? Como verificar se um valor é nulo?
70. Para que é usada a palavra-chave “nova”?

1. Qual é a diferença entre nulo e indefinido?


Para começar, vamos falar sobre o que eles têm em comum.

Em primeiro lugar, eles pertencem a 7 "primitivas" JS (tipos primitivos):

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

Em segundo lugar, são valores falsos, ou seja, o resultado da conversão para um booleano usando Boolean () ou o operador "!!" é falso:

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

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

Ok, agora sobre as diferenças.

undefined é o valor padrão:
  • uma variável à qual nenhum valor foi atribuído, ou seja, uma variável declarada mas não inicializada;
  • uma função que não retorna nada explicitamente, por exemplo, console.log (1);
  • propriedade inexistente do objeto.

Nesses casos, o mecanismo JS define o valor como 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

null é o "valor sem valor". null é o valor atribuído explicitamente à variável. No exemplo abaixo, obtemos nulo quando o método fs.readFile funciona sem erros:

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

Ao comparar nulo e indefinido, obtemos true ao usar o operador "==" e false ao usar o operador "===". Sobre o motivo disso acontecer, veja abaixo.

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

2. Para que é utilizado o operador &&?


O operador && (lógico e) encontra e retorna o primeiro valor falso ou o último operando quando todos os valores forem verdadeiros. Ele usa um curto-circuito para evitar custos desnecessários:

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

Com a instrução if:

const router: Router = Router()

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

A mesma coisa com o operador &&:

const router: Router = Router()

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

3. Para que é utilizado o operador "||"?


O operador "||" (booleano ou) localiza e retorna o primeiro valor verdadeiro. Ele também usa um curto-circuito. Este operador foi usado para atribuir parâmetros padrão em funções antes que os parâmetros padrão fossem padronizados no ES6.

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

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

logName() // Mark

4. O uso do sinal de mais unário (+) é a maneira mais rápida de converter uma string em um número?


De acordo com o MDN , o operador + é realmente a maneira mais rápida de converter uma string em um número, pois não realiza nenhuma operação em um valor que seja um número.

5. O que é um DOM?


O DOM ou o Document Object Model é uma interface de programação de aplicativos (API) para trabalhar com documentos HTML e XML. Quando o navegador lê pela primeira vez ("analisa") o documento HTML, ele forma um objeto grande, um objeto realmente grande com base no documento - o DOM. O DOM é uma estrutura em árvore (árvore de documentos). O DOM é usado para interagir e alterar a estrutura do próprio DOM ou de seus elementos e nós individuais.

Digamos que temos 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>

O DOM deste HTML se parece com o seguinte:



Em JS, o DOM é representado por um objeto Document. O objeto Document possui um grande número de métodos para trabalhar com elementos, sua criação, modificação, exclusão etc.

6. O que é Propagação de Eventos?


Quando um evento ocorre em um elemento DOM, na verdade ocorre não apenas nele. O evento "se propaga" do objeto Window para o elemento que o chamou (event.target). Nesse caso, o evento permeia sequencialmente (afeta) todos os ancestrais do elemento de destino. Uma propagação de evento possui três estágios ou fases:
  1. Fase de imersão (captura, interceptação) - um evento ocorre no objeto Window e desce para o destino do evento por todos os seus ancestrais.
  2. A fase de destino é quando o evento atinge o elemento de destino.
  3. Fase ascendente - um evento surge do event.target, passa sequencialmente por todos os seus ancestrais e atinge o objeto Window.



Leia mais sobre a distribuição de eventos aqui e aqui .

7. O que é um pop-up de eventos?


Quando um evento ocorre em um elemento DOM, ele afeta não apenas esse elemento. Um evento "aparece" (como uma bolha de ar na água), passa do elemento que causou o evento (event.target) para o pai, e depois aumenta ainda mais, para o pai do pai do elemento, até atingir o objeto Window.

Digamos que temos essa marcação:

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

E 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')
    })
})

O método addEventListener possui um terceiro parâmetro opcional - useCapture. Quando seu valor é falso (o padrão), o evento começa com a fase de subida. Quando seu valor é verdadeiro, o evento começa com a fase de imersão (para os "ouvintes" de eventos anexados ao destino do evento, o evento está na fase de destino e não nas fases de imersão ou ascensão. Os eventos na fase de destino são acionados por todos os ouvintes no elemento na ordem em que foram registrados independentemente do parâmetro useCapture - aprox. Se clicarmos no elemento filho, o console exibirá: filho, pai, avô, html, documento, janela. Aqui está o que é um pop-up de evento.

8. O que é um evento de imersão?


Quando um evento ocorre em um elemento DOM, ele ocorre não apenas nele. Na fase de imersão, o evento desce do objeto Window para o destino do evento através de todos os seus ancestrais.

Marcação:

<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)
})

O método addEventListener possui um terceiro parâmetro opcional - useCapture. Quando seu valor é falso (o padrão), o evento começa com a fase de subida. Quando seu valor é verdadeiro, o evento começa com a fase de mergulho. Se clicarmos no elemento filho, veremos o seguinte no console: janela, documento, html, avô, pai, filho. Esta é a imersão do evento.

9. Qual é a diferença entre os métodos event.preventDefault () e event.stopPropagation ()?


O método event.preventDefault () desativa o comportamento padrão de um elemento. Se você usar esse método no elemento do formulário, ele impedirá o envio do formulário. Se você usá-lo no menu de contexto, o menu de contexto será desativado (esse método é frequentemente usado no teclado para redefinir o teclado, por exemplo, ao criar um player de música / vídeo ou editor de texto - aprox. O método event.stopPropagation () desativa a propagação do evento (sua subida ou imersão).

10. Como aprender sobre o uso do método event.preventDefault ()?


Para fazer isso, podemos usar a propriedade event.defaulPrevented, que retorna um booleano que serve como um indicador de aplicativo para o elemento do método event.preventDefault.

11. Por que obj.someprop.x causa um erro?



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

A resposta é óbvia: estamos tentando acessar a propriedade x da propriedade someprop, que é indefinida. obj .__ proto __.__ proto = null, portanto, o undefined é retornado e undefined não possui a propriedade x.

12. O que é um destino de evento ou elemento de destino (event.target)?


Em palavras simples, event.target é o elemento em que o evento ocorre ou o elemento que gerou o evento.

Temos a seguinte marcação:

<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>

E um JS tão simples:

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

Anexamos um "ouvinte" à div externa. No entanto, se clicarmos no botão, obteremos o layout desse botão no console. Isso permite concluir que o elemento que causou o evento é o próprio botão, e não as divs externas ou internas.

13. Qual é o objetivo atual do evento (event.currentTarget)?


Event.currentTarget é o elemento ao qual o ouvinte de evento está anexado.

Marcação semelhante:

<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>

E um JS ligeiramente modificado:

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

Anexamos o ouvinte à div externa. Onde quer que clicamos, seja um botão ou uma das divs internas, no console sempre obtemos o layout da div externa. Isso permite concluir que event.currentTarget é o elemento ao qual o ouvinte de evento está anexado.

14. Qual a diferença entre os operadores "==" e "==="?


A diferença entre o operador == (igualdade abstrata ou não estrita) e o operador === (igualdade estrita) é que o primeiro compara os valores após serem convertidos ou convertidos em um tipo (Coersion) e o segundo - sem essa conversão .

Vamos cavar mais fundo. E primeiro, vamos falar sobre a transformação.

Uma conversão é um processo de conversão de um valor para outro tipo, ou melhor, um processo de conversão de valores comparados para um tipo. Ao comparar, o operador "==" produz a chamada comparação implícita. O operador "==" executa algumas operações antes de comparar dois valores.

Suponha que comparemos x e y.

O algoritmo é o seguinte:

  1. Se x e y são do mesmo tipo, a comparação é realizada usando o 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.

Lembre-se: para converter um objeto em uma "primitiva", o método toPrimitive primeiro usa o método valueOf, depois o método toString.

Exemplos:



Todos os exemplos retornam true.

O primeiro exemplo é a primeira condição do algoritmo.
O segundo exemplo é a quarta condição.
O terceiro é o segundo.
O quarto é o sétimo.
Quinto - oitavo.
E o último é o décimo.



Se usarmos o operador "===", todos os exemplos, exceto o primeiro, retornarão falso, pois os valores nesses exemplos são de tipos diferentes.

15. Por que o resultado da comparação de dois objetos semelhantes é falso?


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

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

Em JS, objetos e primitivos são comparados de maneira diferente. As primitivas são comparadas por valor. Objetos - por referência ou endereço na memória em que a variável está armazenada. É por isso que o primeiro console.log retorna false e o segundo retorna true. As variáveis ​​“a” e “c” se referem ao mesmo objeto, enquanto as variáveis ​​“a” e “b” se referem a objetos diferentes com as mesmas propriedades e valores.

16. Para que o operador "!!" é usado?


O operador "!!" (dupla negação) leva o valor ao seu direito a um 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. Como escrever várias expressões em uma linha?


Para isso, podemos usar o operador "," (vírgula). Este operador "move" da esquerda para a direita e retorna o valor da última expressão ou operando.

let x = 5

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

function addFive(num) {
    return num + 5
}

Se imprimirmos o valor de x no console, obteremos 27. Primeiro, aumentamos o valor de x em um (x = 6). Em seguida, chamamos a função addFive () com o parâmetro 6, ao qual adicionamos 5 (x = 11). Depois disso, multiplicamos o valor de x por 2 (x = 22). Subtraia 5 (x = 17). E, finalmente, adicione 10 (x = 27).

18. O que é içar?


Aumento é um termo que descreve o aumento de uma variável ou função em um escopo global ou funcional.

Para entender o que é Hoisting, você precisa entender qual é o contexto de execução.

O contexto de execução é o ambiente em que o código é executado. O contexto de execução possui duas fases - a compilação e a própria execução.

Compilação. Nesta fase, expressões funcionais e variáveis ​​declaradas usando a palavra-chave “var” com o valor indefinido sobem para o topo do escopo global (ou funcional) (como se estivéssemos migrando para o início do nosso código. Isso explica por que podemos chamar funções antes que elas anúncios - aprox.

Atuação. Nesta fase, as variáveis ​​recebem valores e funções (ou métodos de objetos) são chamadas ou executadas.

Lembre-se: apenas expressões funcionais e variáveis ​​declaradas usando a palavra-chave “var” são geradas. Funções comuns e funções de seta, bem como variáveis ​​declaradas usando as palavras-chave “let” e “const”, não são geradas.

Suponha que tenhamos código como este:

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

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

var y

Ficamos indefinidos, 1 e 'Hello Mark!'.

Aqui está a aparência da fase de compilação:

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

var y //  undefined

//    

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

Após a conclusão da fase de compilação, a fase de execução começa quando as variáveis ​​recebem valores e as funções são chamadas.

Mais informações sobre o içamento podem ser encontradas aqui .

19. O que é um escopo?


Um escopo é um local em que (ou de onde) temos acesso a variáveis ​​ou funções. JS, temos três tipos de escopos: global, funcional e block (ES6).

Escopo global - variáveis ​​e funções declaradas no espaço para nome global têm um escopo global e são acessíveis de qualquer lugar no código.

//   
var g = 'global'

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

Escopo funcional (escopo de uma função) - variáveis, funções e parâmetros declarados dentro de uma função estão disponíveis apenas dentro dessa função.

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) //  

Escopo do bloco - variáveis ​​(declaradas usando as palavras-chave "let" e "const") dentro do bloco ({}) estão disponíveis apenas dentro dele.

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

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

Um escopo também é um conjunto de regras pelas quais uma variável é pesquisada. Se a variável não existir no escopo atual, sua pesquisa será realizada mais alto na visibilidade externa do escopo atual. Se não houver variável no escopo externo, sua pesquisa continuará no escopo global. Se uma variável for encontrada no escopo global, a pesquisa será interrompida; caso contrário, uma exceção será lançada. A pesquisa é realizada pelo mais próximo das áreas de visibilidade atuais e para com a localização da variável. Isso é chamado de cadeia de escopo.

//   
//    ->    ->   

//   
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. O que é um fechamento (Closures)?


Esta é provavelmente a pergunta mais difícil da lista. Vou tentar explicar como entendo o fechamento.

De fato, o fechamento é a capacidade de uma função criar, no momento da criação, memorizar referências a variáveis ​​e parâmetros localizados no escopo atual, no escopo da função pai, no escopo do pai da função pai e assim por diante no escopo global usando a cadeia de escopo. Normalmente, o escopo é determinado quando uma função é criada.

Os exemplos são uma ótima maneira de explicar o fechamento:

//   
var globalVar = 'abc'

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

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

Neste exemplo, quando declaramos uma função, o escopo global faz parte do fechamento.



A variável "globalVar" não importa na imagem, pois seu valor pode mudar dependendo de onde e quando a função será chamada. Mas no exemplo acima, globalVar terá o valor "abc".

Agora o exemplo é mais 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')



O resultado é "adivinhar o interior". A explicação é a seguinte: quando chamamos a função outerFunc e configuramos a variável "x" para o valor retornado pela função innerFunc, o parâmetro "outerParam" é igual a "outer". Apesar de atribuirmos a variável “outerVar” a “outer-2”, isso aconteceu depois de chamar a função outerFunc, que “conseguiu” encontrar o valor da variável “outerVar” na cadeia de escopo, esse valor era “outer”. Quando chamamos "x", que se refere a innerFunc, o valor de "innerParam" é "inner" porque passamos esse valor como parâmetro ao chamar "x". globalVar tem um valor de "palpite" porque atribuímos esse valor antes de chamar "x".

Um exemplo de um mal-entendido de um 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 não funciona conforme o esperado. Declarar uma variável usando a palavra-chave var torna essa variável global. Após adicionar funções à matriz arrFunc, o valor da variável global "i" se torna "5". Portanto, quando chamamos a função, ela retorna o valor da variável global "i". Um fechamento armazena uma referência a uma variável, não seu valor no momento da criação. Esse problema pode ser resolvido usando IIFE ou declarando uma variável usando a palavra-chave "let".

Leia mais sobre o fechamento aqui e aqui .

21. Quais valores em JS são falsos?


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

False são valores cuja conversão para um valor booleano é false.

22. Como verificar se um valor é falso?


Use a função booleana ou o operador "!!" (duas vezes não).

23. Para que serve a diretiva estrita de uso?


"Use strict" é uma diretiva ES5 que força todo o nosso código ou o código de uma função individual a ser executado no modo estrito. O modo estrito introduz algumas restrições na escrita de código, evitando erros nos estágios iniciais.

Aqui estão as limitações do modo estrito.

Você não pode atribuir valores ou acessar variáveis ​​não declaradas:

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

É proibido atribuir valores globais a variáveis ​​somente leitura ou somente gravação:

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

Você não pode excluir a propriedade "undeletable" de um objeto:

'use strict'
const obj = {}

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

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

É proibida a duplicação de parâmetros:

'use strict'

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

Você não pode criar funções usando a função eval:

'use strict'

eval('var x = 1')

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

O valor padrão para isso é indefinido:

'use strict'

function showMeThis() {
    return this
}

showMeThis() // undefined

... etc.

24. O que isso significa?


Isso geralmente se refere ao valor do objeto que está atualmente executando ou chamando a função. "No momento" significa que o valor disso varia dependendo do contexto de execução, onde usamos isso.

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

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

Nesse caso, o método getName retorna this.name e refere-se a carDetails, o objeto no qual getName é executado, que é seu "proprietário".

Adicione três linhas após console.log:

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

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

O segundo console.log produz um Ford Ranger, e isso é estranho. A razão para esse comportamento é que o "proprietário" de getCarName é o objeto de janela. Variáveis ​​declaradas com a palavra-chave var no escopo global são gravadas nas propriedades do objeto window. isso no escopo global refere-se ao objeto de janela (a menos que seja um modo estrito).

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

Neste exemplo, this e window se referem ao mesmo objeto.

Uma maneira de resolver esse problema é usar a chamada ou aplicar métodos:

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

Chame e aplique como primeiro argumento um objeto que será o valor disso dentro da função.

No IIFE, funções criadas no escopo global, funções anônimas e funções internas dos métodos de um objeto, o valor padrão para isso é o objeto de janela.

(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
})

Existem duas maneiras de obter o Marko Polo.

Primeiro, podemos armazenar o valor disso em uma variável:

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

Em segundo lugar, podemos usar a função de seta:

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

As funções de seta não possuem esse autovalor. Eles copiam o significado disso do ambiente lexical externo.

25. O que é um protótipo de um objeto?


Em poucas palavras, um protótipo é um plano (diagrama ou projeto) de um objeto. É usado como um substituto para as propriedades e métodos existentes neste objeto. É também uma das maneiras de trocar propriedades e funcionalidades entre objetos. Este é o conceito básico de herança de protótipo em JS.

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

Embora o objeto “o” não tenha a propriedade toString, o acesso a essa propriedade não causa um erro. Se uma propriedade específica não estiver no objeto, sua pesquisa será realizada primeiro no protótipo do objeto, depois no protótipo do protótipo do objeto e assim por diante até que a propriedade seja encontrada. Isso é chamado de cadeia de protótipos. No topo da cadeia de protótipos está o Object.prototype.

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

Leia mais sobre protótipos e herança aqui e aqui .

26. O que é IIFE?


IIFE ou expressão de função chamada imediatamente é uma função que é chamada ou executada imediatamente após a criação ou declaração. Para criar o IIFE, é necessário agrupar a função entre parênteses (o operador de agrupamento), transformá-la em uma expressão e chamá-la usando outros parênteses. É assim: (function () {}) ().

(function( ) { }( ))

(function( ) { })( )

(function named(params) { })( )

(( ) => { })

(function(global) { })(window)

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

Todos esses exemplos são válidos. O penúltimo exemplo mostra que podemos passar parâmetros para o IIFE. O último exemplo mostra que podemos armazenar o resultado do IIFE em uma variável.

O melhor uso do IIFE é executar funções de configuração de inicialização e evitar conflitos de nomes com outras variáveis ​​no escopo global (poluição do espaço para nome global). Nós damos um exemplo.

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

Temos um link para a biblioteca somelibrary.js que fornece algumas funções globais que podemos usar em nosso código, mas existem dois métodos nessa biblioteca, createGraph e drawGraph, que não usamos porque contêm erros. E queremos implementar essas funções por conta própria.

Uma maneira de resolver esse problema é alterar a estrutura de nossos scripts:

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

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

Assim, redefinimos os métodos fornecidos pela biblioteca.

A segunda maneira é mudar os nomes de nossas funções:

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

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

A terceira maneira é usar o IIFE:

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

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

Neste exemplo, criamos uma variável de utilitário que contém o resultado IIFE, que retorna um objeto que contém os métodos createGraph e drawGraph.

Aqui está outro problema que pode ser resolvido com o 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)
    })
}

Suponha que tenhamos um elemento ul com uma classe de grupo de lista contendo 5 elementos filhos li. E queremos exibir o valor "i" no console ao clicar em um "li" separado. No entanto, em vez disso, o console sempre exibe 5. A falha é toda a falha.

Uma solução é 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)
}

A razão pela qual esse código funciona conforme o esperado é porque o IIFE cria um novo escopo a cada iteração e escrevemos o valor "i" no currentIndex.

27. Para que é usado o método Function.prototype.apply?


Apply é usado para ligar um objeto específico ao valor this da função chamada.

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

function getMessage() {
    return this.message
}

getMessage.apply(details) // Hello World!

Este método é semelhante ao Function.prototype.call. A única diferença é que, na aplicação, os argumentos são transmitidos como uma matriz.

const person = {
    name: 'Marko Polo'
}

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

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

28. Para que é usado o método function.prototype.call?


A chamada é usada para ligar um objeto específico ao valor dessa função que está sendo chamada.

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

function getMessage() {
    return this.message;
}

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

Este método é semelhante ao Function.prototype.apply. A diferença é que os argumentos de chamada são passados ​​separados por vírgulas.

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

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

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

29. Qual é a diferença entre chamar e aplicar métodos?


A diferença entre chamar e aplicar é como passamos argumentos na função chamada. Na aplicação, os argumentos são transmitidos como uma matriz, em chamada, separada por vírgulas.

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 que é usado o método function.prototype.bind?


Bind retorna uma nova função cujo valor é o objeto especificado como o primeiro parâmetro. Ao contrário do bind, chame e aplique imediatamente chame a função.

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. O que é programação funcional e quais recursos do JS nos permitem falar sobre isso como uma linguagem de programação funcional?


A programação funcional é um conceito de programação declarativa ou um exemplo (padrão) de como os aplicativos são construídos, como as funções que contêm expressões que calculam valores sem alterar os argumentos que são transmitidos a eles são usadas.

O objeto Array contém os métodos mapear, filtrar e reduzir, que são as funções mais famosas do mundo da programação funcional devido à sua utilidade e também porque não modificam a matriz, o que torna essas funções "limpas". JS também possui funções de fechamento e ordem superior, características de uma linguagem de programação funcional.

O método map retorna uma nova matriz com resultados de retorno de chamada para cada elemento da matriz:

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

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

O método filter cria uma nova matriz com todos os elementos que atendem à condição especificada no retorno de chamada:

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

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

O método reduzir executa um retorno de chamada uma vez para cada elemento da matriz, com exceção dos vazios, com quatro argumentos: o valor inicial (ou o valor do retorno de chamada anterior), o valor do elemento atual, o índice atual e a matriz iterada:

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

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

32. O que são funções de ordem superior?


Uma função de ordem superior é uma função que retorna outra função ou aceita outra função como argumento.

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

33. Por que as funções no JS são chamadas de Objetos de Primeira Classe?


As funções são chamadas de objetos de primeira classe porque são processadas como qualquer outro valor em JS. Eles podem ser atribuídos a variáveis, ser propriedade de um objeto (método), um elemento de uma matriz, um argumento para outra função, o valor retornado pela função. A única diferença entre uma função e qualquer outro valor em JS é que a função pode ser executada ou chamada.

34. Como você implementaria o 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
    }
}

O método map cria uma nova matriz com o resultado da chamada da função especificada para cada elemento da matriz.

35. Como você implementaria o 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
    }
}

O método filter cria uma nova matriz com todos os elementos que passaram no teste especificado na função que está sendo aprovada.

36. Como você implementaria o 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
    }
}

O método de redução aplica a função redutora a cada elemento da matriz (da esquerda para a direita), retornando um valor resultante.

37. O que é um objeto de argumentos?


Arguments é uma coleção de argumentos passados ​​para uma função. Este é um objeto parecido com um array, possui a propriedade length, podemos acessar um valor específico usando argumentos [i], mas não possui os métodos forEach, reduzir, filtrar e mapear. Ele permite que você descubra o número de parâmetros de função.

Você pode converter argumentos em uma matriz usando Array.prototype.slice:

Array.prototype.slice.call(arguments)

Lembre-se: nas funções de seta, o objeto de argumentos não funciona.

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

four() // arguments is not defined

Chamar as quatro funções resulta em um erro ReferenceError: argumentos não está definido. Esse problema pode ser resolvido usando a instrução rest:

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

Isso colocará automaticamente todos os parâmetros em uma matriz.

38. Como criar um objeto que não possui um protótipo?


Isso pode ser feito 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 que no código apresentado a variável b se torna global quando a função é chamada?



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

Isso acontece porque o operador de atribuição ("=") possui associatividade com a mão direita, ou seja, atribui valores da direita para a esquerda. Portanto, o código assume o seguinte formato:

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

Primeiro, o valor 0 é atribuído à variável "b", que não é declarada. O mecanismo JS o torna global. O valor (0) retornado por b = 0 é então atribuído à variável local “a”.

Esse problema pode ser resolvido primeiro declarando variáveis ​​locais e depois atribuindo valores a elas:

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

40. O que é o ECMAScript?


ECMAScript é uma especificação, uma linguagem de programação de script padrão, é a base do JS, portanto, quaisquer alterações no ECMAScript são refletidas no JS.

A versão mais recente da especificação ECMA-262 pode ser vista aqui .

41. Que novidades o ES6 ou o ECMAScript2015 trouxeram para o JS?


  • Funções de seta
  • Aulas
  • Sequências de modelo.
  • Literais de objetos aprimorados
  • Reestruturação (Reestruturação de Objetos).
  • Promessas (promessas).
  • Geradores
  • Módulos
  • Símbolo.
  • Proxies
  • Sets.
  • As opções padrão.
  • Descansar e espalhar operadores.
  • Bloqueie o escopo (palavras-chave "let" e "const").

42. Qual é a diferença entre as palavras-chave "var", "let" e "const"?


Variáveis ​​declaradas usando a palavra-chave var são globais. Isso significa que eles são acessíveis de qualquer lugar no código:

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

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

O resultado do primeiro console.log será indefinido, o segundo - 5. Temos acesso à variável “x” devido ao seu surgimento no escopo global. O código do exemplo acima é interpretado da seguinte maneira:

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

O resultado do primeiro console.log é indefinido, porque variáveis ​​declaradas que não recebem um valor são indefinidas por padrão.

Variáveis ​​declaradas usando as palavras-chave "let" e "const" têm um escopo de bloco. Isso significa que eles estão disponíveis apenas dentro do bloco ({}):

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

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

A chamada dessas funções com o parâmetro false resultará em um erro ReferenceError, porque as variáveis ​​"x" e "y" não estão acessíveis fora do bloco e seus valores não são retornados (não são exibidos).

A diferença entre “let” e “const” é que, no primeiro caso, podemos alterar o valor da variável, e no segundo - não (constante). Ao mesmo tempo, podemos alterar o valor da propriedade de um objeto declarado usando const, mas não a própria propriedade (variável).

43. O que são funções de seta (funções de seta)?


A função de seta é uma maneira relativamente nova de criar funções em JS. As funções de seta são mais rápidas e têm sintaxe mais legível do que expressões funcionais. Nas funções de seta, a palavra "função" é omitida:

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

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

Em uma expressão funcional, usamos a palavra-chave return para retornar um valor. Na função de seta, não fazemos isso, pois as funções de seta retornam implicitamente valores, desde que retornemos uma única expressão ou valor:

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

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

Também podemos passar parâmetros para funções de seta. Se passarmos um parâmetro, não precisaremos colocá-lo entre parênteses:

const getArgs = () => arguments

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

As funções de seta não têm acesso ao objeto de argumentos. Portanto, chamar a primeira função resultará em um erro. Para obter os parâmetros passados ​​para a função, podemos usar o operador resto.

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. O que são aulas?


Classes são uma maneira relativamente nova de escrever funções construtoras em JS. Este é um açúcar sintático para funções de construtor. As classes são baseadas nos mesmos protótipos e herança de protótipo:

// 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}`
    }
}

Substituições e herança de método de outra classe:

// 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]'
    }
}

Como aprender sobre o uso de protótipos?

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. O que são literais de modelo?


Literais de modelo são uma maneira relativamente nova de criar strings em JS. Literais de modelo são criados usando backticks duplos (``):

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

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

Nos literais de modelo, não precisamos escapar de aspas simples.
// ES5
var lastWords = '\n'
    + ' I \n'
    + ' am \n'
    + 'Iron Man \n'

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

No ES6, não precisamos usar a sequência de escape "\ n" para alimentar a linha.

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

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

No ES6, não precisamos usar a concatenação de strings para combinar texto com uma variável: podemos usar a expressão $ {expr} para obter o valor da variável.

46. ​​O que é destruição de objetos?


A reestruturação é uma maneira relativamente nova de obter (recuperar) os valores de um objeto ou matriz.

Digamos que temos um objeto como este:

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

Anteriormente, para criar as propriedades de um objeto, criamos variáveis ​​para cada propriedade. Era muito chato e muito chato:

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

O uso da desestruturação torna o código mais limpo e leva menos tempo. A sintaxe de desestruturação é a seguinte: colocamos as propriedades do objeto que queremos receber entre colchetes ({}) e, se estamos falando de uma matriz, entre colchetes ([]):

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

Para alterar o nome da variável, use "propertyName: newName":

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

Para atribuir valores padrão a variáveis, use "propertyName = 'defaultValue'":

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

47. O que são módulos?


Os módulos permitem combinar (usar) o código de arquivos diferentes e evitar que tenhamos que manter todo o código em um arquivo grande. Antes de os módulos aparecerem no JS, havia dois sistemas de módulos populares para suporte ao código:

  • CommonJS - Nodejs
  • AMD (AsyncronousModuleDefinition) - Navegadores

A sintaxe dos módulos é muito simples: usamos import para importar funcionalidades ou valores de outro arquivo ou arquivos e exportamos para exportar.

Exportar funcionalidade para outro arquivo (denominado export):

// 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 funcionalidade para outro arquivo:

// 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"  

Exportação padrão:

// 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))

Esse é o uso básico dos módulos. Não entrei em detalhes, porque minha postagem já é muito grande.

48. O que é um objeto Set?


O objeto Set permite armazenar valores exclusivos, primitivas e referências a objetos. Mais uma vez: somente valores exclusivos podem ser adicionados ao conjunto. Ele verifica os valores armazenados usando o algoritmo SameZeroValue.

Uma instância de Set é criada usando o construtor Set. Também podemos passar alguns valores ao criar:

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

Podemos adicionar valores a Set usando o método add. Como o método add é retornável, podemos usar uma cadeia de chamadas:

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

Podemos excluir valores de Set usando o método delete:

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

Podemos verificar uma propriedade em Set usando o método has:

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

Para obter o comprimento de Set, use o método size

set2.size // 10

O método clear limpa Set:

set2.clear() // 

Podemos usar Set para remover valores duplicados em uma 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. O que é uma função de retorno de chamada?


A função de retorno de chamada é uma função cuja chamada é adiada para o futuro (ocorre sob certas condições, por exemplo, quando ocorre um evento).

const btnAdd = document.getElementById('btnAdd')

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

No exemplo, estamos aguardando um evento "click" em um elemento com o identificador "btnAdd". Por clique, a função clickCallback é chamada. A função de retorno de chamada adiciona alguma funcionalidade aos dados ou evento. Os métodos de redução, filtro e mapa recebem uma função de retorno de chamada como o segundo argumento. Uma boa analogia ao retorno de chamada é a seguinte situação: você liga para alguém, ele não responde, deixa uma mensagem para ele e espera que ele ligue novamente. Uma chamada ou mensagem é um evento ou dados e um retorno de chamada é a expectativa (antecipação) de uma chamada de retorno.

50. O que são promessas?


As promessas são uma maneira de trabalhar com código assíncrono em JS. Eles retornam o resultado de uma operação assíncrona. Promessas foram inventadas para resolver o problema das chamadas funções infernais de retorno de chamada.

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

Os problemas com essa abordagem começam quando precisamos adicionar outra operação assíncrona à primeira (dentro da primeira), depois outra etc. Como resultado, obtemos um código confuso e ilegível:

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

E aqui está o que parece com promessas:

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

A promessa tem quatro condições:

  • Esperar é o estado inicial de uma promessa. O resultado da promessa é desconhecido porque a operação não foi concluída.
  • Concluído - Operação assíncrona concluída, há um resultado.
  • Rejeitado - a operação assíncrona falhou, existe um motivo.
  • Concluído - concluído ou rejeitado.

O construtor Promise aceita resolver e rejeitar como parâmetros. Na resolução, o resultado da operação é registrado, em rejeição, o motivo da falha da operação. O resultado pode ser processado no método .then, o erro pode ser processado no método .catch. O método .then também retorna uma promessa, para que possamos usar uma cadeia que consiste em vários .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 criar uma função auxiliar para converter uma operação assíncrona de retorno de chamada em promessa. Funcionará como util do Node.js ("promisification"):

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))

Você pode ler mais sobre promessas aqui e aqui .

51. O que é assíncrono / aguardar?


Assíncrono / espera é uma maneira relativamente nova de escrever código assíncrono (sem bloqueio) em JS. Eles estão envolvidos em uma promessa. Torna o código mais legível e limpo do que as funções prometidas e de retorno de chamada. No entanto, para usar o assíncrono / espera, você precisa conhecer bem as promessas.

// 
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){
        //   
    }
}

Lembre-se: usar a palavra-chave assíncrona antes que uma função o force a retornar uma promessa:

const giveMeOne = async () = 1

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

A palavra-chave wait pode ser usada apenas dentro de uma função assíncrona. Usar aguardar dentro de outra função resultará em um erro. Aguardar aguarda que a expressão termine à direita para retornar seu valor antes da próxima linha 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

Leia mais sobre assíncrono / aguarde aqui e aqui .

52. Qual a diferença entre um operador de spread e um operador de descanso?


As instruções spread e rest têm a mesma sintaxe ("..."). A diferença está no fato de que, com a ajuda da propagação, transferimos ou espalhamos os dados da matriz para outros dados, e com a ajuda do resto obtemos todos os parâmetros da função e os colocamos na matriz (ou extraímos alguns dos parâmetros).

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

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

Neste exemplo, usamos spread quando chamamos a função add com os dados da matriz nums. O valor da variável “a” será 5, b = 6, soma = 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

Aqui chamamos a função add com qualquer número de argumentos. Adicionar retorna a soma desses argumentos.

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

Neste exemplo, usamos rest para colocar qualquer número de parâmetros, exceto o primeiro, na matriz outros.

53. Quais são os parâmetros padrão?


Essa é uma maneira relativamente nova de definir valores de variáveis ​​padrão.

// 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

Você pode usar a desestruturação:

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]

Podemos até usar os parâmetros padrão declarados no mesmo local:

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

54. O que é um wrapper de objeto (Wrapper Objects)?


As primitivas string, number e boolean têm propriedades e métodos, apesar de não serem objetos:

let name = 'marko'

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

Nome é uma string (tipo primitivo) que não possui propriedades e métodos, mas quando chamamos o método toUpperCase (), isso leva não a um erro, mas a "MARKO".

A razão para esse comportamento é que o nome é temporariamente convertido em um objeto. Cada primitivo, exceto nulo e indefinido, possui um objeto wrapper. Esses objetos são String, Number, Boolean, Symbol e BigInt. No nosso caso, o código assume o seguinte formato:

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

Um objeto temporário é descartado após a conclusão do trabalho com uma propriedade ou método.

55. Qual é a diferença entre coerção implícita e explícita?


A conversão implícita é uma maneira de lançar um valor para outro tipo sem o nosso conhecimento (participação).

Suponha que tenhamos o seguinte:

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

O resultado do primeiro console.log será 16. Em outros idiomas, isso levaria a um erro, mas no JS 1 ele é convertido em uma sequência e concatenado (anexado) a partir de 6. Não fizemos nada, a conversão ocorreu automaticamente.

O resultado do segundo console.log será 1. Falso foi convertido em 0, verdadeiro em 1. 0 + 1 = 1.

O resultado do terceiro console.log será 12. A linha 2 foi convertida em um número antes da multiplicação por 6. A

conversão explícita implica nossa participação em convertendo o valor para outro tipo:

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

Neste exemplo, usamos parseInt para converter a string 6 em um número, depois somamos os dois números e obtemos 7.

56. O que é NaN? Como verificar se o valor é NaN?


NaN ou Não Um número (não um número) é o valor obtido como resultado da execução de uma operação numérica em um valor não 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))

O JS possui um método isNaN interno que permite verificar se o valor é NaN, mas se comporta de maneira estranha:

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

O resultado de todo o console.log é verdadeiro, apesar de nenhum dos valores ser NaN.

O ES6 recomenda o uso do método Number.isNaN para verificar se o valor é NaN. Também podemos escrever uma função auxiliar para resolver o problema da “desigualdade de NaN por si só”:

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

57. Como verificar se um valor é uma matriz?


Para fazer isso, use o 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

Se o ambiente em que você trabalha não suporta esse método, você pode usar o seguinte polyfile:

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

58. Como verificar se um número é par, sem usar a divisão de módulo ou divisão com o restante (operador "%")?


Para resolver esse problema, você pode usar o operador "&" (binário e). O operador & compara operandos como valores binários.

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

0 na notação binária é 000
1 - é 001
2 - 010
3 - 011
4 - 100
5 - 101
6 - 110
7 - 111
, etc.



Console.log (5 e 1) retornará 1. Primeiro, o operador & converte os dois números em valores binários, 5 se transforma em 101, 1 se transforma em 001. Em seguida, é feita uma comparação bit a bit:



Compare 1 e 0, obtemos 0.
Compare 0 e 0 , obtemos 0.
Compare 1 e 1, obtemos 1.
Converta o valor binário em um número inteiro, obtemos 1.

Se essa informação parecer muito complicada para você, podemos resolver o problema usando a função recursiva:

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

59. Como determinar a presença de uma propriedade em um objeto?


Existem três formas de fazer isso.

A primeira maneira é usar o operador in:

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

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

O segundo é usar o método hasOwnProperty:

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

A terceira é a notação de índice da matriz:

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

60. O que é o AJAX?


AJAX ou JavaScript e XML assíncrono é um conjunto de tecnologias interconectadas que permitem trabalhar com dados no modo assíncrono. Isso significa que podemos enviar dados para o servidor e receber dados dele sem recarregar a página da web.

O AJAX usa as seguintes tecnologias:
HTML - estrutura da página da web.
CSS - estilos de página da web.
JavaScript - comportamento da página e trabalho com o DOM.
API XMLHttpRequest - enviando e recebendo dados do servidor.
PHP, Python, Nodejs - algum tipo de linguagem de servidor.

61. Como criar um objeto em JS?


Literal do objeto:

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

o.greeting // Hi, I'm Mark

Função construtora:

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. Qual é a diferença entre os métodos Object.freeze e Object.seal?


A diferença é que, ao usar o método Object.freeze, não podemos alterar ou editar as propriedades do objeto e, ao usar o Object.seal, temos essa oportunidade.

63. Qual é a diferença entre o operador in e o método hasOwnProperty?


A diferença é que o operador "in" verifica a presença de uma propriedade não apenas no próprio objeto, mas também em seus protótipos e no método hasOwnProperty - apenas no 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. Quais técnicas de trabalho com código assíncrono em JS você conhece?


  • Retornos de chamada
  • Promessas (promessas).
  • Assíncrono / aguardar.
  • Bibliotecas como async.js, blueprint, q, co.

65. Qual é a diferença entre uma função normal e uma expressão funcional?


Digamos que temos o seguinte:

hoistedFunc()
notHoistedFunc()

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

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

Uma chamada para notHoistedFunc resultará em um erro, mas uma chamada para hoistedFunc não, porque hoistedFunc "aparece", sobe para o escopo global, mas notHoistedFunc não.

66. Como chamar uma função em JS?


Em JS, existem 4 maneiras de chamar uma função. A chamada define o valor deste ou o "proprietário" da função.

Chame como uma função. Se uma função é chamada como um método, construtor ou usando os métodos apply ou call, é chamada como uma função. O proprietário dessa função é o objeto de janela:

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
})

Chame como um método. Quando uma função é uma propriedade de um objeto, chamamos de método. Quando um método é chamado, este objeto se torna o objeto deste método:

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

details.getName() // Marko,  this   details

Chame como construtor. Quando uma função é chamada usando a palavra-chave "new", chamamos essa função de construtor. Isso cria um objeto vazio, que é o valor disso:

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)

Uma chamada usando os métodos apply ou call. Usamos esses métodos quando queremos determinar explicitamente o valor disso ou do proprietário de uma função:

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. O que é memorização ou memorização?


Memoização é a técnica de criação de uma função que pode lembrar resultados ou valores calculados anteriormente. A vantagem da memorização é que evitamos re-executar uma função com os mesmos argumentos. A desvantagem é que somos forçados a alocar memória adicional para salvar os resultados.

68. Como você implementaria a função auxiliar de memorização?


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 a função de memorização com um argumento. Vamos torná-lo "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 que typeof null retorna objeto? Como verificar se um valor é nulo?


typeof null == 'objeto' sempre retornará verdadeiro por razões históricas. Havia uma proposta para corrigir esse erro alterando typeof null = 'object' para typeof null = 'null', mas foi rejeitado com o objetivo de manter a compatibilidade com versões anteriores (essa alteração implicaria um grande número de erros).

Para verificar se o valor é nulo, você pode usar o operador de igualdade estrita (===):

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

70. Para que é usada a palavra-chave “nova”?


A palavra-chave "new" é usada nas funções do construtor para criar um novo objeto (uma nova instância da classe).

Digamos que temos um 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)

A palavra-chave "novo" faz 4 coisas:

  1. Cria um objeto vazio.
  2. Vincula esse valor a ele.
  3. Uma função herda de functionName.prototype.
  4. Retorna isso, a menos que seja especificado de outra forma.

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


All Articles