├── 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 | --------------------------------------------------------------------------------