23 preguntas difíciles para entrevistas de JavaScript

¿Desea prepararse para una entrevista de JavaScript y está buscando preguntas para practicar? Si es así, considere que su búsqueda ha terminado. El autor del material, cuya traducción publicamos hoy, dice que ha recopilado más de dos docenas de preguntas en JavaScript destinadas a aquellos que desean pasar de ser junior a senior, para aquellos que buscan pasar con éxito una entrevista en el campo del desarrollo front-end y obtener una oferta interesante de el empleador.


imagen

1. Explicar las características de validación de igualdad de JavaScript


Dificultad: *


JavaScript tiene dos operadores para verificar la igualdad de valores. El primero es el llamado operador de igualdad estricta. El segundo es el operador de igualdad no estricto, que se puede utilizar para convertir los tipos de cantidades controladas.

  • El operador de igualdad estricta ( ===) verifica los valores de igualdad sin realizar conversiones de tipo.
  • El operador de igualdad no estricto ( ==) verifica los valores de igualdad, convirtiéndolos en un tipo común.

var a = "42";
var b = 42;

a == b;         // true
a === b;        // false

Aquí hay algunas pautas para usar varios verificadores de igualdad en JavaScript:

  • Si alguno de los valores comparados puede ser un valor trueo false, intente evitar el operador ==. Utiliza un operador ===.
  • Utilice el operador ===en caso de que si se está trabajando con los siguientes valores: 0, «»o [](matriz vacía).
  • En todos los demás casos, puede utilizar el operador de forma segura ==. Además, esto no solo es seguro, sino que también ayuda a simplificar el código y mejorar su legibilidad.

Fuente

2. Dé ejemplos de conversión a un tipo lógico de valores que no están relacionados con este tipo


Dificultad: ***


La esencia de esta pregunta es descubrir qué valores, en el caso de convertirlos a un tipo lógico, se convierten en false, y cuáles - en true.

Aquí hay una lista de valores que se pueden llamar "falsos". Ellos, al convertir a un tipo lógico, se convierten en un valor false:

  • «» (línea vacía)
  • 0, -0, NaN(No es un número).
  • null, undefined.

"Falso" es un significado lógico false.

Cualquier valor que no esté incluido en esta lista, cuando se convierte a un tipo lógico, se convierte en true(dichos valores se denominan "verdadero" - verdad). Por ejemplo:

  • «hello».
  • 42.
  • [ ], [ 1, «2», 3 ](matrices).
  • { }, { a: 42 }(objetos).
  • function foo() { .. } (funciones).

"Verdadero" es también un significado lógico true.

Fuente

3. ¿Qué es el IIFE?


Dificultad: ***


IIFE (Expresión de función invocada inmediatamente) es una expresión funcional invocada de inmediato. Dicha expresión se ejecuta inmediatamente después de la creación.

(function IIFE(){
    console.log( "Hello!" );
})();
// "Hello!"

Este patrón se usa a menudo para evitar la contaminación del espacio de nombres global. El hecho es que las variables declaradas en IIFE (como en cualquier otra función ordinaria) son invisibles fuera de esta función.

Fuente

4. ¿Cuándo debo usar las funciones de flecha que aparecieron en ES6?


Dificultad: ***


Aquí hay reglas simples para usar las diversas formas de declarar las funciones que sigo al desarrollar código para entornos que admiten ES6 y estándares más nuevos:

  • Use la palabra clave functionen el ámbito global y para las propiedades Object.prototype.
  • Use la palabra clave functionpara constructores de objetos.
  • En otros casos, use las funciones de flecha.

Como puede ver, se recomienda utilizar las funciones de flecha en casi todas partes. Hay varias razones para este estado de cosas:

  • Trabajo conveniente con contexto. Las funciones de flecha usan el valor del thiscontexto circundante sin tener el suyo this. Si tales funciones se usan secuencialmente, sin usar funciones ordinarias en construcciones complejas, esto garantiza un trabajo seguro con el contexto.
  • Compacidad El código de función de flecha es más fácil de ingresar y más fácil de leer. Quizás esta ventaja de las funciones de flecha sobre las normales le parecerá controvertida y dependerá del punto de vista de cada desarrollador específico.
  • Código de claridad. Si casi todo el código está representado por funciones de flecha, cualquier función ordinaria se distingue en dicho código creando su propio contexto. Usando las funciones de flecha, el programador crea un código más comprensible en el que es más fácil trabajar que en el código sin funciones de flecha this.

