Análise de perguntas no estande hh.ru em # HolyJS18

Tentamos fazer algo interessante e incomum para você. Eu realmente espero que tenhamos sucesso. Não queríamos deixar você sem resposta e sem explicação. Vamos acertar.


Para começar, quero lembrá-lo de como a competição foi realizada. Foram quatro rodadas de 15 perguntas sobre JS, uma rodada fora de competição de 15 perguntas sobre o React e uma final de 10 perguntas.


imagem


Sob o corte - a análise das tarefas das 4 primeiras rodadas.


Esta é a segunda parte de nossa análise.
Reaja às perguntas aqui


Como todos nós fizemos isso? Decidimos que precisamos gerar cerca de 80 a 90 perguntas para que haja um estoque para escolher. Depois disso, dividimos tudo em tópicos:


  • eventos do navegador
  • várias APIs (matriz, conjunto, define propriedade, etc.),
  • atenção
  • trabalhar com números fracionários
  • elevação
  • laço de evento
  • conversão de tipo
  • typeof
  • lógico (com lógico AND e OR)

Depois disso, as perguntas foram distribuídas em quatro rodadas. Tentamos tornar todos os passeios iguais em complexidade, por isso fizemos várias visitas passando nesses testes e determinando onde as perguntas são mais fáceis, onde é mais difícil e substituímos as questões pendentes por outras mais adequadas. E fizemos o mesmo número de perguntas sobre um tópico específico em cada rodada. Como resultado, verificou-se que em diferentes turnês havia perguntas semelhantes, mas não as mesmas.


Por causa disso, não parece muito conveniente classificar os passeios, porque haverá muitas explicações duplicadas, sugiro analisá-las por tópico. Vamos começar com o mais simples.


Perguntas para atenção:


O que será exibido no console?


console.log(0,1 + 0,2); a) 0.30000000000000004 b) 0.3 c) 2 d) 0 1 2 

Resposta + análise

d) 0 1 2
Aqui fica entre os números, e não . se você formatar a pergunta assim:
console.log(0, 1 + 0, 2); tudo ficará claro


O que será exibido no console?


 (() => { 'use strict'; a = null + undefined; console.log(a); })(); a) 0 b) NaN c) null d)  

Resposta + análise

d) erro
a não é criado como uma variável (não é uma declaração de variável), aqui há uma expressão de atribuição implícita para this.a que muitas vezes pode não ser o que você espera, porque uma variável global window.a será criada no modo estrito, isso é proibido.


O que será exibido no console?


 let foo = function bar() { return 123; }; console.log( typeof bar() ); a) 'function' b) 'number' c) 'undefined' d)  

Resposta + análise

d) erro
Esta é uma expressão funcional (expressão) - o nome da função nesse caso é local para a função. Para chamar uma função, você precisa chamar foo , não bar . Se fosse uma declaração, a resposta seria number .


Perguntas sobre como trabalhar com números fracionários:


O que será exibido no console?


 console.log(0.1 ** 2); a) 0.2 b) 0.01 c) 0.010000000000000002 d) NaN 

A resposta

c) 0,010000000000000002


O que será exibido no console?


 console.log(0.1 + 0.2); a) 0.30000000000000004 b) 0.3 c) 2 d) NaN 

Resposta + análise

a) 0.30000000000000004
** - este é um análogo do Math.pow ao quadrado 0.1 - deve sair 0.01 , mas em JS (como em muitos outros idiomas) há um problema conhecido com a precisão das operações ao trabalhar com números de ponto flutuante . Será 0.010000000000000002 Isso se deve ao fato de que no sistema binário uma fração infinita é obtida, porque exatamente 64 bits são sempre alocados para um número em JS - todos os números são sempre ponto flutuante de precisão dupla. O mesmo acontece quando adicionado.


Passamos às perguntas um pouco mais complicadas.


Eventos no navegador:


Existe um manipulador de eventos no elemento .. Quais valores dentro desse manipulador sempre serão os mesmos?


 elem.onclick = function(event) { } a) event.target  event.currentTarget b) event.target  this c) event.currentTarget  this d)     

Resposta + análise

