├── README.md
├── img
└── ninja_inter.png
└── index.js
/README.md:
--------------------------------------------------------------------------------
1 |
2 |

3 |
Вопросы и ответы для собеседования Front-end/JS разработчика и не только
4 |
Хотите подготовиться к собеседованию по JavaScript и ищете вопросы, на которых можно попрактиковаться? Если так — считайте, что ваши поиски окончены.
5 |
6 |
7 | Мы собрали много вопросов по JavaScript, предназначенных для тех, кто хочет превратиться из джуниора в сеньора, для тех, кто стремится успешно пройти собеседование в сфере фронтенд-разработки и получить интересное предложение от работодателя.
8 |
9 | ## Содержание
10 |
11 | 0. [Послесловие](#epilogue)
12 | 1. [Какие навыки необходимо обязательно подтянуть для собеседования?](#wtf_skills)
13 | 2. [Объясните особенности проверки равенства значений в JavaScript.](#one)
14 | 3. [Приведите примеры приведения к логическому типу значений, не относящихся к этому типу](#two)
15 | 4. [Что такое IIFE?](#4)
16 | 5. [Когда следует использовать стрелочные функции, которые появились в ES6?](#5)
17 | 6. [В чём разница между ES6-классами и конструкторами функций?](#6)
18 | 7. [Расскажите о методе Function.prototype.bind().](#7)
19 | 8. [Для чего обычно используются анонимные функции?](#8)
20 | 9. [В чём разница между методом Object.freeze() и ключевым словом const?](#9)
21 | 10. [Что такое «генератор»?](#10)
22 | 11. [Когда стоит использовать генераторы?](#11)
23 | 12. [Что такое «поднятие переменных»?](#12)
24 | 13. [Что выведет следующий код?](#13)
25 | 14. Что такое всплытие событий?
26 | 15. Что такое перехват событий?
27 | 16. Можете ли вы объяснить каждую часть URL?
28 | 17. В чем разница между npm и npx?
29 | 18. Что такое WebAssembly?
30 | 19. Как работает CSS Flexbox?
31 | 20. Что такое AJAX и для чего он нужен?
32 | 21. Что такое JAMstack и как с ним работать?
33 | 22. [Что такое «временная мёртвая зона» в ES6?](#22)
34 | 23. [Можете ли вы описать основное различие методов массивов forEach () и map ()? В каких ситуациях вы предпочли бы один из этих методов другому?](#23)
35 | 24. [Расскажите о шаблоне проектирования «Прототип»](#24)
36 | 25. [Чем отличаются друг от друга необъявленная переменная, переменная, содержащая значение null, и undefined-переменная? Как проверить переменную на предмет того, что она необъявлена, а также на null и undefined?](#25)
37 | 26. [Расскажите о шаблоне проектирования «Открытый модуль»](#26)
38 | 27. [В чём разница между объектами Map и WeakMap?](#27)
39 | 28. [Как в JavaScript-функции передаются параметры: по ссылке или по значению?](#28)
40 | 29. [Как организовать «глубокую заморозку» объекта?](#29)
41 | 30. [Почему JavaScript-программисты испытывают проблемы при использовании ключевого слова this?](#30)
42 | 31. [Сравните использование конструкции async/await и генераторов для реализации одного и того же функционала.](#31)
43 | 32. [Напишите функцию сложения вида add(num1)(num2).](#32)
44 | 33. [Что выведется в консоль ? Объясните почему.](#33)
45 | 34.
46 |
47 | ### Какие навыки необходимо обязательно подтянуть для собеседования?
48 |
49 | К любому собеседованию нельзя быть готовым на 100%. Давайте рассмотрим несколько пунктов, чтоб чувствовать себя немного увереннее.
50 |
51 | - Самое основное правило, это умение читать код. В сложных ситуациях это поможет Вам быстрее выявить проблему и устранить ее.
52 | - Обязательно подтяните свои навыки при работе с регулярными выражениями. Регулярные выражения могут быть довольно непростыми. Тем не менее они могут сделать для вас многое. Наиболее очевидный вариант использования - поиск определенных фрагментов текста в больших кодовых базах.
53 | - Еще одним, не менее важным пунктом является знание терминологии. Попробуйте выписать все термины, которые вы знаете и кратко рассказать о каждом.
54 |
55 | ### Объясните особенности проверки равенства значений в JavaScript
56 |
57 | В JavaScript есть два оператора для проверки равенства величин. Первый — это так называемый оператор строгого равенства. Второй — оператор нестрогого равенства, при использовании которого может производиться преобразование типов проверяемых величин.
58 |
59 | - Оператор __строгого равенства__ `(===)` проверяет значения на равенство, не выполняя при этом преобразования типов.
60 | - Оператор __нестрогого равенства__ `(==)` проверяет значения на равенство, выполняя их приведение к общему типу.
61 |
62 | ```js
63 | const a = "42";
64 | const b = 42;
65 |
66 | a == b; // true
67 | a === b; // false
68 | ```
69 |
70 | Вот некоторые правила, касающиеся использования различных операторов проверки равенства в JavaScript:
71 |
72 | - Если любое из сравниваемых значений может быть значением `true` или `false` — постарайтесь избегать оператора `==`. Используйте оператор `===`.
73 | - Используйте оператор `===` в том случае, если работаете со следующими значениями: `0`, `""` или `[]` (пустой массив).
74 | - Во всех остальных случаях можете безопасно использовать оператор `==`. Причём, это не только безопасно, но и способствует упрощению кода и улучшению его читабельности.
75 |
76 | ### Приведите примеры приведения к логическому типу значений, не относящихся к этому типу
77 |
78 | Суть этого вопроса в том, чтобы выяснить, какие значения, в случае преобразования их к логическому типу, превращаются в `false`, а какие — в `true`.
79 |
80 | Вот список значений, которые можно назвать __ложными__ (falsy). Они, при преобразовании к логическому типу, превращаются в значение `false`:
81 |
82 | - `""` (пустая строка).
83 | - `0, -0, NaN` (не-число).
84 | - `null, undefined`.
85 |
86 | Любое значение, которое не входит в этот список, при его преобразовании к логическому типу, превращается в `true` (такие значения называют __истинными__ — truthy). Например:
87 |
88 | - `"hello"`.
89 | - `42`.
90 | - `[ ], [ 1, «2», 3 ]` (массивы).
91 | - `{ }, { a: 42 }` (объекты).
92 | - `unction foo() { .. }` (функции).
93 |
94 | ### Что такое IIFE?
95 |
96 | IIFE (Immediately Invoked Function Expression) — это немедленно вызываемое функциональное выражение. Такое выражение выполняется немедленно после создания.
97 |
98 | ```js
99 | (function IIFE(){
100 | console.log("Hello, World!");
101 | })();
102 | // "Hello, World!"
103 | ```
104 |
105 | Этот паттерн часто используется для того чтобы не допустить загрязнения глобального пространства имён. Дело в том, что переменные, объявленные в IIFE (как и в любой другой обычной функции), невидимы за пределами этой функции.
106 |
107 | ### Когда следует использовать стрелочные функции, которые появились в ES6?
108 |
109 | Вот простые правила по использованию различных способов объявления функций, которыми я руководствуюсь, разрабатывая код для сред, поддерживающих стандарт ES6 и более новые стандарты:
110 |
111 | - Используйте ключевое слово function в глобальной области видимости и для свойств `Object.prototype`.
112 | - Используйте ключевое слово function для конструкторов объектов.
113 | - В остальных случаях используйте стрелочные функции.
114 |
115 | Как видите, стрелочные функции рекомендуется использовать практически везде. У такого положения дел есть несколько причин:
116 |
117 | - Удобная работа с контекстом. Стрелочные функции используют значение `this` окружающего контекста, не имея собственного `this`. Если такие функции применяются последовательно, без использования обычных функций в сложных конструкциях, это обеспечивает безопасную работу с контекстом.
118 | - Компактность. Код стрелочных функций легче вводить и легче читать. Возможно, это преимущество стрелочных функций перед обычными покажется вам спорным и зависящим от точки зрения каждого конкретного разработчика.
119 | - Ясность кода. Если практически весь код представлен стрелочными функциями, любая обычная функция выделяется в таком коде тем, что создаёт собственный контекст. Применяя стрелочные функции, программист создаёт более понятный код, в котором легче, чем в коде без стрелочных функций, работать с `this`.
120 |
121 | ### В чём разница между ES6-классами и конструкторами функций?
122 |
123 | Сначала рассмотрим примеры.
124 |
125 | Функция-конструктор:
126 |
127 | ```js
128 | function Person(name) {
129 | this.name = name;
130 | }
131 | ```
132 |
133 | ES6-класс:
134 |
135 | ```js
136 | class Person {
137 | constructor(name) {
138 | this.name = name;
139 | }
140 | }
141 | ```
142 |
143 | Если речь идёт о создании простых объектов, то конструкторы и классы, используемые для этой цели, выглядят очень похоже.
144 |
145 | Основная разница между конструкторами и классами проявляется при использовании наследования. Если нам нужно создать класс Student, являющийся подклассом класса Person, и добавить к этому новому классу поле studentId, то вот как будет выглядеть код, в котором используются конструкторы, и код, в котором применяются классы.
146 |
147 | Функция-конструктор:
148 |
149 | ```js
150 | function Student(name, studentId) {
151 | // Вызываем конструктор суперкласса для инициализации полей, унаследованных от него.
152 | Person.call(this, name);
153 |
154 | // Инициализация собственных полей объекта.
155 | this.studentId = studentId;
156 | }
157 |
158 | Student.prototype = Object.create(Person.prototype);
159 | Student.prototype.constructor = Student;
160 | ```
161 |
162 | ES6-класс:
163 |
164 | ```js
165 | class Student extends Person {
166 | constructor(name, studentId) {
167 | super(name);
168 | this.studentId = studentId;
169 | }
170 | }
171 | ```
172 |
173 | ### Расскажите о методе Function.prototype.bind ()
174 |
175 | Процитируем MDN: «Метод bind() создаёт новую функцию, которая при вызове устанавливает в качестве контекста выполнения this предоставленное значение. В метод также передаётся набор аргументов, которые будут установлены перед переданными в привязанную функцию аргументами при её вызове».
176 |
177 | Полагаю, что метод `.bind()` особенно полезен для привязки значения `this` в методах классов, которые нужно передавать в другие функции. Этот приём часто используется в React-компонентах.
178 |
179 | ### Для чего обычно используются анонимные функции?
180 |
181 | Анонимные функции используются при создании IIFE — конструкций, переменные, объявленные в которых, не загрязняют глобальную область видимости.
182 |
183 | ```js
184 | (function() {
185 | // Какой-то код.
186 | })();
187 | ```
188 |
189 | Анонимные функции применяют в качестве функций обратного вызова, которые используются лишь в одном месте программы. Код будет выглядеть более самодостаточным и читабельным в том случае, если коллбэк будет объявлен прямо в том месте, где он используется. Это избавляет от необходимости просматривать код в поиске тела функции.
190 |
191 | ```js
192 | setTimeout(function() {
193 | console.log('Hello world!');
194 | }, 1000);
195 | ```
196 |
197 | Анонимные функции удобно использовать в конструкциях, характерных для функционального стиля программирования, или при работе с библиотеками вроде `Lodash` (этот вариант их использования похож на их применение в качестве коллбэков).
198 |
199 | ```js
200 | const arr = [1, 2, 3];
201 | const double = arr.map(function(el) {
202 | return el * 2;
203 | });
204 | console.log(double); // [2, 4, 6]
205 | ```
206 |
207 | ### В чём разница между методом Object.freeze () и ключевым словом const?
208 |
209 | Ключевое слово `const` и `метод Object.freeze()` — это совершенно разные вещи.
210 |
211 | Ключевое слово const применяется к привязкам (к «переменным»). Оно создаёт иммутабельную привязку, то есть — к переменной (константе), объявленной с помощью ключевого слова const, нельзя привязать что-то новое. Константе нельзя присвоить новое значение.
212 |
213 | ```js
214 | const person = {
215 | name: "Leonardo"
216 | };
217 | let animal = {
218 | species: "snake"
219 | };
220 | person = animal; // Uncaught TypeError: Assignment to constant variable.
221 | ```
222 |
223 | Метод `Object.freeze()` работает со значениями. А точнее — с объектными значениями. Он делает объект иммутабельным, что защищает от изменений значения свойств этого объекта.
224 |
225 | ```js
226 | let person = {
227 | name: "Leonardo"
228 | };
229 | Object.freeze(person);
230 | person.name = "Lima"; // Uncaught TypeError: Cannot assign to read only property 'name' of object
231 | console.log(person);
232 | ```
233 |
234 | Обратите внимание на то, что сообщение об ошибке выводится в строгом режиме. В обычном режиме операция изменения свойства "замороженного" объекта просто не срабатывает.
235 |
236 | ### Что такое «генератор»?
237 |
238 | __Генераторы__ — это функции, из которых можно «выходить», и в которые можно «входить» по мере необходимости. Их контекст (привязки переменных) сохраняется между сеансами "входа" в них. Генераторы объявляют с использованием ключевого слова `function*`. Такая функция, при её первом вызове, не выполняет код, возвращая особый объект, генератор, который позволяет управлять её выполнением. Для получения очередного значения, выдаваемого генератором, нужно вызвать его метод `next()`. Благодаря этому выполняется код функции до тех пор, пока в нём не встретится ключевое слово `yield`, возвращающее значение.
239 |
240 | Функцию-генератор можно вызывать сколько угодно раз. Каждый раз будет возвращаться новый генератор. Но каждый генератор можно обойти лишь один раз.
241 |
242 | ```js
243 | function* makeRangeIterator(start = 0, end = Infinity, step = 1) {
244 | let iterationCount = 0;
245 | for (let i = start; i < end; i += step) {
246 | iterationCount++;
247 | yield i;
248 | }
249 | return iterationCount;
250 | }
251 | ```
252 |
253 | ### Когда стоит использовать генераторы?
254 |
255 | Если в двух словах описать основные полезные возможности генераторов, то окажется, что они заключаются в следующем:
256 |
257 | - Код, в котором используется генератор, сам определяет момент получения следующего значения. Генератор отвечает только за возврат значений, управление им осуществляется извне.
258 | - Существуют асинхронные генераторы. Они позволяют работать с асинхронными потоками данных.
259 |
260 | Главное в генераторах — это то, что получить следующее значение, возвращаемое генератором, можно только тогда, когда оно нужно в коде, использующем генератор. Генераторы не возвращают всё за один раз. В некоторых ситуациях эта их особенность может оказаться весьма удобной.
261 |
262 | ### Что такое «поднятие переменных»?
263 |
264 | Сущность концепции «поднятия переменных» заключается в том, что объявления «поднимаются» в верхнюю часть текущей области видимости. В результате переменной можно воспользоваться до её объявления. Поднимаются лишь объявления переменных, но не код их инициализации. Обратите внимание на то, что поведение переменных, объявляемых с использованием ключевого слова `var`, отличается от поведения переменных и констант, объявленных с использованием `let` и `const`.
265 |
266 | ### Что выведет следующий код?
267 |
268 | ```js
269 | const output = (function(x) {
270 | delete x;
271 | return x;
272 | })(0);
273 |
274 | console.log(output);
275 | ```
276 |
277 | Этот код выведет 0. Оператор delete используется для удаления свойств объектов. А x — это не свойство объекта — это локальная переменная. Оператор `delete` не воздействует на локальные переменные.
278 |
279 | ```js
280 | const Employee = {
281 | company: 'xyz'
282 | }
283 | const emp1 = Object.create(Employee);
284 | delete emp1.company
285 | console.log(emp1.company);
286 | ```
287 |
288 | Этот код выведет `xyz`. Свойство company является не свойством объекта `emp1`, а свойством его прототипа. Оператор `delete` не удаляет свойства прототипов объектов. У объекта `emp1` нет собственного свойства `company`. Проверить это можно так:
289 |
290 | ```js
291 | console.log(emp1.hasOwnProperty('company')); // false
292 | ```
293 |
294 | Если нам всё же необходимо удалить это свойство — сделать это можно, либо напрямую обратившись к объекту `Employee (delete Employee.company)`, либо — обратившись к прототипу объекта `emp1`, воспользовавшись его свойством `__proto__ (delete emp1.__proto__.company)`.
295 |
296 |
297 | ### Что такое «временная мёртвая зона» в ES6?
298 |
299 | В ES6 выполняется подъём переменных и констант, объявленных с использованием ключевых слов let и const (выполняется и подъём сущностей, объявленных с использованием ключевых слов var, class и function). Однако в коде имеется зона, простирающаяся от входа в область видимости до объявления переменной или константы. При обращении к переменной или константе в этой зоне будет выдана ошибка. Это и есть «временная мёртвая зона» (Temporal Dead Zone, TDZ).
300 |
301 | ```js
302 | //console.log(aLet) // выбросит ReferenceError
303 |
304 | let aLet;
305 | console.log(aLet); // undefined
306 | aLet = 10;
307 | console.log(aLet); // 10
308 | ```
309 |
310 | ### Можете ли вы описать основное различие методов массивов forEach () и map ()? В каких ситуациях вы предпочли бы один из этих методов другому?
311 |
312 | Для того чтобы понять разницу между этими методами — поговорим об особенностях работы каждого из них.
313 |
314 | Вот как работает `.forEach()`:
315 |
316 | - Он перебирает элементы массива.
317 | - Он выполняет переданную ему функцию обратного вызова для каждого элемента массива.
318 | - Он ничего не возвращает.
319 |
320 | ```js
321 | const a = [1, 2, 3];
322 | const doubled = a.forEach((num, index) => {
323 | // Сделать что-то с num и/или с index.
324 | });
325 |
326 | // doubled = undefined
327 | ```
328 |
329 | Вот краткая характеристика метода `.map()`:
330 |
331 | - Он перебирает элементы массива.
332 | - Он преобразует каждый элемент исходного массива в элемент нового массива, вызывая переданную ему функцию для каждого элемента исходного массива.
333 |
334 | ```js
335 | const a = [1, 2, 3];
336 | const doubled = a.map(num => {
337 | return num * 2;
338 | });
339 |
340 | // doubled = [2, 4, 6]
341 | ```
342 |
343 | В результате оказывается, что основное различие между `.forEach()` и `.map()` заключается в том, что `.map()` возвращает новый массив. Если вам нужно получить результат преобразования элементов исходного массива, не меняя этот массив, тогда стоит выбрать `.map()`. Если же нужно просто перебрать элементы массива — тогда можно воспользоваться `.forEach()`.
344 |
345 | ### Расскажите о шаблоне проектирования «Прототип»
346 |
347 | __Прототип (Prototype)__ — это порождающий шаблон проектирования. Он используется для создания объектов. Объекты, созданные с его помощью, содержат значения, скопированные из их прототипа (из объекта-образца). Этот шаблон ещё называют шаблоном Свойства (`Properties`).
348 |
349 | Пример использования паттерна «прототип» — это инициализация неких объектов стандартными значениями, хранящимися в базе данных. Такие значения, записанные в прототип, копируются в новые объекты без обращения к базе данных.
350 |
351 | Надо отметить, что этот паттерн редко используется в классических языках. В JavaScript применяется модель прототипного наследования. Данный паттерн применяется при конструировании новых объектов и их прототипов.
352 |
353 | ### Чем отличаются друг от друга необъявленная переменная, переменная, содержащая значение null, и undefined-переменная? Как проверить переменную на предмет того, что она необъявлена, а также на null и undefined?
354 |
355 | Необъявленная переменная создаётся при назначении значения идентификатору, который не был ранее объявлен с использованием `var`, `let` или `const`. Необъявленные переменные объявляются в глобальной области видимости, за пределами текущей области видимости. В строгом режиме при попытке назначения значения необъявленной переменной будет выброшено исключение `ReferenceError`. Использовать необъявленные переменные не рекомендуется — так же, как не рекомендуется использовать глобальные переменные. Их стоит всеми силами избегать. Для того чтобы обезопасить себя от последствий использования необъявленных переменных, воспользуйтесь блоком `try/catch`.
356 |
357 | ```js
358 | function foo() {
359 | x = 1; // Выбрасывает в строгом режиме ReferenceError
360 | }
361 |
362 | foo();
363 | console.log(x); // 1
364 | ```
365 |
366 | Переменная, содержащая `undefined` — это объявленная переменная, которой не назначено некое значение. Значение `undefined` образует собственный тип данных. Если функция ничего не возвращает, и при этом результат её вызова записывается в переменную, то в эту переменную попадёт `undefined`. Для того чтобы организовать проверку на `undefined`, можно воспользоваться оператором строгого равенства (`===`) или оператором typeof, который возвратит строку `undefined`. Обратите внимание на то, что при проверке на `undefined` не следует пользоваться оператором нестрогого равенства (`==`), так как он считает равными значения `undefined` и `null`.
367 |
368 | ```js
369 | let foo;
370 | console.log(foo); // undefined
371 | console.log(foo === undefined); // true
372 | console.log(typeof foo === 'undefined'); // true
373 |
374 | console.log(foo == null); // true. Не используйте такую конструкцию для проверки на undefined!
375 |
376 | function bar() {}
377 | let baz = bar();
378 | console.log(baz); // undefined
379 | ```
380 |
381 | Переменная, содержащая значение `null`, должна быть явным образом установлена в это значение. Она символизирует отсутствие значения и отличается от undefined-переменной тем, что значение, находящееся в ней, было ей явным образом назначено. Для того чтобы проверить значение на `null`, достаточно воспользоваться оператором строгого равенства. Для проверки на `null`, как и в случае с проверкой на `undefined`, не следует пользоваться оператором нестрогого равенства, считающим равными значения `null` и undefined.
382 |
383 | ```js
384 | let foo = null;
385 | console.log(foo === null); // true
386 | console.log(typeof foo === 'object'); // true
387 |
388 | console.log(foo == undefined); // true Не используйте такую конструкцию для проверки на null!
389 | ```
390 |
391 | Я стараюсь никогда не оставлять переменные в необъявленном состоянии, или в состоянии, когда они объявлены, но им явным образом не назначено никакого значения. Если я не собираюсь записывать в переменную какое-то значение сразу после её объявления, я записываю в неё `null`. Если вы пользуетесь линтером, то он обычно сообщает о случаях использования необъявленных переменных.
392 |
393 | ### Расскажите о шаблоне проектирования «Открытый модуль»
394 |
395 | Шаблон "Открытый модуль" (Revealing Module) является разновидностью шаблона «Модуль» (Module). Цель использования этого шаблона заключается в поддержке инкапсуляции и в открытии некоторых свойств и методов, возвращённых в объектном литерале. Вот как будет выглядеть непосредственная реализация этого шаблона:
396 |
397 | ```js
398 | const Exposer = (function() {
399 | let privateVariable = 10;
400 |
401 | let privateMethod = function() {
402 | console.log('Inside a private method!');
403 | privateVariable++;
404 | }
405 |
406 | let methodToExpose = function() {
407 | console.log('This is a method I want to expose!');
408 | }
409 |
410 | let otherMethodIWantToExpose = function() {
411 | privateMethod();
412 | }
413 |
414 | return {
415 | first: methodToExpose,
416 | second: otherMethodIWantToExpose
417 | };
418 | })();
419 |
420 | Exposer.first(); // Вывод: This is a method I want to expose!
421 | Exposer.second(); // Вывод: Inside a private method!
422 | Exposer.methodToExpose; // undefined
423 | ```
424 |
425 | Очевидный недостаток этого шаблона заключается в том, что при его использовании нельзя обращаться к приватным методам.
426 |
427 | ### В чём разница между объектами Map и WeakMap?
428 |
429 | Эти объекты ведут себя по-разному в том случае, если переменная, содержащая ссылку на объект, являющийся ключом одной из пар ключ/значение, оказывается недоступной. Вот пример:
430 |
431 | ```js
432 | const map = new Map();
433 | const weakmap = new WeakMap();
434 |
435 | (function() {
436 | let a = {
437 | x: 12
438 | };
439 | let b = {
440 | y: 12
441 | };
442 |
443 | map.set(a, 1);
444 | weakmap.set(b, 2);
445 | })()
446 | ```
447 |
448 | После того, как завершается выполнение `IIFE`, у нас уже не будет доступа к объектам `a` и `b`. Поэтому сборщик мусора удаляет ключ b из weakmap и очищает память. А вот содержимое map остаётся при этом неизменным.
449 |
450 | В результате оказывается, что объекты WeakMap позволяют сборщику мусора избавляться от тех своих записей, на ключи которых нет ссылок во внешних переменных. Объекты map хранят пары ключ/значение вне зависимости от наличия или отсутствия внешних ссылок на ключи. То же самое можно сказать и о реализации структуры данных `Map` с использованием обычных массивов. В `WeakMap` используются «слабые» ссылки на ключи. Они не препятствуют работе сборщика мусора в том случае, если на объект, используемый в роли ключа, нет других ссылок.
451 |
452 | ### Как в JavaScript-функции передаются параметры: по ссылке или по значению?
453 |
454 | Параметры всегда передаются по значению, но в переменные, представляющие объекты, записаны ссылки на объекты. Поэтому, когда в функцию передают объект и меняют свойство этого объекта, это изменение сохраняется в объекте и при выходе из функции. В результате возникает ощущение того, что параметры в функции передаются по ссылке. Но если изменить значение переменной, представляющей объект, это изменение не повлияет на объекты, находящиеся за пределами функции.
455 |
456 | Вот пример:
457 |
458 | ```js
459 | function changeStuff(a, b, c)
460 | {
461 | a = a * 10;
462 | b.item = "changed";
463 | c = {item: "changed"};
464 | }
465 |
466 | var num = 10;
467 | var obj1 = {item: "unchanged"};
468 | var obj2 = {item: "unchanged"};
469 |
470 | changeStuff(num, obj1, obj2);
471 |
472 | console.log(num);
473 | console.log(obj1.item);
474 | console.log(obj2.item);
475 |
476 | // Вот что выведет этот код:
477 |
478 | 10
479 | changed
480 | unchanged
481 | ```
482 |
483 | ### Как организовать «глубокую заморозку» объекта?
484 |
485 | Для того чтобы обеспечить «глубокую заморозку» объекта с использованием Object.freeze(), нужно создать рекурсивную функцию, которая «замораживает» свойства объекта, которые также являются объектами.
486 |
487 | Вот пример обычной «заморозки» объекта:
488 |
489 | ```js
490 | let person = {
491 | name: "Leonardo",
492 | profession: {
493 | name: "developer"
494 | }
495 | };
496 | Object.freeze(person); // делает объект иммутабельным
497 | person.profession.name = "doctor";
498 | console.log(person); //вывод { name: 'Leonardo', profession: { name: 'doctor' } }
499 | ```
500 |
501 | Вот — «глубокая заморозка»:
502 |
503 | ```js
504 | function deepFreeze(object) {
505 | let propNames = Object.getOwnPropertyNames(object);
506 | for (let name of propNames) {
507 | let value = object[name];
508 | object[name] = value && typeof value === "object" ?
509 | deepFreeze(value) : value;
510 | }
511 | return Object.freeze(object);
512 | }
513 | let person = {
514 | name: "Leonardo",
515 | profession: {
516 | name: "developer"
517 | }
518 | };
519 | deepFreeze(person);
520 | person.profession.name = "doctor"; // TypeError: Cannot assign to read only property 'name' of object
521 | ```
522 |
523 | Сообщение об ошибке выводится лишь в строгом режиме. В обычном режиме значение не меняется без вывода сообщений об ошибках.
524 |
525 | ### Почему JavaScript-программисты испытывают проблемы при использовании ключевого слова this?
526 |
527 | Самое важное, что нужно понять о `this`, заключается в том, что у функций нет фиксированного значения `this`. Это значение зависит от того, как именно вызывается функция. Если мы говорим о том, что функция вызывается с некоторым конкретным значением `this`, это значит, что это значение определяется не во время объявления функции, а во время её вызова. Вот некоторые особенности `this`:
528 |
529 | - Если функция вызывается в обычном виде (то есть, с использованием конструкции вида `someFunc()`), то `this` будет ссылаться на глобальный объект (в браузере это `window`). Если код выполняется в строгом режиме, то в `this` будет записано значение `undefined`.
530 | - Если функция вызывается как метод объекта, то ключевое слово this будет представлено объектом, которому принадлежит метод.
531 | - Если функцию вызывают с использованием `call` или `apply`, this будет представлено тем, что указано в качестве первого аргумента `call` или `apply`.
532 | - Если функция вызывается в виде обработчика события, то в `this` будет целевой элемент события.
533 | - Если функцию вызывают в виде конструктора, с использованием ключевого слова new, то в `this` будет новый объект, прототип которого установлен в качестве свойства `prototype` функции-конструктора.
534 | - Если функция создана с использованием метода `bind`, то ключевое слово `this` функции будет жёстко привязано к значению, переданному bind в качестве первого аргумента. Это — единственное исключение из правила, в соответствии с которым функции не имеют жёстко заданного значения `this`. Функции, созданные с использованием `bind`, имеют иммутабельное значение `this`.
535 |
536 | ### Сравните использование конструкции async/await и генераторов для реализации одного и того же функционала
537 |
538 | - При итерировании генератора с использованием метода `.next()` каждый вызов этого метода приводит к возврату одного значения с помощью ключевого слова `yield`. При использовании конструкции async/await await-выражения выполняются последовательно.
539 | Конструкция `async/await` упрощает реализацию определённого сценария использования генераторов.
540 | - Значения, возвращаемые генератором, всегда имеют вид `{value: X, done: Boolean}`, а асинхронные функции возвращают промисы, разрешаемые со значением X, либо завершаются с ошибкой.
541 | - Асинхронную функцию можно преобразовать в генератор, использующий промисы. Ниже приведён пример такого преобразования.
542 |
543 | Вот асинхронная функция:
544 |
545 | ```js
546 | // Асинхронная функция
547 | async function init() {
548 | const res1 = await doTask1();
549 | console.log(res1);
550 |
551 | const res2 = await doTask2(res1);
552 | console.log(res2);
553 |
554 | const res3 = await doTask3(res2);
555 | console.log(res3);
556 |
557 | return res3;
558 | }
559 |
560 | init();
561 | ```
562 |
563 | Вот аналогичный генератор.
564 |
565 | ```js
566 | // Эта функция выполняет генератор
567 | function runner(genFn) {
568 | const itr = genFn();
569 |
570 | function run(arg) {
571 | let result = itr.next(arg);
572 |
573 | if (result.done) {
574 | return result.value;
575 | } else {
576 | return Promise.resolve(result.value).then(run);
577 | }
578 | }
579 |
580 | return run;
581 | }
582 |
583 | // Вызывает функцию runner с передачей ей генератора
584 | runner(function* () {
585 | const res1 = await doTask1();
586 | console.log(res1);
587 |
588 | const res2 = await doTask2(res1);
589 | console.log(res2);
590 |
591 | const res3 = await doTask3(res2);
592 | console.log(res3);
593 |
594 | return res3;
595 | });
596 | ```
597 |
598 | ### Напишите функцию сложения вида add(num1)(num2).
599 |
600 | В оригинале это задача решается таким образом:
601 |
602 | ```js
603 | const add = (a) => {
604 | let sum = a;
605 |
606 | const func = (b) => {
607 | if (b) {
608 | sum += b;
609 | return func;
610 | }
611 |
612 | return sum;
613 | };
614 |
615 | return func;
616 | };
617 |
618 | add(2)(3)(); // 5;
619 | ```
620 |
621 | Но потом вам ставят одно условие.
622 |
623 | > Убрать в конце лишние скобки
624 |
625 | ```js
626 | add(2)(3) // 5
627 | add(1)(2)(5) // 8
628 | ```
629 |
630 | Теперь задача усложнилась. А решение кроется в переопределении метода valueOf .
631 |
632 | ```js
633 | const add = (a) => {
634 | let sum = a;
635 |
636 | const func = (b) => {
637 | sum += b;
638 | return func;
639 | };
640 |
641 | func.valueOf = () => sum;
642 |
643 | return func;
644 | };
645 |
646 | console.log(add(2)(3)); // 5;
647 | ```
648 |
649 | Когда мы вызываем console.log , он ожидает увидеть String, если его там нет, то он попытается сделать из полученного значения String.
650 | В примере выше после выполнения add(2)(3) возвращается function, которую console.log будет превращать в String, в ходе этих действий будет вызван метод valueOf для преобразования function к примитиву, а так мы переопределили данный метод, то он вернёт наше значение sum вместо стандартного.
651 |
652 | ### Что выведется в консоль ? Объясните почему.
653 |
654 | ```js
655 | let a = {};
656 | let b = {key:'b'};
657 | let c = {key:'c'};
658 |
659 | a[b] = 123;
660 | a[c] = 456;
661 |
662 | console.log(a[b]);
663 | ```
664 |
665 | Что же происходит? Когда у объекта устанавливается новое свойство, то JavaScript неявно сделает `stringify` значения. В коде выше b и c являются объектами, следовательно они оба конвертируются в `"[object Object]"` (String). Так как `stringify` значения равны, то получается, что мы присваиваем новое значение одному и тому же свойству.
666 |
667 | __Ответ:__ 456
668 |
669 | Равносильно, что написать:
670 |
671 | ```js
672 | let a = {};
673 | let b = 'object';
674 | let c = 'object';
675 |
676 | a[b] = 123;
677 | a[c] = 456;
678 | ```
679 | ---
680 |
681 | ## :older_man: Послесловие
682 |
683 | Само собой, что универсального «рецепта», как успешно пройти собеседование, не существует. Кто-то не сумеет пройти собеседование просто потому, что найдется более опытный соискатель с нужным набором знаний. В некоторых случаях причиной отказа становятся личные симпатии или антипатии руководства, и с этим вы ничего не сможете поделать. Но все же, правильная подготовка к собеседованию значительно повысит ваши шансы на успех.
684 |
685 | Собеседование – это, прежде всего, личное общение с потенциальным работодателем. Ваше резюме уже изучили и заинтересовались. Скорей всего, вы также успешно прошли этап тестирования. Теперь с вами хотят встретиться и поговорить, составить о вас какое-то впечатление как о специалисте и человеке. Давайте разбираться, как подготовиться, чтобы это впечатление было как можно лучше.
686 |
687 | ### Запаситесь необходимыми знаниями
688 |
689 | Относитесь к собеседованию как к своеобразному экзамену, отлично сдать который поможет хорошая теоретическая подготовка:
690 |
691 | - **Изучите стандартный список вопросов ко всем кандидатам на трудоустройство и научитесь четко и быстро отвечать на них.**
692 |
693 | Определите свои положительные качества и конкурентные преимущества, научитесь их подчеркивать при ответах на эти вопросы. Вам нужно уметь грамотно и развернуто, но без лишних слов сообщать о том, почему ваш выбор пал на эту компанию, почему вы покидаете предыдущее место работы, чего ждете от новой работы и на какие перспективы рассчитываете в будущем.
694 |
695 | - **Проведите своеобразную «разведку», изучив направление и масштабы деятельности фирмы.**
696 |
697 | Ознакомьтесь с уже запущенными и действующими проектами. Базируясь на этой информации, продумайте, какие еще вопросы могут к вам возникнуть: приходилось ли вам работать с определенным фреймворком или библиотекой, насколько хорошо вы знаете конкретные языки программирования, занимались ли разработкой схожих продуктов.
698 |
699 | - **Найдите в интернете стандартные логические вопросы и задания, которые часто используют HR-менеджеры, и проработайте их.**
700 |
701 | Такие задачи ставят перед соискателями не всегда, но за хорошую вакансию с большим конкурсом среди претендентов придется побороться.
702 |
703 | ### Проверьте грамотность и логичность своих резюме и тестового задания
704 |
705 | Работодатель при собеседовании в первую очередь будут опираться на информацию в вашем резюме и результаты выполнения тестового задания. И обязательно проверит, соответствуют ли они реальному уровню ваших знаний и навыков.
706 |
707 | Правильно составленное резюме – залог того, что вы, как минимум, привлечете внимание и работодатель выделит вас среди других соискателей. Но не стоит включать в него ложные сведения или оставлять явные недосказанности – это может поставить вас в неловкую ситуацию во время личного собеседования.
708 |
709 | Тщательно и придирчиво перечитайте свое резюме, обратив особое внимание на перечисленные умения и выполненные проекты. Подумайте, какие вопросы по этому поводу могут возникнуть у менеджеров по персоналу или технических специалистов.
710 |
711 | ### Совершенствуйтесь
712 |
713 | Нет идеальных специалистов, в том числе и среди программистов. Технологии совершенствуются настолько быстро, что даже самый продвинутый специалист имеет пробелы в знаниях. Но нередко отсутствие определенных знаний и навыков связано с личностными факторами. Это может быть банальная лень, отсутствие стремления к развитию, нежелание выходить за рамки своих узких профессиональных задач.
714 |
715 | В результате подобного отношения к профессии вы можете не найти ответа на каверзный вопрос во время собеседования. И оправдания в духе «у меня не было нужды этим заниматься», «давно с этим не работал» вполне могут лишить вас возможности устроиться на место своей мечты. Поэтому при работе над предыдущим пунктом не тешьте себя иллюзиями, а честно определите и восполните свои пробелы в знаниях.
716 |
717 | ### Составьте речь
718 |
719 | Этот совет особенно важен для тех, кто сильно волнуется или может растеряться в стрессовой ситуации. Заблаговременно составьте и заучите свое вступительное слово. Длина речи должна быть небольшой – около минуты. При этом вы должны суметь рассказать о себе как можно больше:
720 |
721 | - 10-секундный рассказ о ваших человеческих свойствах и качествах;
722 | - 30-секундное повествование о ваших профессиональных достоинствах;
723 | - 10-секундный анонс ваших ожиданий от будущей работы;
724 | - 10-секундная демонстрация знаний о потенциальном месте работы.
725 |
726 | Все эти сведения зафиксируйте письменно, затем перемешайте так, чтобы сухая констатация фактов стала похожа на живое непосредственное общение, и озвучьте эту речь в начале собеседования. Если в эту минуту вы сможете сконцентрировать внимание на себе, то и волнение переборете, и позитивное впечатление создадите.
727 |
728 | ### Не молчите при решении задач
729 |
730 | Как известно, обычные люди мысли читать не умеют. Ваши собеседники не в состоянии догадаться, как и о чем вы думаете. Ваше молчание – знак того, что решения у вас нет.
731 |
732 | Не замыкайтесь в себе – проговаривайте все этапы размышлений над задачей. Это поможет вашему визави понять ход ваших мыслей и при необходимости направить вас в нужном направлении. Так самые простые коммуникативные навыки сослужат вам добрую службу, и вы получите желанную работу.
733 |
734 | И последний, самый важный совет: __не впадайте в отчаяние из-за возможного отказа или неудачи на собеседовании__. Всем понравиться невозможно. И даже если вам откажут, стоит принять это как ценный опыт. Он обязательно пригодится вам в дальнейшем, когда вы будете готовиться к следующему интервью.
735 |
--------------------------------------------------------------------------------
/img/ninja_inter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ninja-js/js-interview/4ee4f0c062e40c3d1555194a74e0d59bc764e0df/img/ninja_inter.png
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | const main = () => {
2 | // Hello JS Ninja!
3 | }
4 |
--------------------------------------------------------------------------------