Fuente

5. ¿Cuál es la diferencia entre las clases ES6 y los constructores de funciones?


Dificultad: ***


Veamos algunos ejemplos primero.

Función del constructor:

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

Clase ES6:

class Person {
  constructor(name) {
    this.name = name;
  }
}

Cuando se trata de crear objetos simples, los constructores y las clases utilizados para este propósito se ven muy similares.

La principal diferencia entre constructores y clases aparece cuando se usa la herencia. Si necesitamos crear una clase Studentque sea una subclase de la clase Persony agregar un campo a esta nueva clase studentId, así es como se verá el código en el que se usan los constructores y el código en el que se usan las clases.

Función del constructor:

function Student(name, studentId) {
  //      ,   .
  Person.call(this, name);

  //    .
  this.studentId = studentId;
}

Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;

Clase ES6:

class Student extends Person {
  constructor(name, studentId) {
    super(name);
    this.studentId = studentId;
  }
}

Fuente

6. Cuéntanos sobre el método Function.prototype.bind ().


Dificultad: ***


Para citar MDN: “El método bind()crea una nueva función que, cuando se llama, establece el thisvalor proporcionado como el contexto de ejecución . El conjunto de argumentos también se pasa al método, que se establecerá antes de que los argumentos pasen a la función enlazada cuando se llama ".

Yo creo en ese método. bind()especialmente útil para enlazar valores thisen métodos de clase que deben pasarse a otras funciones. Esta técnica se usa a menudo en componentes React.

Fuente 

7. ¿Para qué se usan comúnmente las funciones anónimas?


Dificultad: ***


Las funciones anónimas se utilizan para crear construcciones IIFE, las variables declaradas en las que no contaminan el alcance global.

(function() {
  // - .
})();

Las funciones anónimas se usan como funciones de devolución de llamada, que se usan solo en un lugar del programa. El código se verá más autosuficiente y legible si la devolución de llamada se anuncia directamente en el lugar donde se usa. Esto elimina la necesidad de mirar el código en busca del cuerpo de la función.

setTimeout(function() {
  console.log('Hello world!');
}, 1000);

Las funciones anónimas se usan convenientemente en construcciones específicas del estilo de programación funcional, o cuando se trabaja con bibliotecas como Lodash (este caso de uso es similar a su uso como devoluciones de llamada).

const arr = [1, 2, 3];
const double = arr.map(function(el) {
  return el * 2;
});
console.log(double); // [2, 4, 6]

Fuente

8. ¿Cuál es la diferencia entre el método Object.freeze () y la palabra clave const?


Dificultad: ***


La palabra clave consty el método Object.freeze()son cosas completamente diferentes.

La palabra clave se constaplica a los enlaces (a "variables"). Crea un enlace inmutable, es decir, es constimposible vincular algo nuevo a una variable (constante) declarada usando una palabra clave . A una constante no se le puede asignar un nuevo valor.

const person = {
    name: "Leonardo"
};
let animal = {
    species: "snake"
};
person = animal; // Uncaught TypeError: Assignment to constant variable.

El método Object.freeze()funciona con valores. O más bien, con valores de objeto. Hace que el objeto sea inmutable, lo que protege contra cambios en el valor de las propiedades de este objeto.

let person = {
    name: "Leonardo"
};
Object.freeze(person);
person.name = "Lima"; // Uncaught TypeError: Cannot assign to read only property 'name' of object
console.log(person);

Tenga en cuenta que el mensaje de error se muestra en modo estricto. En modo normal, la operación de cambiar la propiedad de un objeto "congelado" simplemente no funciona.

Fuente

9. ¿Qué es un "generador"?


Dificultad: ***


Los generadores son funciones desde las cuales puede "salir" y en las que puede "entrar" según sea necesario. Su contexto (enlaces variables) se mantiene entre sesiones de "entrada" en ellos. Los generadores se declaran usando una palabra clave function*. Dicha función, cuando se llama por primera vez, no ejecuta el código, devolviendo un objeto especial, un generador, que le permite controlar su ejecución. Para obtener el siguiente valor emitido por el generador, debe llamar a su método next(). Debido a esto, el código de función se ejecuta hasta que encuentra una palabra clave yieldque devuelve un valor.