c) event.currentTarget e isso
this - sempre apontará para um elemento
currentTarget - o elemento no qual o evento trava
target - o elemento no qual o evento ocorreu


O que esse código produzirá ao clicar em uma div?


 div.onclick = function() { console.log(1) }; div.onclick = function() { console.log(2) }; div.addEventListener('click', function() { console.log(3) }); a) 1 b) 1 3 c) 2 3 d) 3 

Resposta + análise

c) 2 3
O onclick adicionará o manipulador console.log(1) , mas na próxima linha nós o trituraremos com uma nova função e apenas o console.log(2) permanecerá. onclick é uma propriedade DOM; é sempre uma
Os eventos funcionarão na ordem em que estão suspensos; primeiro 2 e 3 serão exibidos.
Se addEventListener várias vezes, cada um deles funcionaria, porque manipuladores adicionam eventos à fila.


Seção de perguntas sobre várias APIs


defineProperty:


O que esse código produzirá?


 (() => { const obj = { key: 1 }; Object.defineProperty(obj, 'key', { enumerable: false, configurable: false, writable: false, value: 2 }); console.log(obj.key); obj.key = 3; console.log(obj.key); })(); a) 1, 2 b) 2, 2 c) 2, 3 d)  

A resposta

b) 2, 2


O que esse código produzirá?


 (() => { 'use strict'; const obj = { key: 1 }; Object.defineProperty(obj, 'key', { enumerable: false, configurable: false, writable: false, value: 2 }); console.log(obj.key); obj.key = 3; console.log(obj.key); })(); a) 1, 2 b) 2, 2 c) 2, 3 d) 2,  

A resposta

d) 2, erro


O que esse código produzirá?


 (() => { const obj = { key: 1 }; Object.defineProperty(obj, 'key', { enumerable: false, configurable: false, writable: true, value: 2 }); console.log(obj.key); obj.key = 3; console.log(obj.key); })(); a) 1, 2 b) 2, 2 c) 2, 3 d)  

Resposta + análise

c) 2, 3
Em todas as perguntas acima, o conhecimento do método defineProperty é defineProperty e defineProperty mais especificamente, configurações writable . Se estiver definido como false é proibido alterar os valores da chave transmitida pelo segundo parâmetro em defineProperty . A única diferença é que, sem o modo estrito - use strict mecanismo fingirá que está tudo bem, mas não alterará o valor, e haverá um erro no modo estrito.


incremento:


O que esse código produzirá?


 let x = 5; console.log(x++); a) 5 b) 6 c) '5++' d)  

A resposta

a) 5


O que esse código produzirá?


 const a = 5; console.log(a++); a) 5 b) 6 c) '5++' d)  

A resposta

d) erro
Ao usar o formulário de incriminação postfix, o valor é retornado antes do aumento.
E com prefixo depois, ou seja, console.log(++5) imprimiria 6
const não pode ser substituído; Number é um primitivo, quando incrementado, a variável será substituída pelo novo valor e ocorrerá um erro.


Conjunto:


O que esse código produzirá?


 const a = [...new Set([1, 1, 2, , 3, , 4, 5, 5])]; console.log(a); a) [1, 1, 2, , 3, , 4, 5, 5] b) [1, 2, undefined, 3, 4, 5] c) [1, 1, 2, undefined, 3, undefined, 4, 5, 5] d)  

A resposta

b) [1, 2, indefinido, 3, 4, 5]


O que esse código produzirá?


 let set = new Set([10, '10', new Number(10), 1e1, 0xA]); console.log(set.size); a) 5 b) 3 c) 2 d) 1 

A resposta

b) 3


O que esse código produzirá?


 let obj = {}; let set = new Set([obj, obj, {}, {}, {...{}}, {...obj}]); console.log(set.size); a) 6 b) 5 c) 2 d) 1 

A resposta

b) 5
Set é um conjunto; por definição, não pode haver valores idênticos. A questão é como esses valores são comparados. Primitivas são comparadas por valor e objetos por referência.
Ele próprio não cita tipos de dados e pode armazenar valores de qualquer tipo 1e1 e 0xA - será convertido em um sistema decimal e obter 10 .
E novos objetos nem sempre são iguais: console.log({} == {}) retornará false pois os objetos serão criados de uma nova maneira em diferentes locais da memória e seus links não serão iguais.


