Мы собрали много вопросов по JavaScript, предназначенных для тех, кто хочет превратиться из джуниора в сеньора, для тех, кто стремится успешно пройти собеседование в сфере фронтенд-разработки и получить интересное предложение от работодателя.
- Послесловие
- Какие навыки необходимо обязательно подтянуть для собеседования?
- Объясните особенности проверки равенства значений в JavaScript.
- Приведите примеры приведения к логическому типу значений, не относящихся к этому типу
- Что такое IIFE?
- Когда следует использовать стрелочные функции, которые появились в ES6?
- В чём разница между ES6-классами и конструкторами функций?
- Расскажите о методе Function.prototype.bind().
- Для чего обычно используются анонимные функции?
- В чём разница между методом Object.freeze() и ключевым словом const?
- Что такое «генератор»?
- Когда стоит использовать генераторы?
- Что такое «поднятие переме��ных»?
- Что выведет следующий код?
- Что такое всплытие событий?
- Что такое перехват событий?
- Можете ли вы объяснить каждую часть URL?
- В чем разница между npm и npx?
- Что такое WebAssembly?
- Как работает CSS Flexbox?
- Что такое AJAX и для чего он нужен?
- Что такое JAMstack и как с ним работать?
- Что такое «временная мёртвая зона» в ES6?
- Можете ли вы описать основное различие методов массивов forEach () и map ()? В каких ситуациях вы предпочли бы один из этих методов другому?
- Расскажите о шаблоне проектирования «Прототип»
- Чем отличаются друг от друга необъявленная переменная, переменная, содержащая значение null, и undefined-переменная? Как проверить переменную на предмет того, что она необъявлена, а также на null и undefined?
- Расскажите о шаблоне проектирования «Открытый модуль»
- В чём разница между объектами Map и WeakMap?
- Как в JavaScript-функции передаются параметры: по ссылке или по значению?
- Как организовать «глубокую заморозку» объекта?
- Почему JavaScript-программисты испытывают проблемы при использовании ключевого слова this?
- Сравните использование конструкции async/await и генераторов для реализации одного и того же функционала.
- Напишите функцию сложения вида add(num1)(num2).
- Что выведется в консоль ? Объясните почему.
К любому собеседованию нельзя быть готовым на 100%. Давайте рассмотрим несколько пунктов, чтоб чувствовать себя немного увереннее.
- Самое основное правило, это умение читать код. В сложных ситуациях это поможет Вам быстрее выявить проблему и устранить ее.
- Обязательно подтяните свои навыки при работе с регулярными выражениями. Регулярные выражения могут быть довольно непростыми. Тем не менее они могут сделать для вас многое. Наиболее очевидный вариант использования - поиск определенных фрагментов текста в больших кодовых базах.
- Еще одним, не менее важным пунктом является знание терминологии. Попробуйте выписать все термины, которые вы знаете и кратко рассказать о каждом.
В JavaScript есть д��а оператора для проверки равенства величин. Первый — это так называемый оператор строгого равенства. Второй — оператор нестрогого равенства, при использовании которого может производиться преобразование типов проверяемых величин.
- Оператор строгого равенства
(===)проверяет значения на равенство, не выполняя при этом преобразования типов. - Оператор нестрогого равенства
(==)проверяет значения на равенство, выполняя их приведение к общему типу.
const a = "42";
const b = 42;
a == b; // true
a === b; // falseВот некоторые правила, касающиеся использования различных операторов проверки равенства в JavaScript:
- Если любое из сравниваемых значений может быть значением
trueилиfalse— постарайтесь избегать оператора==. Используйте оператор===. - Используйте оператор
===в том случае, если работаете со следующими значениями:0,""или[](пустой массив). - Во всех остальных случаях можете безопасно использовать оператор
==. Причём, это не только безопасно, но и способствует упрощению кода и улучшению его читабельности.
Суть этого вопроса в том, чтобы выяснить, какие значения, в случае преобразования их к логическому типу, превращаются в false, а какие — в true.
Вот список значений, которые можно назвать ложными (falsy). Они, при преобразовании к логическому типу, превращаются в значение false:
""(пустая строка).0, -0, NaN(не-число).null, undefined.
Любое значение, которое не входит в этот список, при его преобразовании к логическому типу, превращается в true (такие значения называют истинными — truthy). Например:
"hello".42.[ ], [ 1, «2», 3 ](массивы).{ }, { a: 42 }(объекты).unction foo() { .. }(функции).
IIFE (Immediately Invoked Function Expression) — это немедленно вызываемое функциональное выражение. Такое выражение выполняется немедленно после создания.
(function IIFE(){
console.log("Hello, World!");
})();
// "Hello, World!"Этот паттерн часто используется для того чтобы не допустить загрязнения глобального пространства имён. Дело в том, что переменные, объявленные в IIFE (как и в любой другой обычной функции), невидимы за пределами этой функции.
Вот простые правила по использованию различных способов объявления функций, которыми я руководствуюсь, разрабатывая код для сред, поддерживающих стандарт ES6 и более новые стандарты:
- Используйте ключевое слово function в глобальной области видимости и для свойств
Object.prototype. - Используйте ключевое слово function для конструкторов объектов.
- В остальных случаях используйте стрелочные функции.
Как видите, стрелочные функции рекомендуется использовать практически везде. У такого положения дел есть несколько причин:
- Удобная работа с контекстом. Стрелочные функции используют значение
thisокружающего контекста, не имея собственногоthis. Если такие функции применяются последовательно, без использования обычных функций в сложных конструкциях, это обеспечивает безопасную работу с контекстом. - Компактность. Код стрелочных функций легче вводить и легче читать. Возможно, это преимущество стрелочных функций перед обычными покажется вам спорным и зависящим от точки зрения каждого конкретного разработчика.
- Ясность кода. Если практически весь код представлен стрелочными функциями, любая обычная функция выделяется в таком коде тем, что создаёт собственный контекст. Применяя стрелочные функции, программист создаёт более понятный код, в котором легче, чем в коде без стрелочных функций, работать с
this.
Сначала рассмотрим примеры.
Функция-конструктор:
function Person(name) {
this.name = name;
}ES6-класс:
class Person {
constructor(name) {
this.name = name;
}
}Если речь идёт о создании простых объектов, то конструкторы и классы, используемые для этой цели, выглядят очень похоже.
Основная разница между конструкторами и классами проявляется при использовании наследования. Если нам нужно создать класс Student, являющийся подклассом класса Person, и добавить к этому новому классу поле studentId, то вот как будет выглядеть код, в котором используются конструкторы, и код, в котором применяются классы.
Функция-конструктор:
function Student(name, studentId) {
// Вызываем конструктор суперкласса для инициализации полей, унаследованных от него.
Person.call(this, name);
// Инициализация собственных полей объекта.
this.studentId = studentId;
}
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;ES6-класс:
class Student extends Person {
constructor(name, studentId) {
super(name);
this.studentId = studentId;
}
}Процитируем MDN: «Метод bind() создаёт новую функцию, которая при вызове устанавливает в качестве контекста выполнения this предоставленное значение. В метод также передаётся набор аргументов, которые будут установлены перед переданными в привязанную функцию аргументами при её вызове».
Полагаю, что метод .bind() особенно полезен для привязки значения this в методах классов, которые нужно передавать в другие функции. Этот приём часто используется в React-компонентах.
Анонимные функции используются при создании IIFE — конструкций, переменные, объявленные в которых, не загрязняют глобальную область видимости.
(function() {
// Какой-то код.
})();Анонимные функции применяют в качестве функций обратного вызова, которые используются лишь в одном месте программы. Код будет выглядеть более самодостаточным и читабельным в том случае, если коллбэк будет объявлен прямо в том месте, где он используется. Это избавляет от необходимости просматривать код в поиске тела функции.
setTimeout(function() {
console.log('Hello world!');
}, 1000);Анонимные функции удобно использовать в конструкциях, характерных для функционального стиля программирования, или при работе с библиотеками вроде Lodash (этот вариант их использования похож на их применение в качестве коллбэков).
const arr = [1, 2, 3];
const double = arr.map(function(el) {
return el * 2;
});
console.log(double); // [2, 4, 6]Ключевое слово const и метод Object.freeze() — это совершенно разные вещи.
Ключевое слово const применяется к привязкам (к «переменным»). Оно создаёт иммутабельную привязку, то есть — к переменной (константе), объявленной с помощью ключевого слова const, нельзя привязать что-то новое. Константе нельзя присвоить новое значение.
const person = {
name: "Leonardo"
};
let animal = {
species: "snake"
};
person = animal; // Uncaught TypeError: Assignment to constant variable.Метод Object.freeze() работает со значениями. А точнее — с объектными значениями. Он делает объект иммутабельным, что защищает от изменений значения свойств этого объекта.
let person = {
name: "Leonardo"
};
Object.freeze(person);
person.name = "Lima"; // Uncaught TypeError: Cannot assign to read only property 'name' of object
console.log(person);Обратите внимание на то, что сообщение об ошибке выводится в строгом режиме. В обычном режиме операция изменения свойства "замороженного" объекта просто не срабатывает.
Генераторы — это функции, из которых можно «выходить», и в которые можно «входить» по мере необходимости. Их контекст (привязки переменных) сохраняется между сеансами "входа" в них. Генераторы объявляют с использованием ключевого слова function*. Такая функция, при её первом вызове, не выполняет код, возвращая особый объект, генератор, который позволяет управлять её выполнением. Для получения очередного значения, выдаваемого генератором, нужно вызвать его метод next(). Благодаря этому выполняется код функции до тех пор, пока в нём не встретится ключевое слово yield, возвращающее значение.
Функцию-генератор можно вызывать сколько угодно раз. Каждый раз будет возвращаться новый генератор. Но каждый генератор можно обойти лишь один раз.
function* makeRangeIterator(start = 0, end = Infinity, step = 1) {
let iterationCount = 0;
for (let i = start; i < end; i += step) {
iterationCount++;
yield i;
}
return iterationCount;
}Если в двух словах описать основные полезные возможности генераторов, то окажется, что они заключаются в следующем:
- Код, в котором используется генератор, сам определяет ��омент получения следующего значения. Генератор отвечает только за возврат значений, управление им осуществляется извне.
- Существуют асинхронные генераторы. Они позволяют работать с асинхронными потоками данных.
Главное в генераторах — это то, что получить следующее значение, возвращаемое генератором, можно только тогда, когда оно нужно в коде, использующем генератор. Генераторы не возвращают всё за один раз. В некоторых ситуациях эта их особенность может оказ��ться весьма удобной.
Сущность концепции «поднятия переменных» заключается в том, что объявления «поднимаются» в верхнюю часть текущей области видимости. В результате переменной можно воспользоваться до её объявления. Поднимаются лишь объявления переменных, но не код их инициализации. Обратите внимание на то, что поведение переменных, объявляемых с использованием ключевого слова var, отличается от поведения переменных и констант, объявленных с использованием let и const.
const output = (function(x) {
delete x;
return x;
})(0);
console.log(output);Этот код выведет 0. Оператор delete используется для удаления свойств объектов. А x — это не свойство объекта — это локальная переменная. Оператор delete не воздействует на локальные переменные.
const Employee = {
company: 'xyz'
}
const emp1 = Object.create(Employee);
delete emp1.company
console.log(emp1.company);Этот код выведет xyz. Свойство company является не свойством объекта emp1, а свойством его прототипа. Оператор delete не удаляет свойства прототипов объектов. У объекта emp1 нет собственного свойства company. Проверить это можно так:
console.log(emp1.hasOwnProperty('company')); // falseЕсли нам всё же необходимо удалить это свойство — сделать это можно, либо напрямую обратившись к объекту Employee (delete Employee.company), либо — обратившись к прототипу объекта emp1, воспользовавшись его свойством __proto__ (delete emp1.__proto__.company).
В ES6 выполняется подъём переменных и констант, объявленных с использованием ключевых слов let и const (выполняется и подъём сущностей, объявленных с использованием ключевых слов var, class и function). Однако в коде имеется зона, простирающаяся от входа в область видимости до объявления переменной или константы. При обращении к переменной или константе в этой зоне будет выдана ошибка. Это и есть «временная мёртвая зона» (Temporal Dead Zone, TDZ).
//console.log(aLet) // выбросит ReferenceError
let aLet;
console.log(aLet); // undefined
aLet = 10;
console.log(aLet); // 10Можете ли вы описать основное различие методов массивов forEach () и map ()? В каких ситуациях вы предпочли бы один из этих методов другому?
Для того чтобы понять разницу между этими методами — поговорим об особенностях работы каждого из них.
Вот как работает .forEach():
- Он перебирает элементы массива.
- Он выполняет переданную ему функцию обратного вызова для каждого элемента массива.
- Он ничего не возвращает.
const a = [1, 2, 3];
const doubled = a.forEach((num, index) => {
// Сделать что-то с num и/или с index.
});
// doubled = undefinedВот краткая характеристика метода .map():
- Он перебирает элементы массива.
- Он преобразует каждый элемент исходного массива в элемент нового массива, вызывая переданную ему функцию для каждого элемента исходного массива.
const a = [1, 2, 3];
const doubled = a.map(num => {
return num * 2;
});
// doubled = [2, 4, 6]В результате оказывается, что основное различие между .forEach() и .map() заключается в том, что .map() возвращает новый массив. Если вам нужно получить результат преобразования элементов исходного массива, не меняя этот массив, тогда стоит выбрать .map(). Если же нужно просто перебрать элементы массива — тогда можно воспользоваться .forEach().
Прототип (Prototype) — это порождающий шаблон проектирования. Он используется для создания объектов. Объекты, созданные с его помощью, содержат значения, скопированные из их прототипа (из объекта-образца). Этот шаблон ещё называют шаблоном Свойства (Properties).
Пример использования паттерна «прототип» — это инициализация неких объектов стандартными значениями, хранящимися в базе данных. Такие значения, записанные в прототип, копируются в новые объекты без обращения к базе данных.
Надо отметить, что этот паттерн редко используется в классических языках. В JavaScript применяется модель прототипного наследования. Данный паттерн применяется при конструировании новых объектов и их прототипов.
Чем отличаются друг от друга необъявленная переменная, переменная, содержащая значение null, и undefined-переменная? Как проверить переменную на предмет того, что она необъявлена, а ��акже на null и undefined?
Необъявленная переменная создаётся при назначении значения идентификатору, который не был ранее объявлен с использованием var, let или const. Необъявленные переменные объявляются в глобальной области видимости, за пределами текущей области видимости. В строгом режиме при попытке назначения значения необъявленной переменной будет выброшено исключение ReferenceError. Использовать необъявленные переменные не рекомендуется — так же, как не рекомендуется использовать глобальные переменные. Их стоит всеми силами избегать. Для того чтобы обезопасить себя от последствий использования необъявленных переменных, воспользуйтесь блоком try/catch.
function foo() {
x = 1; // Выбрасывает в строгом режиме ReferenceError
}
foo();
console.log(x); // 1Переменная, содержащая undefined — это объявленная переменная, которой не назначено некое значение. Значение undefined образует собственный тип данных. Если функция ничего не возвращает, и при этом результат её вызова записывается в переменную, то в эту переменную попадёт undefined. Для того чтобы организовать проверку на undefined, можно воспользоваться оператором строгого равенства (===) или оператором typeof, который возвратит строку undefined. Обратите внимание на то, что при проверке на undefined не следует пользоваться оператором нестрогого равенства (==), так как он считает равными значения undefined и null.
let foo;
console.log(foo); // undefined
console.log(foo === undefined); // true
console.log(typeof foo === 'undefined'); // true
console.log(foo == null); // true. Не используйте такую конструкцию для проверки на undefined!
function bar() {}
let baz = bar();
console.log(baz); // undefinedПеременная, содержащая значение null, должна быть явным образом установлена в это значение. Она символизирует отсутствие значения и отличается от undefined-переменной тем, что значение, находящееся в ней, было ей явным образом назначено. Для того чтобы проверить значение на null, достаточно воспользоваться оператором строгого равенства. Для проверки на null, как и в случае с проверкой на undefined, не следует пользоваться оператором нестрогого равенства, считающим равными значения null и undefined.
let foo = null;
console.log(foo === null); // true
console.log(typeof foo === 'object'); // true
console.log(foo == undefined); // true Не используйте такую конструкцию для проверки на null!Я стараюсь никогда не оставлять переменные в необъявленном состоянии, или в состоянии, когда они объявлены, но им явным образом не назначено никакого значения. Если я не собираюсь записывать в переменную какое-то значение сразу после её объявления, я записываю в неё null. Если вы пользуетесь линтером, то он обычно сообщает о случаях использования необъявленных переменных.
Шаблон "Открытый модуль" (Revealing Module) является разновидностью шаблона «Модуль» (Module). Цель использования этого шаблона заключается в поддержке инкапсуляции и в открытии некоторых свойств и методов, возвращённых в объектном литерале. Вот как будет выглядеть непосредственная реализация этого шаблона:
const Exposer = (function() {
let privateVariable = 10;
let privateMethod = function() {
console.log('Inside a private method!');
privateVariable++;
}
let methodToExpose = function() {
console.log('This is a method I want to expose!');
}
let otherMethodIWantToExpose = function() {
privateMethod();
}
return {
first: methodToExpose,
second: otherMethodIWantToExpose
};
})();
Exposer.first(); // Вывод: This is a method I want to expose!
Exposer.second(); // Вывод: Inside a private method!
Exposer.methodToExpose; // undefinedОчевидный недостаток этого шаблона заключается в том, что при его использовании нельзя обращаться к приватным методам.
Эти объекты ведут себя по-разному в том случае, если переменная, содержащая ссылку на объект, являющийся ключом одной из пар ключ/значение, оказывается недоступной. Вот пример:
const map = new Map();
const weakmap = new WeakMap();
(function() {
let a = {
x: 12
};
let b = {
y: 12
};
map.set(a, 1);
weakmap.set(b, 2);
})()После того, как завершается выполнение IIFE, у нас уже не будет доступа к объектам a и b. Поэтому сборщик мусора удаляет ключ b из weakmap и очищает память. А вот содержимое map остаётся при этом неизменным.
В результате оказывается, что объекты WeakMap позволяют сборщику мусора избавляться от тех своих записей, на ключи которых нет ссылок во внешних переменных. Объекты map хранят пары ключ/значение вне зависимости от наличия или отсутствия внешних ссылок на ключи. То же самое можно сказать и о реализации структуры данных Map с использованием обычных массивов. В WeakMap используются «слабые» ссылки на ключи. Они не препятствуют работе сборщика мусора в том случае, если на объект, используемый в роли ключа, нет других ссылок.
Параметры всегда передаются по значению, но в переменные, представляющие объекты, записаны ссылки на объекты. Поэтому, когда в функцию передают объект и меняют свойство этого объекта, это изменение сохраняется в объекте и при выходе из функции. В результате возникает ощущение того, что параметры в функции передаются по ссылке. Но если изменить значение переменной, представляющей объект, это изменение не повлияет на объекты, находящиеся за пределами функции.
Вот пример:
function changeStuff(a, b, c)
{
a = a * 10;
b.item = "changed";
c = {item: "changed"};
}
var num = 10;
var obj1 = {item: "unchanged"};
var obj2 = {item: "unchanged"};
changeStuff(num, obj1, obj2);
console.log(num);
console.log(obj1.item);
console.log(obj2.item);
// Вот что выведет этот код:
10
changed
unchangedДля того чтобы обеспечить «глубокую заморозку» объекта с использованием Object.freeze(), нужно создать рекурсивную функцию, которая «замораживает» свойства объекта, которые также являются объектами.
Вот пример обычной «заморозки» объекта:
let person = {
name: "Leonardo",
profession: {
name: "developer"
}
};
Object.freeze(person); // делает объект иммутабельным
person.profession.name = "doctor";
console.log(person); //вывод { name: 'Leonardo', profession: { name: 'doctor' } }Вот — «глубокая заморозка»:
function deepFreeze(object) {
let propNames = Object.getOwnPropertyNames(object);
for (let name of propNames) {
let value = object[name];
object[name] = value && typeof value === "object" ?
deepFreeze(value) : value;
}
return Object.freeze(object);
}
let person = {
name: "Leonardo",
profession: {
name: "developer"
}
};
deepFreeze(person);
person.profession.name = "doctor"; // TypeError: Cannot assign to read only property 'name' of objectСообщение об ошибке выводится лишь в строгом режиме. В обычном режиме значение не меняется без вывода сообщений об ошибках.
Самое важное, что нужно понять о this, заключается в том, что у функций нет фиксированного значения this. Это значение зависит от того, как именно вызывается функция. Если мы говорим о том, что функция вызывается с некоторым конкретным значением this, это значит, что это значение определяется не во время объявления функции, а во время её вызова. Вот некоторые особенности this:
- Если функция вызывается в обычном виде (то есть, с использованием конструкции вида
someFunc()), тоthisбудет ссылаться на глобальный объект (в браузере этоwindow). Если код выполняется в строгом режиме, то вthisбудет записано значениеundefined. - Если функция вызывается как метод объекта, то ключевое слово this будет представлено объектом, которому принадлежит метод.
- Если функцию вызывают с использованием
callилиapply, this будет представлено тем, что указано в качестве первого аргументаcallилиapply. - Если функция вызывается в виде обработчика события, то в
thisбудет целевой элемент события. - Если функцию вызывают в виде конструктора, с использованием ключевого слова new, то в
thisбудет новый объект, прототип которого установлен в качестве свойстваprototypeфункции-конструктора. - Если функция создана с использованием метода
bind, то ключевое словоthisфункции будет жёстко привязано к значению, переданному bind в качестве первого аргумента. Это — единственное исключение из правила, в соответствии с которым функции не имеют жёстко заданного значенияthis. Функции, созданные с использованиемbind, имеют иммутабельное значениеthis.
Сравните использование конструкции async/await и генераторов для реализации одного и того же функционала
- При итерировании генератора с использованием метода
.next()каждый вызов этого метода приводит к возврату одного значения с помощью ключевого словаyield. При использовании конструкции async/await await-выражения выполняются последовательно. Конструкцияasync/awaitупрощает реализацию определённого сценария использования генераторов. - Значения, возвращаемые генератором, всегда имеют вид
{value: X, done: Boolean}, а асинхронные функции возвращают промисы, разрешаемые со значением X, либо завершаются с ��шибкой. - Асинхронную функцию можно преобразовать в генератор, использующий промисы. Ниже приведён пример такого преобразования.
Вот асинхронная функция:
// Асинхронная функция
async function init() {
const res1 = await doTask1();
console.log(res1);
const res2 = await doTask2(res1);
console.log(res2);
const res3 = await doTask3(res2);
console.log(res3);
return res3;
}
init();Вот аналогичный генератор.
// Эта функция выполняет генератор
function runner(genFn) {
const itr = genFn();
function run(arg) {
let result = itr.next(arg);
if (result.done) {
return result.value;
} else {
return Promise.resolve(result.value).then(run);
}
}
return run;
}
// Вызывает функцию runner с передачей ей генератора
runner(function* () {
const res1 = await doTask1();
console.log(res1);
const res2 = await doTask2(res1);
console.log(res2);
const res3 = await doTask3(res2);
console.log(res3);
return res3;
});В оригинале это задача решается таким образом:
const add = (a) => {
let sum = a;
const func = (b) => {
if (b) {
sum += b;
return func;
}
return sum;
};
return func;
};
add(2)(3)(); // 5;Но потом вам ставят одно условие.
Убрать в конце лишние скобки
add(2)(3) // 5
add(1)(2)(5) // 8Теперь задача усложнилась. А решение кроется в переопределении метода valueOf .
const add = (a) => {
let sum = a;
const func = (b) => {
sum += b;
return func;
};
func.valueOf = () => sum;
return func;
};
console.log(add(2)(3)); // 5;Когда мы вызываем console.log , он ожидает увидеть String, если его там нет, то он попытается сделать из полученного значения String. В примере выше после выполнения add(2)(3) возвращается function, которую console.log будет превращать в String, в ходе этих действий будет вызван метод valueOf для преобразования function к примитиву, а так мы переопределили данный метод, то он вернёт наше значение sum вместо стандартного.
let a = {};
let b = {key:'b'};
let c = {key:'c'};
a[b] = 123;
a[c] = 456;
console.log(a[b]);Что же происходит? Когда у объекта устанавливается новое свойство, то JavaScript неявно сделает stringify значения. В коде выше b и c являются объектами, следовательно они оба конвертируются в "[object Object]" (String). Так как stringify значения равны, то получается, что мы присваиваем новое значение одному и тому же свойству.
Ответ: 456
Равносильно, что написать:
let a = {};
let b = 'object';
let c = 'object';
a[b] = 123;
a[c] = 456;Само собой, что универсального «рецепта», как успешно пройти собеседование, не существует. Кто-то не сумеет пройти собеседование просто потому, что найдется более опытный соискатель с нужным набором знаний. В некоторых случаях причиной отказа становятся личные симпатии или антипатии руководства, и с этим вы ничего не сможете поделать. Но все же, правильная подготовка к собеседованию значительно повысит ваши шансы на успех.
Собеседование – это, прежде всего, личное общение с потенциальным работодателем. Ваше резюме уже изучили и заинтересовались. Скорей всего, вы также успешно прошли этап тестирования. Теперь с вами хотят встретиться и поговорить, составить о вас какое-то впечатление как о специалисте и человеке. Давайте разбираться, как подготовиться, чтобы это впечатление было как можно лучше.
Относитесь к собеседованию как к своеобразному экзамену, отлично сдать который поможет хорошая теоретическая подготовка:
- Изучите стандартный список вопросов ко всем кандидатам на трудоустройство и научитесь четко и быстро отвечать на них.
Определите свои положительные качества и конкурентные преимущества, научитесь их подчеркивать при ответах на эти вопросы. Вам нужно уметь грамотно и развернуто, но без лишних слов сообщать о том, почему ваш выбор пал на эту компанию, почему вы покидаете предыдущее место работы, чего ждете от новой работы и на какие перспективы рассчитываете в будущем.
- Проведите своеобразную «разведку», изучив направление и масштабы деятельности фирмы.
Ознакомьтесь с уже запущенными и действующими проектами. Базируясь на этой информации, продумайте, какие еще вопросы могут к вам возникнуть: приходилось ли вам работать с определенным фреймворком или библиотекой, насколько хорошо вы знаете конкретные языки программирования, занимались ли разработкой схожих продуктов.
- Найдите в интернете стандартные логические вопросы и задания, которые часто используют HR-менеджеры, и проработайте их.
Такие задачи ставят перед соискателями не всегда, но за хорошую вакансию с большим конкурсом среди претендентов придется побороться.
Работодатель при собеседовании в первую очередь будут опираться на информацию в вашем резюме и результаты выполнения тестового задания. И обязательно проверит, соответствуют ли они реальному уровню ваших знаний и навыков.
Правильно составленное резюме – залог того, что вы, как минимум, привлечете внимание и работодатель выделит вас среди других соискателей. Но не стоит включать в него ложные сведения или оставлять явные недосказанности – это может поставить вас в неловкую ситуацию во время личного собеседования.
Тщательно и придирчиво перечитайте свое резюме, обратив особое внимание на перечисленные умения и выполненные проекты. Подумайте, какие вопросы по этому поводу могут возникнуть у менеджеров по персоналу или технических специалистов.
Нет идеальных специалистов, в том числе и среди программистов. Технологии совершенствуются ��астолько быстро, что даже самый продвинутый специалист имеет пробелы в знаниях. Но нередко отсутствие определенных знаний и навыков связано с личностными факторами. Это может быть банальная лень, отсутствие стремления к развитию, нежелание выходить за рамки своих узких профессиональных задач.
В результате подобного отношения к профессии вы можете не найти ответа на каверзный вопрос во время собеседования. И оправдания в духе «у меня не было нужды этим заниматься», «давно с этим не работал» вполне могут лишить вас возможности устроиться на место своей мечты. Поэтому при работе над предыдущим пунктом не тешьте себя иллюзиями, а честно определите и восполните свои пробелы в знаниях.
Этот совет особенно важен для тех, кто сильно волнуется или может растеряться в стрессовой ситуации. Заблаговременно составьте и заучите свое вступительное слово. Длина речи должна быть небольшой – около минуты. При этом вы должны суметь рассказать о себе как можно больше:
- 10-секундный рассказ о ваших человеческих свойствах и качествах;
- 30-секундное повествование о ваших профессиональных достоинствах;
- 10-секундный анонс ваших ожиданий от будущей работы;
- 10-секундная демонстрация знаний о потенциальном месте работы.
Все эти сведения зафиксируйте письменно, затем перемешайте так, чтобы сухая констатация фактов стала похожа на живое непосредственное общение, и озвучьте эту речь в начале собеседования. Если в эту минуту вы сможете сконцентрировать внимание на себе, то и волнение переборете, и позитивное впечатление создадите.
Как известно, обычные люди мысли читать не умеют. Ваши собеседники не в состоянии догадаться, как и о чем вы думаете. Ваше молчание – знак того, что решения у вас нет.
Не замыкайтесь в себе – проговаривайте все этапы размышлений над задачей. Это поможет вашему визави понять ход ваших мыслей и при необходимости направить вас в нужном направлении. Так самые простые коммуникативные навыки сослужат вам добрую службу, и вы получите желанную работу.
И последний, самый важный совет: не впадайте в отчаяние из-за возможного отказа или неудачи на собеседовании. Всем понравиться невозможно. И даже если вам откажут, стоит принять это как ценный опыт. Он обязательно пригодится вам в дальнейшем, когда вы будете готовиться к следующему интервью.