La función del generador se puede invocar tantas veces como desee. Cada vez que un nuevo generador regresará. Pero cada generador se puede omitir solo una 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;
}

Fuente

10. ¿Cuándo deben usarse los generadores?


Dificultad: ***


Si, en pocas palabras, describe las principales características útiles de los generadores, resulta que son las siguientes:

  • El código en el que se usa el generador determina el momento en que se recibe el siguiente valor. El generador solo es responsable de devolver los valores, se controla desde el exterior.
  • Hay generadores asincrónicos. Le permiten trabajar con flujos de datos asincrónicos.

Lo principal en los generadores es que puede obtener el siguiente valor devuelto por el generador solo cuando es necesario en el código que usa el generador. Los generadores no devuelven todo de una vez. En algunas situaciones, esta característica puede ser muy conveniente.

Fuente

11. ¿Qué es "elevar variables"?


Dificultad: ****


La esencia del concepto de "elevar variables" es que los anuncios "suben" a la cima del alcance actual. Como resultado, la variable se puede usar antes de su declaración. Solo se generan declaraciones de variables, pero no su código de inicialización. Tenga en cuenta que el comportamiento de las variables declaradas con la palabra clave vares diferente del comportamiento de las variables y constantes declaradas con lety const.

Fuente

12. ¿Cuál será el siguiente código de salida?


Dificultad: ****


var output = (function(x) {
  delete x;
  return x;
})(0);

console.log(output);

Este código saldrá 0. El operador se deleteutiliza para eliminar las propiedades de los objetos. Y x, esto no es una propiedad de objeto, es una variable local. El operador deleteno afecta a las variables locales.

Fuente

13. ¿Cuál será el siguiente código de salida?


Dificultad: ****


var Employee = {
  company: 'xyz'
}
var emp1 = Object.create(Employee);
delete emp1.company
console.log(emp1.company);

Este código saldrá xyz. Una propiedad companyno es una propiedad de un objeto emp1, sino una propiedad de su prototipo. El operador deleteno elimina las propiedades prototipo de los objetos. Un objeto emp1no tiene su propia propiedad company. Puede verificar esto mediante:

console.log(emp1.hasOwnProperty('company')); // false

Si aún necesitamos eliminar esta propiedad, puede hacerlo contactando directamente al objeto Employee( delete Employee.company) o contactando al prototipo del objeto emp1utilizando su propiedad __proto__( delete emp1.__proto__.company).

Fuente

14. Cuéntanos sobre el patrón de diseño del prototipo.


Dificultad: ****


El prototipo es un patrón de diseño genérico. Se usa para crear objetos. Los objetos creados con él contienen valores copiados de su prototipo (del objeto de muestra). Esta plantilla también se denomina plantilla de Propiedades.

Un ejemplo del uso del patrón "prototipo" es la inicialización de ciertos objetos con valores estándar almacenados en la base de datos. Dichos valores registrados en el prototipo se copian en nuevos objetos sin acceder a la base de datos.

Cabe señalar que este patrón rara vez se usa en los idiomas clásicos. JavaScript utiliza un modelo de herencia prototipo. Este patrón se utiliza en el diseño de nuevos objetos y sus prototipos.

Fuente

15. ¿Qué es una "zona muerta temporal" en ES6?


Dificultad: ****


El ES6 realizó el levantamiento de variables y constantes declaradas usando las palabras clave lety const(esto se hace y el aumento de la entidad declarada usando palabras clave var, classy function). Sin embargo, el código tiene una zona que se extiende desde la entrada en el alcance hasta la declaración de una variable o constante. Al acceder a una variable o constante en esta zona, se generará un error. Esta es la "Zona muerta temporal" (TDZ).

//console.log(aLet)  //  ReferenceError

let aLet;
console.log(aLet); // undefined
aLet = 10;
console.log(aLet); // 10

En este ejemplo, el TDZ termina después de la declaración aLet, pero no después de aLetque se asigna el valor.

Fuente

16. ¿Puedes describir la diferencia principal entre los métodos de matriz forEach () y map ()? ¿En qué situaciones preferiría uno de estos métodos a otro?


Dificultad: ****


Para comprender la diferencia entre estos métodos, hablemos sobre las características de cada uno de ellos.