O que esse código produzirá?


 console.log(Infinity / Infinity); a) NaN b) 1 c) Error d) Infinity 

A resposta

a) NaN
É impossível dividir o infinito em infinito e subtrair o infinito do infinito, pois do ponto de vista matemático, obtém-se incerteza, o mesmo acontecerá quando o Infinity multiplicado e 0 erros não causarem operações matemáticas - haverá NaN


Perguntas sobre Spread:


O que esse código produzirá?


 const a = { ...{ a: 1, b: 2, c: 3 }, ...{ a: 2, c: 4, d: 8 } }; console.log(a); a) { a: 2, b: 2, c: 4, d: 8 } c) { a: 1, b: 2, c: 3, d: 8 } c) { a: 1, b: 2, c: 3, a: 2, c: 4, d: 8 } d)  

A resposta

a) {a: 2, b: 2, c: 4, d: 8}


O que esse código produzirá?


 const a = [...[1, 2], ...[[3, 4]], ...[5, 6]]; console.log(a); a) [1, 2, 3, 4, 5, 6] b) [1, 2, [3, 4], 5, 6] c) [[1, 2], [[3, 4]], 5, 6] e)  

Resposta + análise

b) [1, 2, [3, 4], 5, 6]
Spread operador Spread serve para analisar um objeto ou matriz em partes. Ele pega valores da entidade depois de ... e os copia para o criado. Vale a pena notar que, para uma matriz e um objeto, ele abre para 1 nível, ou seja ...[[1]] retornará uma matriz com um elemento, não o próprio elemento. Não pode haver valores duplicados nos objetos; portanto, os valores divulgados após serão substituídos pelos que foram divulgados anteriormente. Isso pode ser usado para especificar configurações padrão.


 const fn = (actualProps) => ({ ...defaultProps, ...actualProps }) 

Todos os valores padrão serão substituídos pelos valores passados, se houver.


O que esse código produzirá?


 console.log(parseInt(' -10,3   ')); a) -10,3 b) -10 c) TypeError d) NaN 

Resposta + análise

b) -10
Descrição exaustiva com MDN :
Se a função parseInt encontrar um caractere que não seja um número no sistema numérico especificado, ela pulará esse e todos os caracteres subseqüentes (mesmo que sejam adequados) e retornará um número inteiro convertido da parte da sequência que antecede esse caractere. parseInt corta a parte fracionária de um número. Espaços no início e no final de uma linha são permitidos.


O que esse código produzirá?


 const t = { a: 6, b: 7 }; const p = new Proxy(t, { get() { return 12; }, }); console.log(pa); pa = 18; console.log(pa); console.log(ta); a)  b) 12 18 18 c) 12 18 6 d) 12 12 18 e) 6 18 6 

Resposta + análise

d) 12 12 18
Proxy intercepta todas as chamadas para o objeto. Nesse caso, procuramos apenas o método get e sempre retornamos 12 independentemente do campo do objeto que estamos acessando. Nesse caso, não tocamos em set e, ao acessar o proxy, o valor no objeto será substituído.


matrizes:


O que esse código produzirá?


 let arr = []; arr[1] = 1; arr[5] = 10; console.log(arr.length); a) 1 b) 5 c) 6 d) 10 

A resposta

c) 6


O que esse código produzirá?


 let arr = new Array(3); console.log(arr[1]); a) undefined b) 1 c) 3 d)  

Resposta + análise

a) indefinido
Quando criamos uma Array com um argumento numérico, isso significa o comprimento da matriz. Uma matriz é criada vazia, todos os valores são undefined . O mesmo acontece se você criar acesso a um campo de matriz inexistente. Vale a pena notar que, se você passar um número para Array , uma matriz com esse elemento será retornada, ou seja, Array('a') retornará ['a']


operações lógicas && , || , == , etc .:


O que esse código produzirá?


 console.log([] && 'foo' && undefined && true && false); a) [] b) 'foo' c) undefined d) true 

A resposta

c) indefinido


