Deseja se preparar para uma entrevista em JavaScript e está procurando perguntas para praticar? Se sim - considere que sua pesquisa terminou. O autor do material, cuja tradução publicamos hoje, diz que ele coletou mais de duas dúzias de perguntas sobre JavaScript, destinadas a quem deseja passar de júnior para sênior, para aqueles que procuram passar com êxito por uma entrevista no campo de desenvolvimento front-end e obter uma oferta interessante de o empregador.

1. Explique os recursos de validação de igualdade de JavaScript
Dificuldade: *
JavaScript tem dois operadores para verificar a igualdade de valores. O primeiro é o chamado operador de igualdade estrita. O segundo é o operador de igualdade não restrito, que pode ser usado para converter os tipos de quantidades verificadas.- O operador de igualdade estrita (
===
) verifica os valores de igualdade sem realizar conversões de tipo. - O operador de igualdade não estrito (
==
) verifica os valores de igualdade, convertendo-os em um tipo comum.
var a = "42";
var b = 42;
a == b;
a === b;
Aqui estão algumas diretrizes para o uso de vários verificadores de igualdade no JavaScript:- Se algum dos valores comparados puder ser um valor
true
ou false
- tente evitar o operador ==
. Use um operador ===
. - Use o operador
===
em caso de que se você está trabalhando com os seguintes valores: 0
, «»
ou []
(array vazio). - Em todos os outros casos, você pode usar o operador com segurança
==
. Além disso, isso não é apenas seguro, mas também ajuda a simplificar o código e melhorar sua legibilidade.
→ Origem2. Dê exemplos de conversão para um tipo lógico de valores que não estão relacionados a esse tipo
Dificuldade: ***
A essência dessa pergunta é descobrir quais valores, no caso de convertê-los em um tipo lógico, se transformam false
e quais - em true
.Aqui está uma lista de valores que podem ser chamados de "falsidade". Ao converter para um tipo lógico, eles se transformam em um valor false
:«»
(linha vazia).0
, -0
, NaN
(Não um número).null
, undefined
.
"Falso" é um significado lógico false
.Qualquer valor que não esteja incluído nesta lista, quando convertido em um tipo lógico, se transforma em true
(esses valores são chamados de "true" - na verdade). Por exemplo:«hello»
.42
.[ ]
, [ 1, «2», 3 ]
(matrizes).{ }
, { a: 42 }
(objetos).function foo() { .. }
(funções).
"Verdadeiro" também é um significado lógico true
.→ Origem3. O que é IIFE?
Dificuldade: ***
IIFE (expressão da função chamada imediatamente) é uma expressão funcional chamada imediatamente. Essa expressão é executada imediatamente após a criação.(function IIFE(){
console.log( "Hello!" );
})();
Esse padrão é frequentemente usado para evitar a poluição do espaço para nome global. O fato é que as variáveis declaradas no IIFE (como em qualquer outra função comum) são invisíveis fora dessa função.→ Origem4. Quando devo usar as funções de seta que apareceram no ES6?
Dificuldade: ***
Aqui estão regras simples para usar as várias maneiras de declarar funções que sigo ao desenvolver código para ambientes que suportam ES6 e padrões mais recentes:- Use a palavra-chave
function
no escopo global e para propriedades Object.prototype
. - Use a palavra-chave
function
para construtores de objetos. - Em outros casos, use as funções de seta.
Como você pode ver, as funções de seta são recomendadas para serem usadas em quase todos os lugares. Existem várias razões para esse estado de coisas:- Trabalho conveniente com contexto. As funções de seta usam o valor do
this
contexto circundante sem ter o seu próprio this
. Se essas funções forem usadas seqüencialmente, sem o uso de funções comuns em construções complexas, isso garante um trabalho seguro com o contexto. - Compacidade. O código da função da seta é mais fácil de inserir e mais fácil de ler. Talvez essa vantagem das funções de seta em relação às comuns pareça controversa e dependendo do ponto de vista de cada desenvolvedor específico.
- Clareza do código. Se quase todo o código for representado por funções de seta, qualquer função comum será distinguida nesse código, criando seu próprio contexto. Usando as funções de seta, o programador cria um código mais compreensível no qual é mais fácil trabalhar do que no código sem as funções de seta
this
.
→ Origem5. Qual é a diferença entre as classes ES6 e os construtores de funções?
Dificuldade: ***
Vejamos alguns exemplos primeiro.Função construtora:function Person(name) {
this.name = name;
}
Classe ES6:class Person {
constructor(name) {
this.name = name;
}
}
Quando se trata de criar objetos simples, os construtores e classes usados para esse fim são muito semelhantes.A principal diferença entre construtores e classes aparece ao usar herança. Se precisarmos criar uma classe Student
que é uma subclasse da classe Person
e adicionar um campo a essa nova classe studentId
, é assim que o código no qual os construtores são usados e o código no qual as classes são usadas.Função construtora:function Student(name, studentId) {
Person.call(this, name);
this.studentId = studentId;
}
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;
Classe ES6:class Student extends Person {
constructor(name, studentId) {
super(name);
this.studentId = studentId;
}
}
→ Origem6. Conte-nos sobre o método Function.prototype.bind ().
Dificuldade: ***
Para citar o MDN: “O método bind()
cria uma nova função que, quando chamada, define o this
valor fornecido como o contexto de execução . O conjunto de argumentos também é passado para o método, que será definido antes dos argumentos passados para a função vinculada quando for chamado. ”Eu acredito nesse método. bind()
especialmente útil para vincular valores this
em métodos de classe que precisam ser passados para outras funções. Essa técnica é frequentemente usada nos componentes React.→ Origem 7. Para que as funções anônimas são comumente usadas?
Dificuldade: ***
Funções anônimas são usadas para criar construções IIFE, as variáveis declaradas nas quais não poluem o escopo global.(function() {
})();
Funções anônimas são usadas como funções de retorno de chamada, que são usadas apenas em um local do programa. O código parecerá mais auto-suficiente e legível se o retorno de chamada for anunciado exatamente no local em que é usado. Isso elimina a necessidade de examinar o código em busca do corpo da função.setTimeout(function() {
console.log('Hello world!');
}, 1000);
As funções anônimas são convenientemente usadas em construções específicas para o estilo de programação funcional ou ao trabalhar com bibliotecas como o Lodash (esse caso de uso é semelhante ao uso como retornos de chamada).const arr = [1, 2, 3];
const double = arr.map(function(el) {
return el * 2;
});
console.log(double);
→ Origem8. Qual é a diferença entre o método Object.freeze () e a palavra-chave const?
Dificuldade: ***
Palavra const
- chave e método Object.freeze()
são coisas completamente diferentes.A palavra-chave const
se aplica a ligações (a "variáveis"). Ele cria uma ligação imutável, ou seja, é const
impossível vincular algo novo a uma variável (constante) declarada usando uma palavra-chave . Uma constante não pode ser atribuída a um novo valor.const person = {
name: "Leonardo"
};
let animal = {
species: "snake"
};
person = animal;
O método Object.freeze()
funciona com valores. Ou melhor, com valores de objeto. Torna o objeto imutável, o que protege contra alterações no valor das propriedades desse objeto.let person = {
name: "Leonardo"
};
Object.freeze(person);
person.name = "Lima";
console.log(person);
Observe que a mensagem de erro é exibida no modo estrito. No modo normal, a operação de alterar a propriedade de um objeto "congelado" simplesmente não funciona.→ Origem9. O que é um "gerador"?
Dificuldade: ***
Geradores são funções das quais você pode "sair" e nas quais pode "entrar", conforme necessário. Seu contexto (ligações variáveis) é mantido entre as sessões de "entrada" nelas. Os geradores são declarados usando uma palavra-chave function*
. Essa função, quando é chamada pela primeira vez, não executa o código, retornando um objeto especial, um gerador, que permite controlar sua execução. Para obter o valor próximo emitido pelo gerador, você precisa chamar o seu método next()
. Por esse motivo, o código da função é executado até encontrar uma palavra-chave yield
que retorna um valor.A função do gerador pode ser chamada quantas vezes você desejar. Cada vez que um novo gerador retornará. Mas cada gerador pode ser ignorado apenas uma vez.function* makeRangeIterator(start = 0, end = Infinity, step = 1) {
let iterationCount = 0;
for (let i = start; i < end; i += step) {
iterationCount++;
yield i;
}
return iterationCount;
}
→ Origem10. Quando os geradores devem ser usados?
Dificuldade: ***
Em poucas palavras, para descrever os principais recursos úteis dos geradores, eles são os seguintes:- O código no qual o gerador é usado determina o momento em que o próximo valor é recebido. O gerador é responsável apenas pelo retorno dos valores, é controlado a partir do exterior.
- Existem geradores assíncronos. Eles permitem que você trabalhe com fluxos de dados assíncronos.
O principal nos geradores é que você pode obter o próximo valor retornado pelo gerador somente quando necessário no código que usa o gerador. Os geradores não retornam tudo de uma vez. Em algumas situações, esse recurso pode ser muito conveniente.→ Origem11. O que é "aumentar variáveis"?
Dificuldade: ****
A essência do conceito de "aumentar variáveis" é que os anúncios "sobem" para o topo do escopo atual. Como resultado, a variável pode ser usada antes de sua declaração. Somente declarações de variáveis são geradas, mas não seu código de inicialização. Observe que o comportamento das variáveis declaradas usando a palavra var
- chave é diferente do comportamento das variáveis e constantes declaradas usando let
e const
.→ Origem12. Qual será o código a seguir?
Dificuldade: ****
var output = (function(x) {
delete x;
return x;
})(0);
console.log(output);
Este código será exibido 0
. O operador é delete
usado para excluir as propriedades dos objetos. E x
- isso não é uma propriedade de objeto - é uma variável local. O operador delete
não afeta variáveis locais.→ Origem13. Qual será o código a seguir?
Dificuldade: ****
var Employee = {
company: 'xyz'
}
var emp1 = Object.create(Employee);
delete emp1.company
console.log(emp1.company);
Este código será exibido xyz
. Uma propriedade company
não é uma propriedade de um objeto emp1
, mas uma propriedade de seu protótipo. O operador delete
não exclui as propriedades do protótipo dos objetos. Um objeto emp1
não possui sua própria propriedade company
. Você pode verificar isso:console.log(emp1.hasOwnProperty('company'));
Se ainda precisarmos remover essa propriedade, você poderá fazer isso entrando em contato diretamente com o objeto Employee
( delete Employee.company
) ou com o protótipo do objeto emp1
usando sua propriedade __proto__
( delete emp1.__proto__.company
).→ Origem14. Conte-nos sobre o padrão de design do protótipo.
Dificuldade: ****
Protótipo é um padrão de design genérico. É usado para criar objetos. Os objetos criados usando-o contêm valores copiados de seu protótipo (do objeto de amostra). Este modelo também é chamado de modelo Propriedades.Um exemplo do uso do padrão "protótipo" é a inicialização de certos objetos com valores padrão armazenados no banco de dados. Esses valores registrados no protótipo são copiados para novos objetos sem acessar o banco de dados.Deve-se notar que esse padrão raramente é usado em idiomas clássicos. JavaScript usa um modelo de herança de protótipo. Esse padrão é usado no design de novos objetos e seus protótipos.→ Origem15. O que é uma "zona morta temporária" no ES6?
Dificuldade: ****
O ES6 realizado o levantamento de variáveis e constantes declaradas usando as palavras-chave let
e const
(isso é feito e o aumento da entidade declarada usando palavras-chave var
, class
e function
). No entanto, o código possui uma zona que se estende da inserção do escopo à declaração de uma variável ou constante. Ao acessar uma variável ou constante nesta zona, um erro será gerado. Esta é a "Zona morta temporal" (TDZ).
let aLet;
console.log(aLet);
aLet = 10;
console.log(aLet);
Neste exemplo, o TDZ termina após a declaração aLet
, mas não após o aLet
valor ser atribuído .→ Origem16. Você pode descrever a principal diferença entre os métodos de matriz forEach () e map ()? Em que situações você prefere um desses métodos a outro?
Dificuldade: ****
Para entender a diferença entre esses métodos, vamos falar sobre os recursos de cada um deles.Veja como funciona .forEach()
:- Ele itera sobre os elementos da matriz.
- Ele executa a função de retorno de chamada transmitida a ele para cada elemento da matriz.
- Não retorna nada.
const a = [1, 2, 3];
const doubled = a.forEach((num, index) => {
});
Aqui está uma breve descrição do método .map()
:- Ele itera sobre os elementos da matriz.
- Ele converte cada elemento da matriz original em um elemento da nova matriz, chamando a função passada a ele para cada elemento da matriz original.
const a = [1, 2, 3];
const doubled = a.map(num => {
return num * 2;
});
Como resultado, verifica-se que a principal diferença entre .forEach()
e .map()
é que ela .map()
retorna uma nova matriz. Se você precisar obter o resultado da conversão dos elementos da matriz original sem alterar essa matriz, deverá escolher .map()
. Se você apenas precisar iterar sobre os elementos da matriz - poderá usá-lo .forEach()
.→ Origem17. Qual é a diferença entre uma variável não declarada, uma variável que contém um valor nulo e uma variável não definida? Como verificar uma variável pelo fato de ela não ser declarada, assim como nula e indefinida?
Dificuldade: ****
Uma variável não declarada é criada quando um valor é atribuído a um identificador que não foi declarado anteriormente usando var
, let
ou const
. Variáveis não declaradas são declaradas no escopo global, fora do escopo atual. No modo estrito, uma exceção é lançada ao tentar atribuir um valor a uma variável não declarada ReferenceError
. O uso de variáveis não declaradas não é recomendado - assim como o uso de variáveis globais não é recomendado. Eles devem ser evitados por todos os meios. A fim de proteger-se contra as consequências da utilização de variáveis não declarado, utilize o bloco try/catch
.function foo() {
x = 1;
}
foo();
console.log(x);
Uma variável que contém undefined
é uma variável declarada à qual não é atribuído um valor. O valor undefined
forma seu próprio tipo de dados. Se a função não retornar nada e o resultado de sua chamada for gravado em uma variável, ela cairá nessa variável undefined
. Para organizar uma verificação undefined
, você pode usar o operador de igualdade estrita ( ===
) ou o operador typeof
que retorna uma string undefined
. Observe que, ao procurar, undefined
você não deve usar o operador de igualdade não estrito ( ==
), pois ele considera os valores undefined
e iguais null
.var foo;
console.log(foo);
console.log(foo === undefined);
console.log(typeof foo === 'undefined');
console.log(foo == null);
function bar() {}
var baz = bar();
console.log(baz);
Uma variável que contém um valor null
deve ser definida explicitamente para esse valor. Ele simboliza a ausência de significado e difere de undefined
-variable, pois o valor nele foi explicitamente atribuído a ele. Para verificar o valor null
, basta usar o operador de igualdade estrita. Para verificar null
, como no caso de verificação undefined
, não se deve usar o operador de igualdade não estrito, que considera os valores igual null
e igual a undefined
.var foo = null;
console.log(foo === null);
console.log(typeof foo === 'object');
console.log(foo == undefined);
Eu tento nunca deixar variáveis em um estado não declarado ou em um estado em que elas são declaradas, mas elas não recebem explicitamente nenhum valor. Se não vou escrever um valor em uma variável imediatamente após a declaração, escrevo nela null
. Se você usa um linter, ele geralmente relata casos de uso de variáveis não declaradas.→ Origem18. Conte-nos sobre o módulo de design "Módulo aberto"
Dificuldade: *****
O modelo "Revealing Module" é uma variação do modelo "Module". O objetivo de usar esse padrão é oferecer suporte ao encapsulamento e descobrir algumas propriedades e métodos retornados no literal do objeto. Veja como será a implementação direta deste modelo:var Exposer = (function() {
var privateVariable = 10;
var privateMethod = function() {
console.log('Inside a private method!');
privateVariable++;
}
var methodToExpose = function() {
console.log('This is a method I want to expose!');
}
var otherMethodIWantToExpose = function() {
privateMethod();
}
return {
first: methodToExpose,
second: otherMethodIWantToExpose
};
})();
Exposer.first();
Exposer.second();
Exposer.methodToExpose;
A desvantagem óbvia desse modelo é que você não pode usar métodos privados ao usá-lo.→ Origem19. Qual é a diferença entre os objetos Map e WeakMap?
Dificuldade: *****
Esses objetos se comportam de maneira diferente se uma variável que contém uma referência a um objeto que é a chave de um dos pares chave / valor não está disponível. Aqui está um exemplo:var map = new Map();
var weakmap = new WeakMap();
(function() {
var a = {
x: 12
};
var b = {
y: 12
};
map.set(a, 1);
weakmap.set(b, 2);
})()
Depois que a execução do IIFE for concluída, não teremos mais acesso aos objetos a
e b
. Portanto, o coletor de lixo remove a chave b
de weakmap
e limpa a memória. Mas o conteúdo map
permanece o mesmo.Como resultado, verifica-se que os objetos WeakMap
permitem que o coletor de lixo se livre dos registros que não são referenciados em variáveis externas. Os objetos map
armazenam pares de chave / valor, independentemente da presença ou ausência de referências de chave externas. O mesmo pode ser dito sobre a implementação da estrutura de dados Map
usando matrizes comuns. As WeakMap
referências de tecla "fracas" são usadas. Eles não interferem na operação do coletor de lixo se não houver outras referências ao objeto usado como chave.→ Origem20. Como os parâmetros são passados para a função JavaScript: por referência ou por valor?
Dificuldade: *****
Os parâmetros são sempre passados por valor, mas as referências aos objetos são gravadas nas variáveis que representam os objetos. Portanto, quando um objeto é transferido para uma função e a propriedade desse objeto é alterada, essa alteração é salva no objeto mesmo quando a função é encerrada. Como resultado, há uma sensação de que os parâmetros na função são passados por referência. Mas se você alterar o valor da variável que representa o objeto, essa alteração não afetará objetos que estão fora da função.Aqui está um exemplo:function changeStuff(a, b, c)
{
a = a * 10;
b.item = "changed";
c = {item: "changed"};
}
var num = 10;
var obj1 = {item: "unchanged"};
var obj2 = {item: "unchanged"};
changeStuff(num, obj1, obj2);
console.log(num);
console.log(obj1.item);
console.log(obj2.item);
Aqui está o que esse código produzirá:10
changed
unchanged
→ Origem21. Como organizar um “congelamento profundo” de um objeto?
Dificuldade: *****
Para fornecer o "congelamento profundo" de um objeto usando Object.freeze()
, você precisa criar uma função recursiva que "congele" as propriedades do objeto, que também são objetos.Aqui está um exemplo de um "congelamento" comum de um objeto:let person = {
name: "Leonardo",
profession: {
name: "developer"
}
};
Object.freeze(person);
person.profession.name = "doctor";
console.log(person);
Aqui está o "congelamento profundo":function deepFreeze(object) {
let propNames = Object.getOwnPropertyNames(object);
for (let name of propNames) {
let value = object[name];
object[name] = value && typeof value === "object" ?
deepFreeze(value) : value;
}
return Object.freeze(object);
}
let person = {
name: "Leonardo",
profession: {
name: "developer"
}
};
deepFreeze(person);
person.profession.name = "doctor";
A mensagem de erro é exibida apenas no modo estrito. No modo normal, o valor não muda sem mensagens de erro.→ Origem22. Por que os programadores JavaScript estão tendo problemas para usar a palavra-chave this?
Dificuldade: *****
A coisa mais importante a entender this
é que as funções não têm um valor fixo this
. Este valor depende de como a função é chamada. Se dissermos que uma função é chamada com algum valor específico this
, isso significa que esse valor é determinado não durante a declaração da função, mas durante sua chamada. Aqui estão alguns recursos this
:- Se a função for chamada na forma usual (ou seja, usando a construção view
someFunc()
), ela this
se referirá ao objeto global (no navegador, este window
). Se o código for executado no modo estrito, um this
valor será gravado em undefined
. - Se a função for chamada como método de um objeto, a palavra-chave
this
será representada pelo objeto ao qual o método pertence. - call apply,
this
, call
apply
. - ,
this
. - ,
new
, this
, prototype
-. - Se a função foi criada usando o método bind , a palavra
this
- chave function será rigidamente vinculada ao valor passado bind
como o primeiro argumento. Essa é a única exceção à regra de que funções não possuem um valor codificado this
. As funções criadas usando bind
são imutáveis this
.
→ Origem23. Compare o uso da construção assíncrona / aguardada e geradores para implementar a mesma funcionalidade
Dificuldade: *****
- Ao iterar um gerador usando um método,
.next()
cada chamada para esse método retorna um único valor usando a palavra-chave yield
. Ao usar a construção assíncrona / espera, as expressões aguardam são executadas seqüencialmente. - O design assíncrono / espera simplifica a implementação de um caso de uso de gerador específico.
- Os valores retornados pelo gerador sempre têm a forma
{value: X, done: Boolean}
, e as funções assíncronas retornam promessas resolvidas com o valor X
, ou falham. - Uma função assíncrona pode ser convertida em um gerador usando promessas. A seguir, é apresentado um exemplo dessa conversão.
Aqui está a função assíncrona:
async function init() {
const res1 = await doTask1();
console.log(res1);
const res2 = await doTask2(res1);
console.log(res2);
const res3 = await doTask3(res2);
console.log(res3);
return res3;
}
init();
Aqui está um gerador semelhante.
function runner(genFn) {
const itr = genFn();
function run(arg) {
let result = itr.next(arg);
if (result.done) {
return result.value;
} else {
return Promise.resolve(result.value).then(run);
}
}
return run;
}
runner(function* () {
const res1 = await doTask1();
console.log(res1);
const res2 = await doTask2(res1);
console.log(res2);
const res3 = await doTask3(res2);
console.log(res3);
return res3;
});
→ FonteCaros leitores! Quais perguntas de JavaScript você formulou durante suas entrevistas?