Así es como funciona .forEach():

  • Se itera sobre los elementos de la matriz.
  • Realiza la función de devolución de llamada que se le pasa para cada elemento de la matriz.
  • No devuelve nada.

const a = [1, 2, 3];
const doubled = a.forEach((num, index) => {
  //  -  num /  index.
});

// doubled = undefined

Aquí hay una breve descripción del método .map():

  • Se itera sobre los elementos de la matriz.
  • Convierte cada elemento de la matriz original en un elemento de la nueva matriz, llamando a la función que se le pasa para cada elemento de la matriz original.

const a = [1, 2, 3];
const doubled = a.map(num => {
  return num * 2;
});

// doubled = [2, 4, 6]

Como resultado, resulta que la principal diferencia entre .forEach()y .map()es que .map()devuelve una nueva matriz. Si necesita obtener el resultado de convertir los elementos de la matriz original sin cambiar esta matriz, entonces debe elegir .map(). Si solo necesita iterar sobre los elementos de la matriz, puede usarlo .forEach().

Fuente

17. ¿Cuál es la diferencia entre una variable no declarada, una variable que contiene un valor nulo y una variable indefinida? ¿Cómo verificar una variable por el hecho de que no está declarada, así como nula e indefinida?


Dificultad: ****


Se crea una variable no declarada cuando se asigna un valor a un identificador que no se declaró previamente mediante var, leto const. Las variables no declaradas se declaran en el ámbito global, fuera del ámbito actual. En modo estricto, se produce una excepción al intentar asignar un valor a una variable no declarada ReferenceError. No se recomienda el uso de variables no declaradas, al igual que no se recomienda el uso de variables globales. Deben evitarse por todos los medios. Para protegerse de las consecuencias del uso de variables no declaradas, use el bloque try/catch.

function foo() {
  x = 1; //     ReferenceError
}

foo();
console.log(x); // 1

Una variable que contiene undefinedes una variable declarada a la que no se le asigna un valor. El valor undefinedforma su propio tipo de datos. Si la función no devuelve nada y el resultado de su llamada se escribe en una variable, caerá en esta variable undefined. Para organizar una verificación undefined, puede usar el operador de igualdad estricta ( ===) o el operador typeofque devuelve una cadena undefined. Tenga en cuenta que cuando se verifica, undefinedno se debe utilizar la no-operador de igualdad estricta ( ==), ya que considera que el undefinedy los valores a ser igual null.

var foo;
console.log(foo); // undefined
console.log(foo === undefined); // true
console.log(typeof foo === 'undefined'); // true

console.log(foo == null); // true.        undefined!

function bar() {}
var baz = bar();
console.log(baz); // undefined

Una variable que contiene un valor nulldebe establecerse explícitamente en este valor. Simboliza la ausencia de significado y difiere de undefined-variable en que el valor en él se le asignó explícitamente. Para verificar el valor null, es suficiente usar el operador de igualdad estricta. Para verificar null, como en el caso de verificar undefined, no se debe usar el operador de igualdad no estricto, que considera los valores de nulle igual undefined.

var foo = null;
console.log(foo === null); // true
console.log(typeof foo === 'object'); // true

console.log(foo == undefined); // true        null!

Intento nunca dejar variables en un estado no declarado, o en un estado donde se declaran, pero no se les asigna ningún valor explícitamente. Si no voy a escribir un valor en una variable inmediatamente después de su declaración, lo escribo null. Si usa un linter, generalmente informa casos de uso de variables no declaradas.

Fuente

18. Cuéntanos sobre el módulo de diseño "Módulo abierto"


Dificultad: *****


La plantilla "Módulo revelador" es una variación de la plantilla "Módulo". El propósito de usar este patrón es admitir la encapsulación y descubrir algunas propiedades y métodos devueltos en el objeto literal. Así se verá la implementación directa de esta plantilla:

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();        // : This is a method I want to expose!
Exposer.second();       // : Inside a private method!
Exposer.methodToExpose; // undefined

El inconveniente obvio de esta plantilla es que no puede usar métodos privados cuando la usa.

Fuente

19. ¿Cuál es la diferencia entre los objetos Map y WeakMap?


Dificultad: *****


Estos objetos se comportan de manera diferente si una variable que contiene una referencia a un objeto que es la clave de uno de los pares clave / valor no está disponible. Aquí hay un ejemplo:

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