O que esse código produzirá?


 console.log(0 || 1 && 2 || 3); a) 0 b) 1 c) 2 d) 3 

A resposta

c) 2


O que esse código produzirá?


 console.log(0 || '' || 2 || undefined || true || false); a) 0 b) false c) 2 d) true 

A resposta

c) 2


O que esse código produzirá?


 console.log(2 && '1' && null && undefined && true && false); a) 2 b) false c) undefined d) null 

A resposta

d) nulo


O que esse código produzirá?


 console.log([] && {} || null && 100 || ''); a) true b) 100 c) '' d) {} 

Resposta + análise

d) {}
Uma matriz vazia [] é true como um objeto vazio {} .
A cadeia vazia '' , null e undefined é false
Lógico ou || - retorna o operando esquerdo, se verdadeiro, caso contrário, retorna o operando direito.
Lógico e && - retorna o operando esquerdo, se for falso, caso contrário, retorna o operando direito.


Às vezes, isso pode ser encontrado no código, antes do aparecimento dos parâmetros padrão, eles costumavam escrever assim - se não houver parâmetros na função, usamos os parâmetros padrão:


 function f(userParams) { var params = userParams || defaultParams; } 

Agora o React geralmente verifica se a condição é verdadeira, e renderizamos algo:


 { isDivVisible && <div>bla-bla</div> } 

comparação de matriz:


O que esse código produzirá?


 const arrayFoo = [1, 2, 3, 4]; const arrayBaz = [1, 2, 3, 4]; console.log(arrayFoo == arrayBaz && arrayFoo == arrayBaz); a) false b) true c) undefined d)  

A resposta

a) falso


O que esse código produzirá?


 console.log([null, 0, -0].map(x => 0 <= x)); a) [false, true, false] b) [false, true, true] c) [false, false, false] d) [true, true, true] 

A resposta

d) [verdadeiro, verdadeiro, verdadeiro]


O que esse código produzirá?


 const arrayFoo = [1, 2, 3, 4]; const arrayBaz = [1, 2, 3, 4]; console.log(arrayFoo >= arrayBaz && arrayFoo <= arrayBaz); a) true b) false c) undefined d)  

A resposta

a) verdadeiro


O que esse código produzirá?


 const foo = [1, 2, 3, 4]; const baz = '1,2,3,4'; console.log(foo >= baz && foo <= baz); a) false b) true c) undefined d)   

Resposta + análise

b) verdadeiro
Quando == comparar por referência.
durante a operação >, >=, <, <= operandos são convertidos em primitivas e o método valueOf é chamado no arrayFoo , que deve retornar o valor primitivo arrayFoo , mas retorna uma referência à mesma matriz. Em seguida, uma conversão para um valor primitivo ocorre chamando o método toString , que, por sua vez, retornará uma representação de string da matriz na forma de "1,2,3,4", compara as duas matrizes lexicograficamente e retorna true


O que esse código produzirá?


 console.log(+0 == -0); console.log(+0 === -0); console.log(Object.is(+0, -0)); a) true, false, false b) true, true, false c) false, true, true d) false, false. false 

Resposta + análise

b) verdadeiro, verdadeiro, falso
Explicação abrangente com MDN :
O comportamento desse método (falando sobre Object.is ) não é o mesmo que o operador === . O operador === (assim como o operador == ) considera os valores numéricos -0 e +0 iguais e o valor Number.NaN não Number.NaN igual a si próprio.


Perguntas sobre içamento:


O que esse código produzirá?


 console.log(str); const str = 'HeadHunter'; a) 'HeadHunter' b) undefined c)  

A resposta

c) erro


O que esse código produzirá?


 var arrayFunction = []; for (let i = 0; i <= 10; i++) { arrayFunction.push(() => i); } console.log(arrayFunction[3]()); a) 4 b) 0 c) 11 d) 3 

A resposta

d) 3


O que esse código produzirá?


 console.log(str); var str = 'HeadHunter'; a) 'HeadHunter' b) undefined c) null c)   

A resposta

b) indefinido


O que esse código produzirá?


 console.log(foo); var foo; foo = foo ? 1 : 0; console.log(foo); a)  b) undefined 0 c) '' 1 d) 0 0 

