Nous avons essayé de faire quelque chose d'intéressant et d'inhabituel pour vous. J'espère vraiment que nous réussirons. Nous ne voulions pas vous laisser sans réponse et aucune explication pourquoi. Faisons les choses correctement.
Pour commencer, je veux vous rappeler comment s'est déroulé le concours, il y a eu 4 séries de 15 questions sur JS, 1 série hors compétition de 15 questions sur React et une finale de 10 questions.

Sous la coupe - l'analyse des tâches des 4 premiers tours.
Ceci est la deuxième partie de notre analyse.
Réagissez aux questions ici
Comment avons-nous tous fait cela? Nous avons décidé que nous devions générer environ 80 à 90 questions afin d'avoir un stock parmi lequel choisir. Après cela, nous avons tout divisé en thèmes:
- événements du navigateur
- diverses API (Array, Set, defineProperty, etc.),
- attention
- travailler avec des nombres fractionnaires
- levage
- boucle d'événement
- conversion de type
- typeof
- logique (avec ET logique et OU)
Après cela, les questions ont été réparties en 4 tours. Nous avons essayé de rendre toutes les visites identiques en complexité, pour cela nous avons effectué plusieurs visites en passant ces tests et en déterminant où les questions sont plus faciles, où elles sont plus difficiles et en remplaçant les questions en suspens par des questions plus appropriées. Et nous avons posé à peu près le même nombre de questions sur un sujet particulier à chaque tour. En conséquence, il s'est avéré que dans différentes tournées, il y avait des questions similaires, mais pas les mêmes.
Pour cette raison, il ne semble pas très pratique de trier les visites car il y aura beaucoup d'explications en double, je suggère de les regarder par sujet. Commençons par le plus simple.
Questions Ă l'attention:
Qu'est-ce qui sera affiché dans la console?
console.log(0,1 + 0,2); a) 0.30000000000000004 b) 0.3 c) 2 d) 0 1 2
Réponse + analysed) 0 1 2
Ici se situe entre les chiffres, et non .
si vous formatez la question comme ceci:
console.log(0, 1 + 0, 2);
tout deviendra clair
Qu'est-ce qui sera affiché dans la console?
(() => { 'use strict'; a = null + undefined; console.log(a); })(); a) 0 b) NaN c) null d)
Réponse + analysed) erreur
a
n'est pas créé en tant que variable (pas une déclaration de variable), ici il y a une expression d'assignation implicite à this.a
qui très souvent peut ne pas être ce que vous attendez, car une variable globale window.a
sera créée en mode strict, ceci est interdit.
Qu'est-ce qui sera affiché dans la console?
let foo = function bar() { return 123; }; console.log( typeof bar() ); a) 'function' b) 'number' c) 'undefined' d)
Réponse + analysed) erreur
Il s'agit d'une expression fonctionnelle (expression) - le nom de la fonction dans ce cas est local Ă la fonction. Pour appeler une fonction, vous devez appeler foo
, pas bar
. S'il s'agissait d'une déclaration, la réponse serait le number
.
Questions sur l'utilisation de nombres fractionnaires:
Qu'est-ce qui sera affiché dans la console?
console.log(0.1 ** 2); a) 0.2 b) 0.01 c) 0.010000000000000002 d) NaN
Qu'est-ce qui sera affiché dans la console?
console.log(0.1 + 0.2); a) 0.30000000000000004 b) 0.3 c) 2 d) NaN
Réponse + analysea) 0,30000000000000004
**
- il s'agit d'un analogue de Math.pow au carré 0.1
- il devrait s'avérer 0.01
, mais dans JS (comme dans de nombreuses autres langues) il y a un problème connu avec la précision des opérations lorsque vous travaillez avec des nombres à virgule flottante . Ce sera 0.010000000000000002
Ceci est dû au fait que dans le système binaire une fraction infinie est obtenue, car exactement 64 bits sont toujours alloués pour un nombre dans JS - tous les nombres sont toujours à virgule flottante double précision. La même chose se produit lors de l'ajout.
Nous passons aux questions un peu plus compliquées.
Événements dans le navigateur:
Il y a un gestionnaire d'événements sur l'élément. Quelles valeurs à l'intérieur de ce gestionnaire seront toujours les mêmes?
elem.onclick = function(event) { } a) event.target event.currentTarget b) event.target this c) event.currentTarget this d)
Réponse + analysec) event.currentTarget et ce
this
- pointera toujours vers un élément
currentTarget
- l'élément sur lequel l'événement se bloque
target
- l'élément sur lequel l'événement s'est produit
Que produira ce code en cliquant sur un 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
Réponse + analysec) 2 3
onclick ajoutera le gestionnaire console.log(1)
, mais dans la ligne suivante, nous le console.log(1)
avec une nouvelle fonction et il ne console.log(2)
que console.log(2)
. onclick
est une propriété DOM; elle en est toujours une
Les événements fonctionneront dans l'ordre dans lequel ils sont suspendus, d'abord 2 puis 3 seront affichés.
Si nous avons addEventListener
plusieurs fois, alors chacun d'eux fonctionnerait, car les gestionnaires ajoutent des événements à la file d'attente.
Section Questions sur les différentes API
defineProperty:
Que produira ce code?
(() => { 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)
Que produira ce code?
(() => { '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,
Que produira ce code?
(() => { 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)
Réponse + analysec) 2, 3
Dans toutes les questions ci-dessus, la connaissance de la méthode defineProperty
est defineProperty
et plus précisément, writable
paramètres writable
en writable
. S'il est défini sur false
il est interdit de modifier les valeurs de la clé transmise en tant que deuxième paramètre pour defineProperty
. La seule différence est que sans mode strict - use strict
moteur prétendra que tout va bien, mais il ne changera pas la valeur, et il y aura une erreur en mode strict.
incrément:
Que produira ce code?
let x = 5; console.log(x++); a) 5 b) 6 c) '5++' d)
Que produira ce code?
const a = 5; console.log(a++); a) 5 b) 6 c) '5++' d)
La réponsed) erreur
Lorsque vous utilisez la forme d'incrimination postfixe, la valeur est renvoyée avant l'augmentation.
Et avec le préfixe après, c'est-à -dire console.log(++5)
6
const
ne peut pas être écrasé; Le nombre est une primitive, lorsqu'il est incrémenté, la variable sera remplacée par la nouvelle valeur et il y aura une erreur.
Ensemble:
Que produira ce code?
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)
La réponseb) [1, 2, non défini, 3, 4, 5]
Que produira ce code?
let set = new Set([10, '10', new Number(10), 1e1, 0xA]); console.log(set.size); a) 5 b) 3 c) 2 d) 1
Que produira ce code?
let obj = {}; let set = new Set([obj, obj, {}, {}, {...{}}, {...obj}]); console.log(set.size); a) 6 b) 5 c) 2 d) 1
La réponseb) 5
Set
est un ensemble; par définition, il ne peut pas y avoir de valeurs identiques. La question est de savoir comment ces valeurs sont comparées. Les primitives sont comparées par valeur et les objets par référence.
Il ne cite pas lui-même les types de données et peut stocker des valeurs de tout type 1e1
et 0xA
- sera converti en système décimal et obtiendra 10
.
Et les nouveaux objets ne sont toujours pas égaux: console.log({} == {})
retournera false
puisque les objets seront créés d'une manière nouvelle dans différents lieux de mémoire et leurs liens ne seront pas égaux.
Que produira ce code?
console.log(Infinity / Infinity); a) NaN b) 1 c) Error d) Infinity
La réponsea) NaN
Il est impossible de diviser l'infini en infini et de soustraire l'infini de l'infini car d'un point de vue mathématique, l'incertitude est obtenue, la même chose se produit lorsque Infinity
multiplié et que 0
erreur ne provoque pas d'opérations mathématiques - il y aura NaN
Questions sur Spread:
Que produira ce code?
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)
La réponsea) {a: 2, b: 2, c: 4, d: 8}
Que produira ce code?
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)
Réponse + analyseb) [1, 2, [3, 4], 5, 6]
Spread
opérateur Spread
sert à analyser un objet ou un tableau en plusieurs parties. Il prend des valeurs de l'entité après ...
et les copie dans celle créée. Il convient de noter que pour un tableau et un objet, il s'ouvre à 1 niveau, c'est-à -dire ...[[1]]
retournera un tableau avec un élément, pas l'élément lui-même. Il ne peut pas y avoir de valeurs en double dans les objets, par conséquent, les valeurs divulguées après seront écrasées par celles qui ont été divulguées précédemment. Cela peut être utilisé pour spécifier les paramètres par défaut.
const fn = (actualProps) => ({ ...defaultProps, ...actualProps })
Toutes les valeurs par défaut seront remplacées par les valeurs transmises, le cas échéant.
Que produira ce code?
console.log(parseInt(' -10,3 ')); a) -10,3 b) -10 c) TypeError d) NaN
Réponse + analyseb) -10
Description exhaustive avec MDN :
Si la fonction parseInt rencontre un caractère qui n'est pas un nombre dans le système de numérotation spécifié, elle ignore celui-ci et tous les caractères suivants (même s'ils conviennent) et renvoie un entier converti à partir de la partie de la chaîne précédant ce caractère. parseInt coupe la partie fractionnaire d'un nombre. Les espaces au début et à la fin d'une ligne sont autorisés.
Que produira ce code?
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
Réponse + analysed) 12 12 18
Proxy
intercepte tous les appels à l'objet. Dans ce cas, nous mandatons uniquement la méthode get
et renvoyons toujours 12
quel que soit le champ de l'objet auquel nous accédons. Dans ce cas, nous ne touchons pas set, et lors de l'accès au proxy, la valeur de l'objet sera remplacée.
tableaux:
Que produira ce code?
let arr = []; arr[1] = 1; arr[5] = 10; console.log(arr.length); a) 1 b) 5 c) 6 d) 10
Que produira ce code?
let arr = new Array(3); console.log(arr[1]); a) undefined b) 1 c) 3 d)
Réponse + analysea) indéfini
Lorsque nous créons un Array
avec un argument numérique, cela signifie la longueur du tableau. Un tableau est créé vide, toutes les valeurs ne sont undefined
. La même chose se produit si vous créez l'accès à un champ de tableau inexistant. Il est à noter que si vous passez un nombre à Array
, un tableau avec cet élément sera retourné, c'est-à -dire Array('a')
renverra ['a']
opérations logiques &&
, ||
, ==
, etc.:
Que produira ce code?
console.log([] && 'foo' && undefined && true && false); a) [] b) 'foo' c) undefined d) true
Que produira ce code?
console.log(0 || 1 && 2 || 3); a) 0 b) 1 c) 2 d) 3
Que produira ce code?
console.log(0 || '' || 2 || undefined || true || false); a) 0 b) false c) 2 d) true
Que produira ce code?
console.log(2 && '1' && null && undefined && true && false); a) 2 b) false c) undefined d) null
Que produira ce code?
console.log([] && {} || null && 100 || ''); a) true b) 100 c) '' d) {}
Réponse + analysed) {}
Un tableau vide []
est true
comme un objet vide {}
.
La chaîne vide ''
, null
et undefined
est false
Logique ou ||
- retourne l'opérande gauche, si vrai, sinon retourne l'opérande droit.
Logique et &&
- renvoie l'opérande gauche, s'il est faux, sinon renvoie l'opérande droit.
Cela peut parfois être trouvé dans le code, avant l'apparition des paramètres par défaut, ils écrivaient souvent comme ceci - s'il n'y a pas de paramètres dans la fonction, alors nous prenons les paramètres par défaut:
function f(userParams) { var params = userParams || defaultParams; }
Maintenant, React vérifie souvent si la condition est vraie, puis nous rendons quelque chose:
{ isDivVisible && <div>bla-bla</div> }
comparaison de tableaux:
Que produira ce code?
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)
Que produira ce code?
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]
Que produira ce code?
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)
Que produira ce code?
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)
Réponse + analyseb) vrai
Quand ==
comparer par référence.
au cours de l'opération >, >=, <, <=
opérandes sont convertis en primitives et la méthode valueOf est appelée sur arrayFoo
, qui doit renvoyer la valeur primitive arrayFoo
, mais elle renvoie une référence au même tableau. Ensuite, une conversion en une valeur primitive se produit en appelant la méthode toString
, qui à son tour renverra une représentation sous forme de chaîne du tableau sous la forme "1,2,3,4" comparera deux tableaux lexicographiquement et renverra true
Que produira ce code?
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
Réponse + analyseb) vrai, vrai, faux
Explication complète avec MDN :
Le comportement de cette méthode (parler d' Object.is
) n'est pas le même que l'opérateur ===
. L'opérateur ===
(ainsi que l'opérateur ==
) considère les valeurs numériques -0
et +0
égales, et la valeur Number.NaN
pas égale à elle-même.
Questions sur le levage:
Que produira ce code?
console.log(str); const str = 'HeadHunter'; a) 'HeadHunter' b) undefined c)
Que produira ce code?
var arrayFunction = []; for (let i = 0; i <= 10; i++) { arrayFunction.push(() => i); } console.log(arrayFunction[3]()); a) 4 b) 0 c) 11 d) 3
Que produira ce code?
console.log(str); var str = 'HeadHunter'; a) 'HeadHunter' b) undefined c) null c)
Que produira ce code?
console.log(foo); var foo; foo = foo ? 1 : 0; console.log(foo); a) b) undefined 0 c) '' 1 d) 0 0
Un appel de fonction fonctionnera-t-il?
getCompanyName(); function getCompanyName() { return 'HeadHunter'; } a) b) , . c)
Que produira ce code?
var arrayFunction = []; for (var i = 0; i <= 10; i++) { arrayFunction.push(() => i); } console.log(arrayFunction[3]()); a) 4 b) 0 c) 11 d) 3
Réponse + analysec) 11
Des déclarations de fonctions apparaissent, mais aucune expression.
var
apparaît, mais jusqu'à ce que l'initialisation ne soit undefined
.
let
et const
n'apparaissent pas et ont une portée dans un bloc, c'est-à -dire limité à {}
.
Pour que la boucle fonctionne correctement avec var
vous devez utiliser une fermeture, la valeur y sera enregistrée.
(Auparavant, c'était une tâche classique pour les interviews, mais maintenant nous l'avons laissé)
var arrayFunction = []; for (var i = 0; i <= 10; i++) { (function(i) { arrayFunction.push(() => i); })(i); } console.log(arrayFunction[3]());
Que produira ce code?
console.log(true + false); a) true b) false c) 1 d) 0
Réponse + analysec) 1
Aucun des opérateurs n'est une chaîne, +
mène à un nombre. Il s'avère que 1 + 0
Que produira ce code?
console.log([] - 100 + ![]); a) false b) '-100' c) -100 d) NaN
Réponse + analysec) -100
Le tableau est converti en chaîne, après cela, à cause de -
nous convertissons en nombre, il s'avère -100
, puis nous convertissons le tableau en false
, et ceci est 0
Que produira ce code?
console.log([[], []] + 1); a) 1 b) '1' c) ',1' d) NaN
Réponse + analysec) », 1»
Nous toString
sur l'objet, tandis que toString
sera également appelé sur tous les éléments du tableau. [].toString
renverra une chaîne vide ''
. Il s'avère , + 1
- la réponse ,1
.
Que produira ce code?
console.log([] + 100 + 5); a) 105 b) '1005' c) 1005 d) NaN
Réponse + analyseb) «1005»
Le tableau est réductible en une chaîne, puis la concaténation se produit déjà .
Que produira ce code?
console.log(1 + { a: 3 } + '2'); a) 6 b) '1[object Object]2' c) 3 d) NaN
Réponse + analyseb) '1 [objet Object] 2'
Convertir en chaîne - c'est juste une concaténation.
Que produira ce code?
console.log(10.toString() + 10 + 0x1); a) '10101' b) 21 c) '10100x1' d)
Réponse + analysed) erreur
Pour un nombre, un point .
signifie le début de la partie fractionnaire, nous nous attendons à un nombre là - il y aura une erreur.
Pour que cet exemple fonctionne 10..toString()
, vous devez écrire 10..toString()
Que produira ce code?
console.log(5 + false - null + true); a) '0true' b) NaN c) 6 d)
Réponse + analysec) 6
Ici, tout est réduit à un nombre, il s'avère que 5 + 0 - 0 + 1
Que produira ce code?
console.log(true + NaN + false); a) true b) NaN c) false d) 1
Réponse + analyseb) NaN
Nous réduisons tout à un nombre, lorsque nous ajoutons des nombres avec NaN
- nous obtenons NaN
Que produira ce code?
console.log('0x1' + '1' - '1e1'); a) 17 b) 7 c) '0x111e1' d) NaN
Réponse + analyseb) 7
Il y a déjà des lignes après la première concaténation, on obtient: '0x11' - '1e1'
. À cause du signe -
nous apportons tout Ă un nombre.
0x11
- Le nombre hexadécimal en décimal est 17
.
1e1
- la forme exponentielle est la mĂŞme que 1 * 10 ** 1
- c'est-Ă -dire 10
seulement.
typeof:
Que produira ce code?
let foo = () => { return null; }; console.log( typeof typeof foo ); a) 'function' b) 'string' c) 'null' d)
Que produira ce code?
typeof function() {}.prototype; a) 'function' b) 'object' c) 'undefined' d)
Réponse + analyseb) «objet»
typeof
renvoie toujours une chaîne, a une priorité inférieure à l'appel de la fonction, donc la fonction est exécutée en premier, et typeof
est appliqué au résultat qui lui est renvoyé. Les objets fonction héritent de Function.prototype. Speck détermine explicitement qu'il s'agit d'un objet.
boucle d'événement:
Commençons par 2 questions sur les promesses.
Que produira ce code?
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
Que produira ce code?
Promise.reject('foo') .then(() => Promise.resolve('bar'), () => {}) .then((a) => {console.log(a)}) a) foo b) bar c) undefined d)
Réponse + analysec) indéfini
Promise.reject
- renvoie une promesse dans un état rejeté.
Il faut se rappeler que prend then
2 paramètres, les onFulfill
et onReject
. Si une erreur se produit avant cela, nous onReject
rappel onReject
. S'il n'y a pas d'erreur, alors nous onFulfill
Ă onFulfill
then
. Et n'oubliez pas que () => {}
ne renvoie pas un objet vide, mais undefined
, pour retourner un objet vide, vous devez écrire comme ceci: () => ({})
l'ordre des tâches.
Que produira ce code?
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
Que produira ce code?
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
La réponseb) après, dans la promesse, dans le délai d'attente
Que produira ce code?
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
Que produira ce code?
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
Que produira ce code?
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'
Réponse + analysec) 1, 2, 3, 'promesse1 résolue', 'setTimeout'
Tout d'abord, tous les appels synchrones sont déclenchés, après quoi, lorsque la pile d'appels est vide, ce qui se trouve dans la file d'attente est appelé (tâches asynchrones). Tout d'abord, microtâche - promesses et mutation observer
. À la fin de la tâche en cours, toutes les microtâches sont exécutées, en liaison avec cette microtâche, vous pouvez bloquer la boucle d'événements, une fois la tâche terminée, le navigateur s'affiche. Après cela, les tâches de macro - les délais d'attente sont exécutés.
Ceci est un exemple très simplifié, plus en détail je vous conseille de voir le discours de Mikhail Bashurov
Et la dernière question promet vs attend
Que produira ce code?
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
Réponse + analysec) 2 3 1
Selon la spécification, les promesses ajoutées then
doivent d'abord être exécutées, et seulement après cela, vous devez continuer
exécution d'une fonction asynchrone. Speck . Pour une compréhension plus détaillée de pourquoi il en est ainsi, je vous conseille de lire un excellent article sur v8.dev