17 perguntas de Javascript para entrevistas
undefined: valor primitivo utilizado quando uma variável não teve valor atribuído
null: valor primitivo que representa a ausência intencional de um valor de objeto
Antes de entender as diferenças entre undefined e null devemos entender as semelhanças entre eles.
- Eles pertencem aos 7 tipos primitivos do JavaScript .
let primitiveTypes = ['string','number','null','undefined','boolean','symbol','bigint']
- Eles são valores falsos. Valores avaliados como
falsoao convertê-lo em booleano, usando:Boolean(value)ou!!value.
console.log('!!null: ',!!null) //logs falseconsole.log('!!undefined: ',!!undefined) //logs falseconsole.log('Boolean(null): ',Boolean(null)) //logs falseconsole.log('Boolean(undefined): ',Boolean(undefined)) //logs false
Ok, vamos falar sobre as diferenças:
undefinedé o valor padrão de uma variável que não recebeu um valor específico. Ou uma função que não tem valor de retorno explícito ex.console.log(1). Ou uma propriedade que não existe em um objeto. Exemplo:
let _thisIsUndefinedconsole.log('_thisIsUndefined', _thisIsUndefined) //logs undefinedconst doNothing = () => {}console.log(doNothing()) //logs undefinedconst someObj = {a : "ay",b : "bee",c : "si"}console.log(someObj["d"]) //logs undefined
nullé "um valor que não representa nada", mas que teve uma atribuição!nullé um valor que foi definido explicitamente para uma variável.
Neste exemplo, obtemos um valor de null quando o método fs.readFile não gera um erro (por baixo dos panos, alguém atribuiu o valor null a variável).
fs.readFile('path/to/file', (e,data) => {console.log(e) //it logs null when no error occurredif(e){console.log(e)}console.log(data)})
Ao compararmos null e undefined obtermos true ao usar == (estranho né?)
e obtemos false ao usar ===. Vou explicar a diferença em breve.
console.log(null == undefined) // logs trueconsole.log(null === undefined) // logs false
O operador && ou Logical AND encontra a primeira expressão falsa em seus operandos e a retorna e, se não encontrar nenhuma expressão falsa, retorna a última expressão.
console.log(false && 1 && []) //logs falseconsole.log(" " && true && 5) //logs 5
Outra forma de usar é a verificação de curto-circuito, para evitar trabalho desnecessário, ele automaticamente NÃO executa as próximas instruções caso o resultado da primeira seja falso. Exemplo:
Repare no catch, ele fecha a conexão do banco:
Usando if:
const router: Router = Router()router.get('/endpoint', (req: Request, res: Response) => {let conMobile: PoolConnectiontry {//do some db operations} catch (e) {if (conMobile) {conMobile.release()}}})
Usando o operador && (mais elegante):
const router: Router = Router()router.get('/endpoint', (req: Request, res: Response) => {let conMobile: PoolConnectiontry {//do some db operations} catch (e) {conMobile && conMobile.release()}})
O operador || ou Logical OR encontra a primeira expressão verdadeira em seus operandos e a retorna. Isso também emprega curto-circuito para evitar trabalho desnecessário.
console.log(null || 1 || undefined) // logga 1, nem chega a executar o resto
Ele inicializava valores padrões antes do ES6 chegar.
function logName(name) {var n = name || "Mark" // atribui a variável Mark caso não tenha nomeconsole.log(n)}logName() //logs "Mark"
Exemplo no ES6:
function logName(name = 'Mark') { // atribui a variável Mark caso não tenha nomevar n = nameconsole.log(n)}logName() //logs "Mark"
De acordo com a documentação da MDN, + é a maneira mais rápida de converter uma string em um número porque ela não executa nenhuma operação no valor se já for um número.
Exemplo:
console.log(typeof '123') // stringconsole.log(typeof +'123') // number
O DOM (Document Object Model) é uma API para documentos HTML e XML . Ele fornece uma representação estrutural do documento, permitindo modificar o conteúdo e a apresentação visual usando uma linguagem de script como JavaScript.
Quando o navegador lê ( analisa ) nosso documento HTML pela primeira vez, ele cria um objetão de vários nós, uma estrutura de árvore que é modelada a partir do documento HTML que você escreveu, este é o DOM:
O DOM ele facilita e organiza a ordem dos elementos visuais na árvore, isso permite com que a gente modifique exatamente qualquer carinha que a gente quiser que esteja dentro dessa árvore.
Imagina que temos esse HTML simplão aqui:
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><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 equivalente DOM seria essa bagaça aqui:
No Javascript a gente tem acesso a essa árvore através do objeto document. Ele nos fornece muitos métodos que podemos selecionar e usar elementos para atualizar o seu conteúdo e algumas outras funcionalidades.
Caso queira executar o exemplo, use o codepen.io
Imagina o seguinte HTML:
<div id="avo"><div id="pai"><div id="filhao">Filhao</div></div></div>
Se a gente botar um evento de clique no #filhao e no #avo qual executaria primeiro?
document.getElementById("avo").addEventListener("click", function (event) {console.log("#avo clicado")})document.getElementById("filhao").addEventListener("click", function (event) {console.log("#filhao clicado")})
A propagação de eventos ocorre no DOM inteiro, e ele pode acontecer de baixo pra cima ou de cima pra baixo, não entendi, como assim?
O evento não vai acontecer magicamente só no seu botão, imagina que tem um mensageiro maluco que vai de nó em nó tentando avisar "mano, o #um foi clicado, ai vai pro próximo e fala a mesma coisa: mano, o #um foi clicado" até chegar em todos os nós, só que esse doidinho pode começar a fazer essa checagem a partir do:
botão -> em direção a window (Bubbling)
ou da
window -> em direção ao botão. (Capturing)
Exemplo:
A Propagação de Eventos possui três fases.
- Fase de captura - o evento começa a partir de
windowentão desce para todos os elementos até atingir o elemento de destino. - Fase de destino - o evento atingiu o elemento de destino.
- Fase de Bubbling - o evento borbulha do elemento alvo e depois sobe todos os elementos até atingir o
window.
Ainda não entendeu? Calma, acesse esses materiais:
O método event.preventDefault() evita o comportamento padrão de um elemento.
Se usado em um form, ele impede o envio (submit).
Se usado em um anchor (<a>), ele impede a navegação.
Se usado em um contextmenu, impede a exibição.
Enquanto o event.stopPropagation() interrompe a propagação de um evento ou impede a ocorrência do evento na fase de bubbling ou capturing (veja aula anterior).
Como saber se o event.preventDefault() foi usado em um elemento?
Podemos usar a propriedade event.defaultPrevented no objeto de evento, ele retorna um boolean indicando se o event.preventDefault() foi chamado em um elemento específico.
Exemplinho da massa: Caso queira executar o exemplo, use o codepen.io
HTML:
<p><a id="link1" href="#link1">Esse sim vai funcionar pq a gente não fez nada nele.</a></p><p><a id="link2" href="#link2">Tenta ir pro link 2</a> (ele não vai ir pq a gente bloqueou)</p><p id="log"></p>
JS:
function stopLink(event) {event.preventDefault();}function logClick(event) {const log = document.getElementById('log');if (event.target.tagName === 'A') {if (event.defaultPrevented) {log.innerText = 'Bloqueadão no baguio hein pai slc!\n' + log.innerText;}else {log.innerText = 'Esse sim pode!...\n' + log.innerText;}}}const a = document.getElementById('link2');a.addEventListener('click', stopLink);document.addEventListener('click', logClick);
Ainda não entendeu? Calma, acesse esses materiais:
O event.target é o elemento no qual o evento ocorreu ou o elemento que acionou o evento.
Exemplo de HTML:
Caso queira executar o exemplo, use o codepen.io
<div onclick="clickFunc(event)" style="text-align: centermargin:15pxborder:1px solid redborder-radius:3px"><div style="margin: 25px border:1px solid royalblueborder-radius:3px"><div style="margin:25pxborder:1px solid skyblueborder-radius:3px"><button style="margin:10px">Button</button></div></div></div>
Exemplo de JavaScript.
function clickFunc(event) {console.log(event.target) // botão//console.log(event.currentTarget) // div}
Nesse caso ele aciona no BOTÃO e não na DIV, por mais que tenhamos configurado o evento na div em si, quem é o target é o cara que foi acionado no evento.
currentTarget ?
O event.currentTarget é o elemento no qual anexamos explicitamente o manipulador de eventos (div nesse caso).
Mesmo exemplo, só vamos mudar no JS para usar o currentTarget:
function clickFunc(event) {// console.log(event.target) // botãoconsole.log(event.currentTarget) // div}
Nesse caso ele vai apontar pra div (onde configuramos a ação) e não pro botão.
Caso queira executar os exemplos, use o codepen.io
A diferença entre == (abstrata) e === (estrita) é que 0 == compara por valor APÓS a coerção e === por valor e tipo sem coerção.
Vamos nos aprofundar no ==. Mas, primeiro vamos falar sobre coerção .
Javascript não tem tipagem forte, então ele sempre vai tentar fazer a conversão das variáveis pra fazer determinadas operações, isso é chamado de coerção que é o processo de converter um valor para outro tipo.
tá ligado quando você usa o if(algumObjeto)? por baixo dos panos o javascript converte esse maluco pra boolean e consegue dizer se ele existe ou não.
Voltando ao == ele faz essa coerção implícita que acabei de explicar, e para fazer isso ele segue um monte de regra maluca que depende de várias situações, que até o Brendan Eich tem dúvidas sobre isso então não se preocupe se você não entender de primeira.
![]()
SHOW ME THE CODE:
console.log('true == \'true\': ', true == 'true') // aqui vai dar trueconsole.log('true == \'1\': ', true == '1') // aqui vai dar trueconsole.log('true == true): ', true == true) // aqui vai dar trueconsole.log('[===] true === \'true\': ', true === 'true') // aqui vai dar falseconsole.log('[===]true === \'1\': ', true === '1') // aqui vai dar falseconsole.log('[===]true === true): ', true === true) // aqui vai dar true
Suponha que tenhamos que comparar x == y.
- Se
xeytiver o mesmo tipo. Em seguida, ele os compara com o operador===. - Se
xénulleyéundefinedentão retornetrue. - Se
xéundefinedeyénullentão retornetrue. - Se
xé do tiponumbereyé do tipostringretornex == toNumber(y). - Se
xé do tipostringeyé do tiponumberretornetoNumber(x) == y. - Se
xfor do tipobooleanretornetoNumber(x) == y. - Se
yfor do tipobooleanretornex == toNumber(y). - Se
xé um dos tipos (string,symbolounumber) eyé o tipoobjectentão retornex == toPrimitive(y). - Se
xfor umobjectexfor umstring,symbolEntão retornetoPrimitive(x) == y. - Senão, retorne
false.
Nota: toPrimitive usa primeiro o valueOf e depois o toString nos objetos para obter o valor primitivo desse objeto.
Vamos dar exemplos.
x | y | x == y |
|---|---|---|
5 | 5 | true |
1 | '1' | true |
null | undefined | true |
0 | false | true |
'1,2' | [1,2] | true |
'[object Object]' | {} | true |
Todos esses exemplos retornam true.
O primeiro exemplo vai para a condição um porque xe y tem o mesmo tipo e valor.
O segundo exemplo vai para a condição quatro y é convertida em number antes de comparar.
O terceiro exemplo vai para a condição dois.
O quarto exemplo vai para a condição sete porque y é boolean.
O quinto exemplo vai para a condição oito . O array é convertido em string usando o toString()que retorna 1,2.
O último exemplo vai para a condição dez . O objeto é convertido em um string usando o toString() que retorna [object Object].
x | y | x === y |
|---|---|---|
5 | 5 | true |
1 | '1' | false |
null | undefined | false |
0 | false | false |
'1,2' | [1,2] | false |
'[object Object]' | {} | false |
Se usarmos o operador ===, todas as comparações, com exceção do primeiro exemplo, retornarão false, porque não têm o mesmo tipo, enquanto o primeiro exemplo retornará true porque os dois têm o mesmo tipo e valor.
Ainda não entendeu? Calma, nem quem fez o JS entende isso direito (brinks) mas aqui estão alguns artigos legais pra tentar ao menos entender o básico disto:
Sempre olhe a documentação oficial.
Excelente artigo no Medium, falando a respeito.
Consulte a tabela de igualdade do JS
Exemplos de bizarrices no Javascript.
Outros exemplos de bizarrice.
Artigo interessante do Hackernoon.
Suponha que temos um exemplo abaixo.
let a = { a: 1 }let b = { a: 1 }let c = aconsole.log(a === b) // logga false mesmo que tenha a mesma propriedadeconsole.log(a === c) // logga true hmmmmmmm
JavaScript compara objetos e primitivos de maneira diferente.
Nas primitivas, as compara por valor enquanto nos objetos por referência ou pelo endereço na memória em que a variável está armazenada.
É por isso que o primeiro log retorna false e o segundo log retorna true. a e ctem a mesma referência e a e b não.
Por isso se você quiser comparar objetos use o stringify por exemplo:
function jsonEqual(object1, object2) {return JSON.stringify(object1) === JSON.stringify(object2);}jsonEqual(a, b) // true
O operador NÃO-duplo (!!) força a conversão do valor um booleano EXPLICITAMENTE, basicamente, é uma maneira elegante de converter um valor em um booleano.
console.log('!!null: ', !!null) //logs falseconsole.log('!!undefined: ', !!undefined) //logs falseconsole.log('!!\'\': ', !!'') //logs falseconsole.log('!!0: ', !!0) //logs falseconsole.log('!!NaN: ', !!NaN) //logs falseconsole.log('!!\' \' : ', !!' ') //logs trueconsole.log('!!{}: ', !!{}) //logs trueconsole.log('!![]: ', !![]) //logs trueconsole.log('!!1: ', !!1) //logs trueconsole.log('!![].length: ', !![].length) //logs false
Podemos usar a vírgula , para executar várias expressões em uma linha. Ele executa da esquerda para a direita e retorna o valor do último item à direita.
let x = 5x = (x++ , x = addFive(x), x *= 2, x -= 5, x += 10)function addFive(num) {return num + 5}
O que desgraça essa budega faz?
x++ // x vira 1
x = addFive(x) // 1 + 5 = x vira 6
x = 2 // 7 2 = x vira 22
x -= 5 // x vira 17
x += 10 // 17 + 10 = x vira 27
Nunca seja a desgraça de programador que escreve um código xemelento desse.
Se eu fosse reescrever esse código eu deixaria assim:
let originalValue = 5const INCREMENTED_VALUE = originalValue++const INCREMENTED_VALUE_PLUS_FIVE = addFive(INCREMENTED_VALUE)const RESULT_VALUE_MULTIPLIED_BY_TWO = INCREMENTED_VALUE_PLUS_FIVE * 2const DECREMENTED_RESULT_BY_FIVE = RESULT_VALUE_MULTIPLIED_BY_TWO - 5const RESULT_ADDED_WITH_TEN = DECREMENTED_RESULT_BY_FIVE + 10originalValue = RESULT_ADDED_WITH_TENfunction addFive(num) {return num + 5}
Hoisting no Javascript é o termo usado para descrever a movimentação de variáveis e funções para o topo de seu escopo (global ou função), em relação ao local de onde a definimos.
Exemplo:
printName()function printName() {console.log('Paulo Luan é bonitão pakarai')}
Para entender Hoist , temos que entender o contexto de execução.
O contexto de execução é o "ambiente de código" atualmente em execução. O contexto de execução possui duas fases de compilação e execução.
Compilação - nesta fase esse maluco pega as vars e functions para cima, para que possamos referenciá-los mais tarde e receber os valores.
Execução - nesta fase, atribui valores às variáveis içadas anteriormente e executa ou invoca funções (métodos em objetos) .
Nota: só vale se for function ou var RAÍZ, os nutella do ES6 NÃO são HOIÇADOS (let, const, arrow function e o restante).
veja os exemplos:
console.log(y)var y = 1 // VAR BRABO DO RAÍZ DO RISCA FACA FUNCIONA
console.log(y)let y = 1 // NUTELLA NÃO FUNCIONA
console.log(greet("Paulo Luan Bonitão"))function greet(name){return 'Olá ' + name + '!'}
console.log(greet("Paulo Luan Bonitão"))var greet = (name) => { // NUTTELAreturn 'Olá ' + name + '!'}//console.log(greet("Paulo Luan Bonitão"))
Escopo em JavaScript é a área em que temos acesso válido a variáveis ou funções.
JavaScript tem três tipos de escopos: Escopo Global , Escopo da Função e Escopo do Bloco (ES6).
- Escopo Global - variáveis ou funções declaradas no espaço de nomes global estão no escopo global e, portanto, estão acessíveis em qualquer lugar em nosso código.
//global namespacevar g = "global"function globalFunc(){function innerFunc(){console.log(g) // G é global, então é acessível aqui}innerFunc()}
- Escopo da Função - variáveis, funções e parâmetros declarados em uma função são acessíveis dentro dessa função, mas não fora dela.
function myFavoriteFunc(a) {if (true) {var b = "Hello " + a}return b}myFavoriteFunc("World")console.log(a) // Throws a ReferenceError "a" is not definedconsole.log(b) // does not continue here
- Escopo do bloco - variáveis (
let,const) declaradas em um bloco{}só podem ter acesso dentro dele.
function testBlock(){if(true) {let z = 5}console.log(z)}testBlock() // Throws a ReferenceError "z" is not defined
Escopo também é um conjunto de regras para encontrar variáveis. Se uma variável não existe no escopo atual ele sai olhando nos escopos acima no escopo externo, e se não existe mais uma vez, olha para cima novamente até atingir o escopo global se a variável existe ele usa, senão manda aquele errão brabo na tela e para de procurar. Isso é chamado de cadeia de escopo.
/* Scope ChainInside inner function perspectiveinner's scope -> outer's scope -> global's scope*///Global Scopevar variable1 = "Comrades"var variable2 = "Sayonara"function outer(){//outer's scopevar variable1 = "World"function inner(){//inner's scopevar variable2 = "Hello"console.log(variable2 + " " + variable1)}inner()}outer()// logs Hello World// because (variable2 = "Hello") and (variable1 = "World") are the nearest// variables inside inner's scope.
Closures é um tópico controverso. O que eu gostaria de ouvir como entrevistador seria algo como:
Closures significa que uma função interna sempre tem acesso as variáveis e aos parâmetros de sua função externa, mesmo após o retorno da função externa.
Cê ta ligado que dá pra criar função dentro de função né?
exemplo:
function OuterFunction () {var outerVariable = 1function InnerFunction () {console.log(outerVariable)}InnerFunction()}
No exemplo acima, InnerFunction() pode acessar a variável outerVariable mesmo ela estando declarada no escopo de cima.
function OuterFunction () {var outerVariable = 100var parameters = JSON.stringify(arguments)function InnerFunction () {console.log(`[OUTER CLOSURE] OuterVariable continua viva no meu coração: ${outerVariable}`)console.log(`[OUTER CLOSURE] Parâmetros que mandaram para o Outer: ${parameters}`)console.log(`[INNER] Meus parâmetros: ${JSON.stringify(arguments)}`)}return InnerFunction}var innerFunc = OuterFunction(123, "vral")innerFunc()
Ele chama a OuterFunction mas a única referência que você tem é a da InnerFunction, mas MESMO ASSIM você continua tendo acesso as variáveis que foram passadas pelo parâmetro para a OuterFunction bem como o que foi declarado nela que no caso é a outerVariable
Isso permite que ninguém externamente consiga mudar essas variáveis de dentro do escopo do OuterFunction, olha só:
var counter = (function () {var privateCounter = 0function changeBy (val) {privateCounter += val}return {increment: function () {changeBy(1)},decrement: function () {changeBy(-1)},value: function () {return privateCounter},}})()var original = counter.value()console.log("Contador Original: ", original)alert(counter.value()) // altera o valorcounter.increment()counter.increment()console.log("Contador Original: ", original) // continua a mesma bostaalert(counter.value())counter.decrement()alert(counter.value())console.log("Contador Original: ", original) // continua a mesma bosta
Explicação do Douglinhas (um dos principais devs de JS do mundo).
Tutorial gostosinho do TutorialsTeacher.
"use strict" é um recurso do ES5 no JavaScript que transforma nosso código no Modo Estrito em funções ou scripts inteiros . O Modo Estrito nos ajuda a evitar erros no início de nosso código e adiciona restrições a ele.
Restrições que o Modo Estrito nos fornece.
- Atribuindo ou acessando uma variável que não é declarada.
function returnY(){"use strict"y = 123return y}
- Atribuir um valor a uma variável global somente leitura ou não gravável
"use strict"var NaN = NaNvar undefined = undefinedvar Infinity = "and beyond"
- Excluindo uma propriedade não excluída.
"use strict"const obj = {}Object.defineProperty(obj, 'x', {value : '1'})delete obj.x
- Nomes de parâmetros duplicados.
"use strict"function someFunc(a, b, b, c){}
- Criando variáveis com o uso da função eval .
"use strict"eval("var x = 1")console.log(x) //Throws a Reference Error x is not defined
- O valor padrão disso será
undefined.
"use strict"function showMeThis(){return this}showMeThis() //returns undefined
Existem muito mais restrições no modo estrito que essas.
Errar é extremamente natural, a série de aulas "Javascript para entrevistas" não tiveram o engajamento que eu esperava e eu acabei mudando o foco para outros assuntos de maior interesse das pessoas que me seguem no Instagram.
Errar é um dos processos mais importantes do aprendizado, e eu sempre digo aqui na Reativa que eu tenho a pele em jogo, ou seja, eu aplico as coisas que falo na prática NA VIDA REAL.
Errar não é um problema, o grande segredo é identificá-lo quanto antes e ajustar as devidas mudanças para conseguir os resultados que você almeja, entenda:
Na sua carreira de programador, pode ser que o erro esteja na empresa que você trabalha que não te ajuda a crescer como gostaria, amigos que não te ajudam a ser um dev melhor, ambientes que não colaboram para o seu crescimento como pessoa, etc.
Estar atento a todas essas coisas e mudar rapidamente os rumos da sua vida, te ajudarão a testar novas hipóteses e chegar mais rapidamente aos bons resultados. Crescer na maioria das vezes é melhor que ganhar, porque significa que você está se colocando em situações cada vez mais difíceis em vez de simplesmente estagnar.
A única coisa que não pode faltar é o trabalho duro! 💪
Os conteúdos não vão parar, pelo contrário, já estou preparando novos conteúdos extremamente interessantes para colocar por aqui, aguardem! 🐿⚡️
Estude o artigo original: 70 Interview Questions