A resposta

b) indefinido 0


Uma chamada de função funcionará?


 getCompanyName(); function getCompanyName() { return 'HeadHunter'; } a)  b) ,     . c)  

A resposta

a) sim


O que esse código produzirá?


 var arrayFunction = []; for (var i = 0; i <= 10; i++) { arrayFunction.push(() => i); } console.log(arrayFunction[3]()); a) 4 b) 0 c) 11 d) 3 

Resposta + análise

c) 11


As declarações de função são exibidas, mas nenhuma expressão.
var aparece, mas até a inicialização não ser undefined .
let e const não aparecem e têm escopo em um bloco, ou seja, limitado a {} .


Para que o loop funcione corretamente com var você precisa usar um fechamento, o valor será salvo nele.
(Essa costumava ser uma tarefa clássica para entrevistas, mas agora deixamos)


 var arrayFunction = []; for (var i = 0; i <= 10; i++) { (function(i) { arrayFunction.push(() => i); })(i); } console.log(arrayFunction[3]()); 

O que esse código produzirá?


 console.log(true + false); a) true b) false c) 1 d) 0 

Resposta + análise

c) 1
Nenhum dos operadores é uma sequência, + leva a um número. Acontece 1 + 0


O que esse código produzirá?


 console.log([] - 100 + ![]); a) false b) '-100' c) -100 d) NaN 

Resposta + análise

c) -100
A matriz é convertida em uma string; depois disso, por causa de - convertemos em um número, resulta em -100 , depois convertemos a matriz em false , e este é 0


O que esse código produzirá?


 console.log([[], []] + 1); a) 1 b) '1' c) ',1' d) NaN 

Resposta + análise

c) ', 1'
toString no objeto, enquanto toString também será chamado em todos os elementos da matriz. [].toString retornará uma string vazia '' . Acontece que , + 1 - a resposta ,1 .


O que esse código produzirá?


 console.log([] + 100 + 5); a) 105 b) '1005' c) 1005 d) NaN 

Resposta + análise

b) '1005'
A matriz é redutível a uma sequência e, em seguida, a concatenação já ocorre.


O que esse código produzirá?


 console.log(1 + { a: 3 } + '2'); a) 6 b) '1[object Object]2' c) 3 d) NaN 

Resposta + análise

b) '1 [objeto Objeto] 2'
Converta em uma string - é apenas concatenação.


O que esse código produzirá?


 console.log(10.toString() + 10 + 0x1); a) '10101' b) 21 c) '10100x1' d)  

Resposta + análise

d) erro
Para um número, um ponto . significa o início da parte fracionária, esperamos um número lá - haverá um erro.
Para que este exemplo funcione bem, você precisa escrever 10..toString()


O que esse código produzirá?


 console.log(5 + false - null + true); a) '0true' b) NaN c) 6 d)   

Resposta + análise

c) 6
Aqui tudo é reduzido a um número, verifica-se 5 + 0 - 0 + 1


O que esse código produzirá?


 console.log(true + NaN + false); a) true b) NaN c) false d) 1 

Resposta + análise

b) NaN
Reduzimos tudo para um número, ao adicionar números com NaN - obtemos NaN


O que esse código produzirá?


 console.log('0x1' + '1' - '1e1'); a) 17 b) 7 c) '0x111e1' d) NaN 

Resposta + análise

b) 7
Já existem linhas após a primeira concatenação, obtemos: '0x11' - '1e1' . Por causa do sinal - trazemos tudo para um número.
0x11 - O número hexadecimal em decimal é 17 .
1e1 - a forma exponencial é igual a 1 * 10 ** 1 - ou seja, apenas 10 .


typeof:


O que esse código produzirá?


 let foo = () => { return null; }; console.log( typeof typeof foo ); a) 'function' b) 'string' c) 'null' d)  

A resposta

b) 'string'


O que esse código produzirá?


 typeof function() {}.prototype; a) 'function' b) 'object' c) 'undefined' d)  

Resposta + análise