Una vez completada la ejecución de IIFE, ya no tendremos acceso a los objetos ay b. Por lo tanto, el recolector de basura elimina la clave bde weakmapy borra la memoria. Pero el contenido mapsigue siendo el mismo.

Como resultado, resulta que los objetos WeakMappermiten al recolector de basura deshacerse de aquellos registros que no están referenciados en variables externas. Los objetos mapalmacenan pares clave / valor independientemente de la presencia o ausencia de referencias clave externas. Lo mismo puede decirse sobre la implementación de la estructura de datos Maputilizando matrices ordinarias. Se WeakMaputilizan las referencias clave "débiles". No interfieren con el funcionamiento del recolector de basura si no hay otras referencias al objeto utilizado como clave.

Fuente

20. ¿Cómo se pasan los parámetros a la función JavaScript: por referencia o por valor?


Dificultad: *****


Los parámetros siempre se pasan por valor, pero las referencias a objetos se escriben en variables que representan objetos. Por lo tanto, cuando un objeto se transfiere a una función y se cambia la propiedad de este objeto, este cambio se guarda en el objeto incluso cuando la función sale. Como resultado, existe la sensación de que los parámetros en la función se pasan por referencia. Pero si cambia el valor de la variable que representa el objeto, este cambio no afectará a los objetos que están fuera de la función.

Aquí hay un ejemplo:

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

Esto es lo que generará este código:

10
changed
unchanged

Fuente

21. ¿Cómo organizar una "congelación profunda" de un objeto?


Dificultad: *****


Para proporcionar la "congelación profunda" de un objeto usando Object.freeze(), debe crear una función recursiva que "congele" las propiedades del objeto, que también son objetos.

Aquí hay un ejemplo de una "congelación" ordinaria de un objeto:

let person = {
    name: "Leonardo",
    profession: {
        name: "developer"
    }
};
Object.freeze(person); //   
person.profession.name = "doctor";
console.log(person); // { name: 'Leonardo', profession: { name: 'doctor' } }

Aquí está la "congelación profunda":

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"; // TypeError: Cannot assign to read only property 'name' of object

El mensaje de error se muestra solo en modo estricto. En modo normal, el valor no cambia sin mensajes de error.

Fuente

22. ¿Por qué los programadores de JavaScript tienen problemas para usar esta palabra clave?


Dificultad: *****


Lo más importante a entender thises que las funciones no tienen un valor fijo this. Este valor depende de cómo se llama la función. Si decimos que se llama a una función con algún valor específico this, esto significa que este valor se determina no durante la declaración de la función, sino durante su llamada. Aquí hay algunas características this:

  • Si la función se llama en la forma habitual (es decir, usando la construcción de vista someFunc()), se thisreferirá al objeto global (en el navegador esto window). Si el código se ejecuta en modo estricto, thisse escribirá un valor undefined.
  • Si la función se llama como método de un objeto, la palabra clave thisestará representada por el objeto al que pertenece el método.
  • call apply, this , call apply.
  • , this .
  • , new, this , prototype -.
  • Si la función se ha creado usando el aprieto método , entonces la thisfunción de palabras clave estará vinculado rígidamente al valor pasado bindcomo primer argumento. Esta es la única excepción a la regla de que las funciones no tienen un valor codificado this. Las funciones creadas usando bindson inmutables this.

Fuente

23. Compare el uso de la construcción y generadores asíncronos / en espera para implementar la misma funcionalidad


Dificultad: *****


  • Al iterar un generador utilizando un método, .next()cada llamada a este método devuelve un solo valor utilizando la palabra clave yield. Cuando se utiliza la construcción async / await, las expresiones de wait se ejecutan secuencialmente.
  • El diseño asíncrono / espera simplifica la implementación de un caso de uso de generador específico.
  • Los valores devueltos por el generador siempre tienen la forma {value: X, done: Boolean}, y las funciones asincrónicas devuelven las promesas resueltas con el valor X, o fallan.
  • Una función asincrónica se puede convertir en un generador mediante promesas. El siguiente es un ejemplo de tal conversión.

Aquí está la función asincrónica:

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

Aquí hay un generador similar.

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

Fuente

Estimados lectores! ¿Qué preguntas de JavaScript se te ocurrieron durante tus entrevistas?


All Articles