📜 ⬆️ ⬇️

Analysis of issues at the hh.ru stand at # HolyJS18

We tried to do something interesting and unusual for you. I really hope that we have succeeded. We did not want to leave you without answers and explanations why this is so. Let's figure it out.


First I want to remind you how the competition took place, there were 4 rounds with 15 questions about JS, 1 non-competitive round with 15 questions about React and a final with 10 questions.


image


Under the cut - analysis of the tasks of the first 4 rounds.


This is the second part of our analysis.
Analysis of questions about React here


How did we all do it? We decided that we need to generate about 80-90 questions so that there is a stock to choose from. After that, we divided everything into topics:



After that distributed questions on 4 rounds. We tried to make all the tours the same in complexity, for this we did several passes by passing these tests and determining where the questions were simpler, where it was more difficult and replaced the outstanding questions with more appropriate ones. And we did in each round about the same number of questions on a particular topic. As a result, it turned out that in different tours there were similar, but not identical questions.


Because of this, sorting out the tours seems not very convenient because there will be a lot of duplicate explanations, I suggest looking at them by topic. Let's start with the simplest.


Questions for attention:


What will be displayed in the console?


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

Answer + parse

d) 0 1 2
Here between the numbers , , and not . If you format the question like this:
console.log(0, 1 + 0, 2); everything will become clear


What will be displayed in the console?


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

Answer + parse

d) error
a is created not as a variable (not a Variable Declaration), here an implicit assignment (Assignment Expression) to this.a which very often may not be what you expect, because the global variable window.a will be created in strict mode, this is prohibited.


What will be displayed in the console?


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

Answer + parse

d) error
This is a functional expression (expression) - the function name in this case is local to the function. To call a function, you must call foo , not bar . If this were a declaration, the answer would be number .


Questions about working with fractional numbers:


What will be displayed in the console?


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

Answer

c) 0.010000000000000002


What will be displayed in the console?


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

Answer + parse

a) 0.30000000000000004
** - this is an analogue of Math.pow. We put 0.1 in the square - it should be 0.01 , but in JS (as in many other languages) there is a known problem with the precision of operations when working with floating point numbers . It will be 0.010000000000000002 This is due to the fact that in the binary system an infinite fraction is obtained. exactly 64 bits are always allocated to a number in JS - all numbers are always double precision floating point. The same will happen when adding.


We turn to the questions a little more difficult.


Events in the browser:


There is an event handler on the element. What values ​​inside this handler will always be the same?


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

Answer + parse

c) event.currentTarget and this
this will always point to the element.
currentTarget - the element on which the event hangs
target - the element on which the event occurred


What will this code get by clicking on a 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 

Answer + parse

c) 2 3
onclick will add the console.log(1) handler, but in the next line we will rub it with a new function and only console.log(2) remains. onclick is a DOM property it is always the same
Events will work in the order in which they are hung, first 2 will be displayed, then 3.
If we did addEventListener several times, then each of them would work, since Handlers add events to the queue.


Questions section about various APIs


defineProperty:


What will this code output?


 (() => { 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)  

Answer

b) 2, 2


What will this code output?


 (() => { '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,  

Answer

d) 2, error


What will this code output?


 (() => { 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)  

Answer + parse

c) 2, 3
In all the questions above, knowledge of the defineProperty method is defineProperty and specifically, the settings of writable . If it is set to false then it is forbidden to change values ​​to the key passed in the second parameter in defineProperty . The only difference is that without strict mode - the use strict engine will pretend that everything is fine, but the value will not change, and in strict mode there will be an error.


increment:


What will this code output?


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

Answer

a) 5


What will this code output?


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

Answer

d) error
When using the postfix form of the increment, the value before the increase is returned.
And with prefix after, i.e. console.log(++5) would print 6
const can not be overwritten, but because Number is a primitive, then when it is increased, the variable will overwrite the new value and there will be an error.


Set:


What will this code output?


 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)  

Answer

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