b) 'objeto'
typeof sempre retorna uma string, tem uma prioridade mais baixa do que chamar a função; portanto, a função é executada primeiro e typeof é aplicado ao resultado retornado a ela. Objetos de função herdam de Function.prototype. Speck determina claramente que este é um objeto.


loop de evento:


Vamos começar com 2 perguntas sobre promessas.


O que esse código produzirá?


 Promise.reject() .then(() => console.log(1), () => console.log(2)) .then(() => console.log(3), () => console.log(4)); a) 1 4 b) 1 3 c) 2 3 d) 2 4 

A resposta

c) 2 3


O que esse código produzirá?


 Promise.reject('foo') .then(() => Promise.resolve('bar'), () => {}) .then((a) => {console.log(a)}) a) foo b) bar c) undefined d)  

Resposta + análise

c) indefinido
Promise.reject - retorna uma promessa em um estado rejeitado.
Deve-se lembrar que then em then necessários 2 parâmetros, onReject chamada onReject e onReject . Se ocorrer um erro antes disso, obteremos o retorno de chamada onReject . Se não houver nenhum erro, então entraremos em onFulfill . E não esqueça que () => {} retorna não um objeto vazio, mas undefined , para retornar um objeto vazio, você precisa escrever assim: () => ({})


a ordem das tarefas.


O que esse código produzirá?


 async function get1() { return 1; } function get2() { return 2; } (async () => { console.log(await get1()); })(); console.log(get2()); a) 1,2 b) 2,1 c) 1 d) 2 

A resposta

b) 2.1


O que esse código produzirá?


 setTimeout(() => {console.log('in timeout')}); Promise.resolve() .then(() => {console.log('in promise')}); console.log('after'); a) in timeout, in promise, after b) after, in promise, in timeout c) after, in timeout, in promise d) in timeout, after, in promise 

A resposta

b) depois, em promessa, em timeout


O que esse código produzirá?


 let __promise = new Promise((res, rej) => { setTimeout(res, 1000); }); async function test(i) { await __promise; console.log(i); } test(1); test(2); a) 1, 2 b) 2, 1 c) 1 d) 2 

A resposta

a) 1, 2


O que esse código produzirá?


 console.log('FUS'); setTimeout(() => {console.log('RO')}) Promise.resolve('DAH!').then(x => console.log(x)); a FUS RO DAH! b) FUS DAH! RO c) RO FUS DAH! d) DAH! RO FUS 

A resposta

b) FUS DAH! RO


O que esse código produzirá?


 console.log(1); setTimeout(() => console.log('setTimeout'), 0); console.log(2); Promise.resolve().then(() => console.log('promise1 resolved')); console.log(3); a) 1, 2, 3, 'setTimeout', 'promise1 resolved' b) 1, 'setTimeout', 2, 'promise1 resolved', 3 c) 1, 2, 3, 'promise1 resolved', 'setTimeout' d) 1, 2, 'promise1 resolved', 3, 'setTimeout' 

Resposta + análise

c) 1, 2, 3, 'promessa1 resolvida', 'setTimeout'
Primeiro, todas as chamadas síncronas são acionadas; depois, quando a pilha de chamadas está vazia, é chamado o que está na fila (tarefas assíncronas). Primeiro, microtask - promessas e mutation observer . No final da tarefa atual, todas as microtask são executadas. Em conexão com essa microtask, você pode bloquear o loop de eventos; após a conclusão da tarefa, o navegador é processado. Depois disso, as tarefas de macro - os intervalos são executados.
Este é um exemplo muito simplificado, em mais detalhes eu aconselho você a ver o discurso de Mikhail Bashurov


E a última pergunta promessa vs aguarda


O que esse código produzirá?


 const p = Promise.resolve(); (async () => { await p; console.log('1'); })(); p.then(() => console.log('2')) .then(() => console.log('3')); 

a) 1 2 3
b) 2 1 3
c) 2 3 1
d) 3 2 1


Resposta + análise

c) 2 3 1


De acordo com as especificações, as promessas adicionadas até then devem primeiro ser executadas e somente depois disso você precisa continuar
execução de uma função assíncrona. Speck . Para uma compreensão mais detalhada sobre por que isso ocorre, aconselho a ler um excelente artigo na v8.dev

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


All Articles