What will this code output?


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

Answer

b) 3


What will this code output?


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

Answer

b) 5
Set is a set; by definition, it cannot contain identical values. The question is how these values ​​are compared. Primitives are compared by value, and objects by reference.
It does not in itself lead to data types and can store values ​​of any type 1e1 and 0xA - they will be converted into a decimal system and it will turn out 10 .
And new objects are always not equal: console.log({} == {}) returns false because Objects will be created by new in different places of memory and their links will not be equal.


What will this code output?


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

Answer

a) NaN
It is impossible to divide infinity into infinity and subtract infinity from infinity because from a mathematical point of view, uncertainty is obtained, the same thing will happen when multiplying Infinity and 0 errors; mathematical operations do not cause - will be NaN


Questions about Spread:


What will this code output?


 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)  

Answer

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


What will this code output?


 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)  

Answer + parse

b) [1, 2, [3, 4], 5, 6]
Spread operator is used to parse an object or an array into parts. Takes values ​​from the entity after ... and copies them to the created one. It is worth noting that for an array and an object it expands to 1 level ...[[1]] returns an array with one element, and not the element itself. In objects, duplicate values ​​cannot be, therefore, the values ​​disclosed after will overwrite those that were disclosed earlier. This can be used to specify default parameters.


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

All default values ​​will be overridden by passed values, if any.


What will this code output?


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

Answer + parse

b) -10
Exhaustive description with MDN :
If the parseInt function encounters a character that is not a number in the specified number system, it skips this and all subsequent characters (even if they are suitable) and returns an integer converted from the part of the string that precedes this character. parseInt cuts the fractional part of the number. Spaces at the beginning and end of the line are allowed.


What will this code output?


 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 

Answer + parse

d) 12 12 18
Proxy intercepts all calls to the object. In this case, we only proxify the get method and always return 12 no matter which field of the object we are accessing. In this case, we do not touch set, and when accessing a proxy, the value in the object will be replaced.


arrays:


What will this code output?


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

Answer

c) 6


What will this code output?


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

Answer + parse

a) undefined
When we create an Array with one numeric argument, it means the length of the array. This creates an empty array, all values ​​are undefined . The same thing happens if you create a call to a nonexistent array field. It is worth noting that if you transfer not a number to Array , an array with this element will be returned. Array('a') returns ['a']


logical operations && , || , == etc .:


What will this code output?


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

Answer

c) undefined


What will this code output?


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

Answer

c) 2


What will this code output?


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

Answer

c) 2


What will this code output?


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

Answer

d) null


What will this code output?


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

Answer + parse

d) {}
An empty array [] is true as an empty object {} .
The empty string '' , null and undefined is false
Logical or || - returns the left operand, if it is true, in other cases returns the right operand.
Logical and && - returns the left operand if it is false, otherwise it returns the right operand.


This can sometimes be found in the code, before the default parameters appear, it was often written like this - if there are no parameters in the function, we take the default parameters:


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

Now in React, it is often checked that if the condition is true, then we render something:


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

array comparison:


What will this code output?


 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)  

Answer

a) false


What will this code output?


 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] 

Answer

d) [true, true]


What will this code output?


 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)  

Answer

a) true


What will this code output?


 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)   

Answer + parse

b) true
When == compare the link.
with the operation >, >=, <, <= operands are converted to primitives and the arrayFoo method is called on arrayFoo , which should return the primitive value of arrayFoo , but it returns a reference to the same array. Next, the conversion to a primitive value occurs by calling the toString method, which in turn returns the string representation of the array in the form "1,2,3,4", compares lexicographically two arrays and returns true


What will this code output?


 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 

Answer + parse

b) true, true, false
Exhaustive explanation with MDN :
The behavior of this method (talking about Object.is ) is not similar to the === operator. The === operator (as well as the == operator) considers the numeric values ​​of -0 and +0 equal, and the value of Number.NaN not equal to itself.


Questions about hoisting:


What will this code output?


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

Answer

c) error


What will this code output?


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

Answer

d) 3


What will this code output?


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

Answer

b) undefined


What will this code output?


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

Answer

b) undefined 0


Does the function call work?


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

Answer

a) yes


What will this code output?


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

Answer + parse

c) 11


The function declarations pop up, but there is no expression.
var pops up, but until initialization is equal to undefined .
let and const do not float and have scope in the block i.e. limited to {} .


In order for the loop to work properly with var you must use a closure, the value will be stored in it.
(it used to be a classic task for interviews, and now we have let)


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

What will this code output?


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

Answer + parse

c) 1
None of the operators is a string, + leads to a number. It turns out 1 + 0


What will this code output?


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

Answer + parse

c) -100
The array is reduced to a string, after that, because of - we cast to a number, we get -100 , then we cast the array to false , and this is 0


What will this code output?


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

Answer + parse

c) ', 1'
Call toString on the object, while toString will also be called on all elements of the array. [].toString returns an empty string '' . It turns out , + 1 - the answer ,1 .


What will this code output?


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

Answer + parse

b) '1005'
The array is cast to a string, and then concatenation takes place.


What will this code output?


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

Answer + parse

b) '1 [object Object] 2'
Convert to a string - here is just a concatenation.


What will this code output?


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

Answer + parse

d) error
For the number point . means the beginning of the fractional part, we expect a number there - there will be an error.
To make this example work properly, you need to write 10..toString()


What will this code output?


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

Answer + parse

c) 6
Here everything is led to a number, it turns out 5 + 0 - 0 + 1


What will this code output?


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

Answer + parse

b) NaN
We bring everything to number, when adding numbers with NaN - we get NaN


What will this code output?


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

Answer + parse

b) 7
Here the lines after the first concatenation are: '0x11' - '1e1' . Because of the sign - we bring everything to a number.
0x11 - hexadecimal notation in decimal is 17 .
1e1 - the exponential form is the same as 1 * 10 ** 1 - i.e. just 10 .


typeof:


What will this code output?


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

Answer

b) 'string'


What will this code output?


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

Answer + parse

b) 'object'
typeof always returns a string that has lower priority than a function call, so the function is executed first, and typeof is applied to the result returned by it. Function objects are inherited from Function.prototype. SPECA explicitly determines that this is an object.


event loop:


Let's start with 2 questions about promises.


What will this code output?


 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 

Answer

c) 2 3


What will this code output?


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

Answer + parse

c) undefined
Promise.reject - returns promise in rejected state.
It is necessary to remember that then takes 2 parameters, onFulfill and onReject Kolbeck. If an error occurs before this, then we get into the onReject callback. If there is no error in it, then we fall into the onFulfill next. And do not forget that () => {} returns not an empty object, but undefined , to return an empty object, you should write like this: () => ({})


the order of the tasks.


What will this code output?


 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 

Answer

b) 2.1


What will this code output?


 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 

Answer

b) after, in promise, in timeout


What will this code output?


 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 

Answer

a) 1, 2


What will this code output?


 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 

Answer

b) FUS DAH! RO


What will this code output?


 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' 

Answer + parse

c) 1, 2, 3, 'promise1 resolved', 'setTimeout'
At first, all synchronous calls are triggered, after that, when the call stack is empty, what is in the queue is called (asynchronous tasks). Microtasks are performed first - promises and mutation observer . At the end of the current task, all microtasks are executed, in connection with this microtasks, you can block the event loop, after the task is completed in the browser, rendering takes place. After that, the macro taskouts are executed.
This is a very simplified example, in more detail I would advise you to watch the speech of Mikhail Bashurov


And the last question promise against await


What will this code output?


 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


Answer + parse

c) 2 3 1


According to the spec, first the promises added through then should be executed and only after that should they continue
asynchronous function execution. Specs . For a more detailed understanding of why this is so, I advise you to read an excellent article on v8.dev


')

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


All Articles