company_banner

23 непростых вопроса для JavaScript-собеседования

Автор оригинала: Администратор Codersera
  • Перевод
Хотите подготовиться к собеседованию по JavaScript и ищете вопросы, на которых можно попрактиковаться? Если так — считайте, что ваши поиски окончены. Автор материала, перевод которого мы сегодня публикуем, говорит, что собрал более двух десятков вопросов по JavaScript, предназначенных для тех, кто хочет превратиться из джуниора в сеньора, для тех, кто стремится успешно пройти собеседование в сфере фронтенд-разработки и получить интересное предложение от работодателя.


image

1. Объясните особенности проверки равенства значений в JavaScript


Сложность: *


В JavaScript есть два оператора для проверки равенства величин. Первый — это так называемый оператор строгого равенства. Второй — оператор нестрогого равенства, при использовании которого может производиться преобразование типов проверяемых величин.

  • Оператор строгого равенства (===) проверяет значения на равенство, не выполняя при этом преобразования типов.
  • Оператор нестрогого равенства (==) проверяет значения на равенство, выполняя их приведение к общему типу.

var a = "42";
var b = 42;

a == b;         // true
a === b;        // false

Вот некоторые правила, касающиеся использования различных операторов проверки равенства в JavaScript:

  • Если любое из сравниваемых значений может быть значением true или false — постарайтесь избегать оператора ==. Используйте оператор ===.
  • Используйте оператор === в том случае, если работаете со следующими значениями: 0, «» или [] (пустой массив).
  • Во всех остальных случаях можете безопасно использовать оператор ==. Причём, это не только безопасно, но и способствует упрощению кода и улучшению его читабельности.

Источник

2. Приведите примеры приведения к логическому типу значений, не относящихся к этому типу


Сложность: ***


Суть этого вопроса в том, чтобы выяснить, какие значения, в случае преобразования их к логическому типу, превращаются в false, а какие — в true.

Вот список значений, которые можно назвать «ложными» (falsy). Они, при преобразовании к логическому типу, превращаются в значение false:

  • «» (пустая строка).
  • 0, -0, NaN (не-число).
  • null, undefined.

«Ложным» является и логическое значение false.

Любое значение, которое не входит в этот список, при его преобразовании к логическому типу, превращается в true (такие значения называют «истинными» — truthy). Например:

  • «hello».
  • 42.
  • [ ], [ 1, «2», 3 ] (массивы).
  • { }, { a: 42 } (объекты).
  • function foo() { .. } (функции).

«Истинным» является и логическое значение true.

Источник

3. Что такое IIFE?


Сложность: ***


IIFE (Immediately Invoked Function Expression) — это немедленно вызываемое функциональное выражение. Такое выражение выполняется немедленно после создания.

(function IIFE(){
    console.log( "Hello!" );
})();
// "Hello!"

Этот паттерн часто используется для того чтобы не допустить загрязнения глобального пространства имён. Дело в том, что переменные, объявленные в IIFE (как и в любой другой обычной функции), невидимы за пределами этой функции.

Источник

4. Когда следует использовать стрелочные функции, которые появились в ES6?


Сложность: ***


Вот простые правила по использованию различных способов объявления функций, которыми я руководствуюсь, разрабатывая код для сред, поддерживающих стандарт ES6 и более новые стандарты:

  • Используйте ключевое слово function в глобальной области видимости и для свойств Object.prototype.
  • Используйте ключевое слово function для конструкторов объектов.
  • В остальных случаях используйте стрелочные функции.

Как видите, стрелочные функции рекомендуется использовать практически везде. У такого положения дел есть несколько причин:

  • Удобная работа с контекстом. Стрелочные функции используют значение this окружающего контекста, не имея собственного this. Если такие функции применяются последовательно, без использования обычных функций в сложных конструкциях, это обеспечивает безопасную работу с контекстом.
  • Компактность. Код стрелочных функций легче вводить и легче читать. Возможно, это преимущество стрелочных функций перед обычными покажется вам спорным и зависящим от точки зрения каждого конкретного разработчика.
  • Ясность кода. Если практически весь код представлен стрелочными функциями, любая обычная функция выделяется в таком коде тем, что создаёт собственный контекст. Применяя стрелочные функции, программист создаёт более понятный код, в котором легче, чем в коде без стрелочных функций, работать с this.

Источник

5. В чём разница между ES6-классами и конструкторами функций?


Сложность: ***


Сначала рассмотрим примеры.

Функция-конструктор:

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;
  }
}

Источник

6. Расскажите о методе Function.prototype.bind()


Сложность: ***


Процитируем MDN: «Метод bind() создаёт новую функцию, которая при вызове устанавливает в качестве контекста выполнения this предоставленное значение. В метод также передаётся набор аргументов, которые будут установлены перед переданными в привязанную функцию аргументами при её вызове».

Полагаю, что метод .bind() особенно полезен для привязки значения this в методах классов, которые нужно передавать в другие функции. Этот приём часто используется в React-компонентах.

Источник 

7. Для чего обычно используются анонимные функции?


Сложность: ***


Анонимные функции используются при создании 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]

Источник

8. В чём разница между методом Object.freeze() и ключевым словом const?


Сложность: ***


Ключевое слово 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);

Обратите внимание на то, что сообщение об ошибке выводится в строгом режиме. В обычном режиме операция изменения свойства «замороженного» объекта просто не срабатывает.

Источник

9. Что такое «генератор»?


Сложность: ***


Генераторы — это функции, из которых можно «выходить», и в которые можно «входить» по мере необходимости. Их контекст (привязки переменных) сохраняется между сеансами «входа» в них. Генераторы объявляют с использованием ключевого слова 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;
}

Источник

10. Когда стоит использовать генераторы?


Сложность: ***


Если в двух словах описать основные полезные возможности генераторов, то окажется, что они заключаются в следующем:

  • Код, в котором используется генератор, сам определяет момент получения следующего значения. Генератор отвечает только за возврат значений, управление им осуществляется извне.
  • Существуют асинхронные генераторы. Они позволяют работать с асинхронными потоками данных.

Главное в генераторах — это то, что получить следующее значение, возвращаемое генератором, можно только тогда, когда оно нужно в коде, использующем генератор. Генераторы не возвращают всё за один раз. В некоторых ситуациях эта их особенность может оказаться весьма удобной.

Источник

11. Что такое «поднятие переменных»?


Сложность: ****


Сущность концепции «поднятия переменных» заключается в том, что объявления «поднимаются» в верхнюю часть текущей области видимости. В результате переменной можно воспользоваться до её объявления. Поднимаются лишь объявления переменных, но не код их инициализации. Обратите внимание на то, что поведение переменных, объявляемых с использованием ключевого слова var, отличается от поведения переменных и констант, объявленных с использованием let и const.

Источник

12. Что выведет следующий код?


Сложность: ****


var output = (function(x) {
  delete x;
  return x;
})(0);

console.log(output);

Этот код выведет 0. Оператор delete используется для удаления свойств объектов. А x — это не свойство объекта — это локальная переменная. Оператор delete не воздействует на локальные переменные.

Источник

13. Что выведет следующий код?


Сложность: ****


var Employee = {
  company: 'xyz'
}
var 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).

Источник

14. Расскажите о шаблоне проектирования «Прототип»


Сложность: ****


Прототип (Prototype) — это порождающий шаблон проектирования. Он используется для создания объектов. Объекты, созданные с его помощью, содержат значения, скопированные из их прототипа (из объекта-образца). Этот шаблон ещё называют шаблоном Свойства (Properties).

Пример использования паттерна «прототип» — это инициализация неких объектов стандартными значениями, хранящимися в базе данных. Такие значения, записанные в прототип, копируются в новые объекты без обращения к базе данных.

Надо отметить, что этот паттерн редко используется в классических языках. В JavaScript применяется модель прототипного наследования. Данный паттерн применяется при конструировании новых объектов и их прототипов.

Источник

15. Что такое «временная мёртвая зона» в ES6?


Сложность: ****


В 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

В данном примере TDZ заканчивается после объявления aLet, но не после присвоения aLet значения.

Источник

16. Можете ли вы описать основное различие методов массивов 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().

Источник

17. Чем отличаются друг от друга необъявленная переменная, переменная, содержащая значение 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.

var foo;
console.log(foo); // undefined
console.log(foo === undefined); // true
console.log(typeof foo === 'undefined'); // true

console.log(foo == null); // true. Не используйте такую конструкцию для проверки на undefined!

function bar() {}
var baz = bar();
console.log(baz); // undefined

Переменная, содержащая значение null, должна быть явным образом установлена в это значение. Она символизирует отсутствие значения и отличается от undefined-переменной тем, что значение, находящееся в ней, было ей явным образом назначено. Для того чтобы проверить значение на null, достаточно воспользоваться оператором строгого равенства. Для проверки на null, как и в случае с проверкой на undefined, не следует пользоваться оператором нестрогого равенства, считающим равными значения null и undefined.

var foo = null;
console.log(foo === null); // true
console.log(typeof foo === 'object'); // true

console.log(foo == undefined); // true Не используйте такую конструкцию для проверки на null!

Я стараюсь никогда не оставлять переменные в необъявленном состоянии, или в состоянии, когда они объявлены, но им явным образом не назначено никакого значения. Если я не собираюсь записывать в переменную какое-то значение сразу после её объявления, я записываю в неё null. Если вы пользуетесь линтером, то он обычно сообщает о случаях использования необъявленных переменных.

Источник

18. Расскажите о шаблоне проектирования «Открытый модуль»


Сложность: *****


Шаблон «Открытый модуль» (Revealing Module) является разновидностью шаблона «Модуль» (Module). Цель использования этого шаблона заключается в поддержке инкапсуляции и в открытии некоторых свойств и методов, возвращённых в объектном литерале. Вот как будет выглядеть непосредственная реализация этого шаблона:

var Exposer = (function() {
  var privateVariable = 10;

  var privateMethod = function() {
    console.log('Inside a private method!');
    privateVariable++;
  }

  var methodToExpose = function() {
    console.log('This is a method I want to expose!');
  }

  var 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

Очевидный недостаток этого шаблона заключается в том, что при его использовании нельзя обращаться к приватным методам.

Источник

19. В чём разница между объектами Map и WeakMap?


Сложность: *****


Эти объекты ведут себя по-разному в том случае, если переменная, содержащая ссылку на объект, являющийся ключом одной из пар ключ/значение, оказывается недоступной. Вот пример:

var map = new Map();
var weakmap = new WeakMap();

(function() {
    var a = {
        x: 12
    };
    var b = {
        y: 12
    };

    map.set(a, 1);
    weakmap.set(b, 2);
})()

После того, как завершается выполнение IIFE, у нас уже не будет доступа к объектам a и b. Поэтому сборщик мусора удаляет ключ b из weakmap и очищает память. А вот содержимое map остаётся при этом неизменным.

В результате оказывается, что объекты WeakMap позволяют сборщику мусора избавляться от тех своих записей, на ключи которых нет ссылок во внешних переменных. Объекты map хранят пары ключ/значение вне зависимости от наличия или отсутствия внешних ссылок на ключи. То же самое можно сказать и о реализации структуры данных Map с использованием обычных массивов. В WeakMap используются «слабые» ссылки на ключи. Они не препятствуют работе сборщика мусора в том случае, если на объект, используемый в роли ключа, нет других ссылок.

Источник

20. Как в JavaScript-функции передаются параметры: по ссылке или по значению?


Сложность: *****


Параметры всегда передаются по значению, но в переменные, представляющие объекты, записаны ссылки на объекты. Поэтому, когда в функцию передают объект и меняют свойство этого объекта, это изменение сохраняется в объекте и при выходе из функции. В результате возникает ощущение того, что параметры в функции передаются по ссылке. Но если изменить значение переменной, представляющей объект, это изменение не повлияет на объекты, находящиеся за пределами функции.

Вот пример:

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

Источник

21. Как организовать «глубокую заморозку» объекта?


Сложность: *****


Для того чтобы обеспечить «глубокую заморозку» объекта с использованием 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

Сообщение об ошибке выводится лишь в строгом режиме. В обычном режиме значение не меняется без вывода сообщений об ошибках.

Источник

22. Почему JavaScript-программисты испытывают проблемы при использовании ключевого слова this?


Сложность: *****


Самое важное, что нужно понять о 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.

Источник

23. Сравните использование конструкции 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;
});

Источник

Уважаемые читатели! Какие вопросы по JavaScript встречались вам на собеседованиях?

RUVDS.com
RUVDS – хостинг VDS/VPS серверов

Похожие публикации

Комментарии 71

    +7

    Странный вопрос со странным ответом:


    В чём разница между ES6-классами и конструкторами функций?

    Что такое конструктор функции? Может функция-конструктор класса? Ладно, наверно, ошибка перевода, но ответ ещё интересней, зачем-то наследование приплели, как будто нельзя наследоваться новым синтаксисом от класса созданного по старинке или наоборот — наследоваться по старинке от класса созданного новым синтаксисом. Правильный ответ: класс — это функция-конструктор плюс прототип класса, а функция-конструктор — это только функция конструктор.

      +4

      Там есть ещё маленькая хитрость, что прототип класса нельзя переписать (им всегда будет конструктор), а прототип функции можно переопределить.


      Это легко увидеть из дескрипторов. Эти знания могут пригодиться, если вдруг понадобится когда — то определять, перед нами класс или функция. image


      Такой кейс, например, возник перед создателями React. В React нужно определить, что если перед нами функция, то вызывать её как функцию MyComponent(), а если класс то вызывать через new MyComponent(), правда в React используется другой механизм определения класса/функции — в реакте все компоненты созданные через класс наследуются от React.Component, поэтому проверка делается через instanceof прототипа


      if (MyComponent.prototype instanceof React.Component) {
        new MyComponent(); 
        ...
      } else {
        MyComponent(); 
        ... 
      }
        +1

        А почему просто и функцию не вызывать через new в реакте?

          +1
          Из-за стрелочных функций, может. Проверил, new (() => {}) кидает ошибку.
            0

            Да, это в свою очередь связано с тем, что у стрелочных функций нет собственного контекста (то есть собственного this), а есть контекст который привязан к месту объявления. Было бы немного парадоксально вызывать стрелку через new, создав тем самым ей другой контекст.

              0
              Это из-за того, что связование this в стрелочных функциях невозможно пересвязать. Поэтому делать вызов-конструктор свтрелочной функции просто нет смысла.
        +6
        Вот некоторые правила, касающиеся использования различных операторов проверки равенства в JavaScript:

        Слишком вымученно. Минимальный набор правил сравнения гораздо проще:
        1) == null и != null
        2) === всё остальное.
        Исключение из первого пункта — код, в котором за null и undefined закреплены разные семантические значения. Такой код обычно категорически не стоит писать, но иногда бывают экзотические случаи, когда он уместен.
          +5
          Почему JavaScript-программисты испытывают проблемы при использовании ключевого слова this?
          Программисты не испытывают проблем при использовании слова this. Проблемы испытывают недопрограммисты. А вообще, я бы на собесах просил написать три функции для числа фибоначчи: рекурсивно, динамическим программированием и с помощью генераторов. Если человек это все знает и умеет, с высокой вероятностью он программист. Недостаток знаний по либам и тулзам не важен. Решать гуано-кодище в уме, что выведет фигня за которую в реальности нужно рубить пальцы… Зачем такие вопросы, мне не понятно.
            0

            Рекурсивно и динамически?

              0

              Наверное имелось ввиду через цикл?

                0

                Так рекурсивный подход используется в динамическом программировании, разве нет? И если рекурсия хвостовая, то ее можно написать в виде цикла.

              +1
              Что значит динамически?
                0
                Так с чужим кодом то скорее всего и придется работать, который люди с разной степенью квалификации наговонокодили, а самих уже и след простыл. Причем в запущенных случаях разбираться в нем долго и сложно. Так что иногда бывает важно выяснить справится с этим человек или нет.
                +7
                «Когда стоит использовать генераторы?»
                В ответе какой-то слабосвязанный лепет на тему поведения генераторов, но нет самого ответа — так, собственно, когда стоит использовать генераторы?
                «Почему JavaScript-программисты испытывают проблемы при использовании ключевого слова this?»
                Сложность не ****, а *, максимум **. Это базовые вещи.
                Что касается неявного приведения, лучшая тактика — их не использовать. Тогда не придётся задаваться дурацкими вопросами.
                  +4

                  На самом деле довольно ожидаемый вопрос — так как многие не понимают зачем эти генераторы вообще нужны. На самом деле в большинстве случаев достаточно вместо генератора использовать классический кложур — он так же способен симулировать сохранение состояния в том или ином виде:


                  function* genNumbersFromOneToTen() {
                      for (let i = 1; i <= 10; i++) {
                          yield i
                      }
                  }
                  
                  // По-сути достижимо и просто через
                  
                  function genNumbersFromOneToTen() {
                      let i = 1;
                      const iterator = {
                          next: function() {
                              return {
                                  done: i > 10,
                                  value: i++
                              }
                          }
                      }
                      iterator[Symbol.iterator] = () => iterator
                      return iterator
                  }

                  Но генератор располагает более удобным для этого интерфейсом. Следующие качества, которые делают из генератора совсем не бесполезную фичу:


                  1) Возможность использовать spread (...) и for-of (по скольку итератор инстанции генератора это он же и есть).
                  2) Возможность lazy-вычислений — пожалуй это и есть самый главный козырь генераторов — вы можете засунуть туда сложный синхронный алгоритм, который можете спокойно ставить на паузу где вам угодно и "заводить" опять по мере необходимости. Более того, с помощью next() вы можете прокидывать новые данные внутрь уже работающего генератора.
                  3) Около-асинхронные трюки: по скольку генератор эффективно останавливает даже синхронный код в любой его точке исполнения — вы можете смело делать обертки-генераторы вокруг более сложных и времязатратных синхронных операций — и с помощью того же yield выходить на определенное время из работы — дабы дать остальным таскам, ожидающим в event-queue запуститься. То есть, иными словами, вы относительно безболезненно можете разбить сложную синхронную операцию на более мелкие — и эти мелкие запускать асинхронно — для того, чтобы браузер, например, не фризанул при каком-то сложном алгоритме.

                    +1

                    Вау, спасибо!

                      –1
                      Это уже гораздо лучше.
                      Но всё равно не кажется мне вполне годным ответом на обсуждаемый вопрос. Всё это достижимо без генераторов, а для освобождения от вычислений основного потока, как я понимаю, есть Worker-ы.
                      Так для какой же практической надобности действительно нужны генераторы? Где они реально применяются в практических задачах? Например, в фреймворках?
                        +1

                        Это просто более удобная запись для итератора. Синтаксический сахар. И они с воркерами не взаимозаменяемые. Асинхронщина и многопоточность — совершенно разные вещи.

                    +14
                    23 причины послать лесом собеседника. =)
                    Однако количество добавленных закладок явно говорит, что видимо помимо javascript-собеседований вероятно есть и javascript-компании. XD
                    Но на деле чем больше пишу код тем меньше вижу смысла, что либо спрашивать про тонкости языка.
                      0
                      Я добавлю только из-за толковых комментариев
                        0
                        -
                        +8
                        После объясения стрелочных функций и классов перестал дальше читать. Автор, пожалуйста не надо, остановись. Не пиши вредные советы.

                        Что бы было понятно о чем я.

                        Пункт 4

                        Используйте ключевое слово function в глобальной области видимости и для свойств Object.prototype.

                        А в модулях уже обявлять нельзя? Не надо объявлять функции в глобальной видимости от слова совсем. Для это и появились модули.

                        Используйте ключевое слово function для конструкторов объектов.

                        Что? Т.е используем ES6, но декларируем классы по старинке как в ES5? А как же «class»?

                        В остальных случаях используйте стрелочные функции.

                        Одна из причин появления стрелочных функций это потеря контекста функции. И стрелочные функции надо использовать там, где есть вероятность это получить, высока. Их надо использовать в методах класса и как callback функции.

                        Я настоятельно рекомендую почитать официальную документацию по стрелочным функциям.

                        Пунк 5

                        Если речь идёт о создании простых объектов, то конструкторы и классы, используемые для этой цели, выглядят очень похоже.

                        «class» это всего лишь синтаксический сахар. Под капотом это все тоже наследование на прототипах. Это не разные вещи — это одно и тоже.

                        Основная разница между конструкторами и классами проявляется при использовании наследования

                        Основная разница — стиль написания. Повторюсь, под капотом это одно и тоже. Рекомендую внимательно почитать документацию о классах в ES6 и как происходит наследование в JS.

                        Дальше решил не читать, что бы не расстраиваться еще больше.
                          0

                          Присоединяюсь. Почему-то многие считают что конструкции вроде этой являются гораздо более читабельными чем классические функции


                          const MyComponent: React.FC<MyComponentProps> = ({ myProp }) => {
                             ...
                          }
                          0
                          Вот список значений, которые можно назвать «ложными» (falsy). Они, при преобразовании к логическому типу, превращаются в значение false:

                          (...)

                          Любое значение, которое не входит в этот список, при его преобразовании к логическому типу, превращается в true (такие значения называют «истинными» — truthy).

                          Как всегда, все забывают про BigInt:


                          console.log(Boolean(BigInt(0))); // false

                          Вот простые правила по использованию различных способов объявления функций, которыми я руководствуюсь, разрабатывая код для сред, поддерживающих стандарт ES6 и более новые стандарты:

                          (...)

                          Используйте ключевое слово function для конструкторов объектов.

                          В ES6 уже появились классы, так что это скорее вредный совет.


                          Вот — «глубокая заморозка»:

                          Такая заморозка ломается на циклических ссылках:


                          const foo = {};
                          foo.self = foo;
                          deepFreeze(foo); // InternalError: too much recursion

                          runner(function* () {
                          const res1 = await doTask1(); // SyntaxError: await is only valid in async functions and async generators

                          Возможно, имелся в виду yield, а не await?

                            +1
                            Некоторые вопросы не совсем корректны. Например
                            > Когда следует использовать стрелочные функции, которые появились в ES6?
                            Что такое «когда»? Когда хочу, тогда и использую. Стоило поставить вопрос в чем отличие стрелочных функций от обычных.

                            > Что такое IIFE?
                            Больше 10 лет в вебе, даже и не понял что за аббревиатура такая. Лучше писать нормальное название, тогда все сразу понятно.

                            > Что выведет следующий код?
                            За такие вопросы я готов убивать. Я бы ответил «введите в консоль и посмотрите». Такие вопросы надо строить по принципу «Нам надо сделать _то-то_, вот тут правильно сделано или нет?»
                              –1
                              Когда хочу, тогда и использую.

                              Использовать "когда хочу" — это значит не понимать зачем вообще лямбды были придуманы. Отличий от обычной функции более, чем достаточно. И да — существуют вполне ясные и понятные причины почему в том или ином случае нужно использовать именно то или другое.


                              Больше 10 лет в вебе, даже и не понял что за аббревиатура такая. Лучше писать нормальное название, тогда все сразу понятно.

                              Это общепринятное сокращение, которое используется уже много-много лет. Либо вы не особо в JS, либо вы не читаете материал, третьего представить не могу.


                              За такие вопросы я готов убивать.

                              Воу-воу, попридержите коней. Вполне нормальная постановка вопроса.

                                0
                                Судя по остальным комментариям, я все же прав.

                                IIFE даже загуглил. Ну не сказать, что прям очень известное сокращение, но ладно, оставим на мою необразованность
                                  0

                                  Ну смотрите, я тогда объясню. Сематика лямбд особенная — именно поэтому использовать их абсолютно везде неправильно. Дело в том, что они были созданы именно как функции-помощники, которые не должны полностью представлять все качества функции обычной:
                                  1) Они всегда анонимные.
                                  2) У них нет своего this — и поэтому вы можете смело их использовать/вызывать внутри функций нормальных и не бояться, что контекст этой главной функции подменится контекстом лямбды.
                                  3) У них нет своего arguments — по той же причине, чтобы был прямой и простой доступ к arguments родительской "полноценной" функции.
                                  4) У них более сокращенный синтаксис.
                                  Можно показать это на примере полифила к bind:


                                  // Нам нужно сохранить ссылку на оригинальную функцию, так как если я буду юзать this позже напрямую - то это уже будет this возвращаемой функции - коллбека. 
                                  Function.prototype.bind = Function.prototype.bind || function(ctx, ...args) {
                                      const fn = this
                                      return function(...restArgs) {
                                          return fn.apply(ctx, [...args, ...restArgs])
                                      }
                                  }
                                  // Тут this берется от первой "полноценной" функций - можно не переживать, что он подменится на this лямбды.
                                  Function.prototype.bind = Function.prototype.bind || function(ctx, ...args) {
                                      return (...restArgs) => {
                                          return this.apply(ctx, [...args, ...restArgs])
                                      }
                                  }
                                    0
                                    Ну так и что из этого следует? :) А то, что вопрос надо ставить «в чем отличие стрелочных функций?», а не когда их использовать.

                                    Использование стрелочных функций, по сути, опциональное. Обязательного использования не существует, так что и вопрос не корректный. Это просто синтаксический сахар (хотя не знаю как внутри устроено, но любую лямбду можно переделать в обычную функцию).
                                      0

                                      Ну, мне кажется, вы слишком прискребаетесь к формулировке вопросов в подобном "собеседовании". Как бы, наверное, еще правильнее было бы спросить: "объясните разницу спецификаций Function Declaration, Function Expression и Arrow Function Expression", но, как мне кажется, это уже вопрос человеческого понимания и soft skills, нежели самой темы вопроса.

                                        0
                                        Возможно, но, опять же, судя по комментам, я такой дотошный не один :)
                                          0
                                          Судя по остальным комментариям, я все же прав.

                                          я такой дотошный не один

                                          Просвещайтесь: ru.wikipedia.org/wiki/Argumentum_ad_populum
                                            0
                                            Интересно видеть такую статью на ресурсе на котором толпа может написать заведомо ложную статью только потому что это толпа :)

                                            На самом деле это не совсем относится ко мне. Я не утверждаю, что прав, потому что так же думают многие. Я утверждаю что прав и, посмотрите, многие думают так же, но я не завишу от их мнения.
                                  –1
                                  И да — существуют вполне ясные и понятные причины почему в том или ином случае нужно использовать именно то или другое.

                                  А ещё существуют случаи где можно использовать и то и другое равнозначно. И выбор уже производится исходя из предпочтений команды. К примеру я использую function только тогда, когда:


                                  • мне нужно работать с this (т.е. почти никогда)
                                  • нужно написать export default ... (тупо экономия строки, вкусовщина)
                                  • нужен overload типа в TS
                                  • нужно читаемое имя которое не удаётся задать другим способом (скажем из-за какого-нибудь wrapper-а)

                                  В остальных же случаях я выбираю =>. Исходя из читаемости. С моей точки зрения, опытному разработчику, куда комфортнее когда keyword-ы языка не мешаются перед глазами. Но я отчётливо понимаю, что есть люди, которые мыслят иначе. Ибо это вкусовщина.

                                    +1

                                    Отчасти могу с вами согласиться — безусловно существует масса случаев, где можно использовать оба варианта. Но чисто из "правильности" семантики мне все же ближе использование arrow только в качестве каких-то небольших коллбеков. В остальных случаях я использую fe/fd. Причем я не слепо на них сижу, я сперва как и вы — пересел на "arrow везде, где можно", а потом, поняв их смысл и почему они были сделаны все-таки вернулся обратно.

                                      0

                                      Полагаю это сильно зависит от кода. Последние годы я пишу в 90+% случаев околофункциональный код. Для меня нет смысла в function, т.к. не существует this. Зато существует необходимость в разнообразных пайпах, комбинациях функций, разных видов функций высшего порядка и пр… Чем меньше я вижу keyword-ов и чем больше я вижу бизнес-логики тем лучше. К примеру я не использую readonly при описании типов, т.к. они по-умолчанию у меня все readonly, и это будет просто визуальным мусором. Очень не хватает поддержки |> в typescript :(

                                        0

                                        Может быть вам поможет что-то типа:


                                        function pipe() {
                                            return [...arguments].reduce((value, fn) => fn(value))
                                        }
                                        function double(value) {
                                            return value * 2
                                        }
                                        function plus10(value) {
                                            return value + 10
                                        }
                                        
                                        const value = 42
                                        pipe(42, double, plus10)
                                          0
                                          Может быть вам поможет что-то типа:

                                          Подобный механизм мы как раз используем. Не то, конечно, но сгодится. К сожалению в случае сложных generic-типов TypeScript очень часто теряет в таком вот pipe все generic-параметры, превращая всё в any. Мы даже сделали alias для React.memo специально чтобы переписать сложные React types на элементарную конструкцию, чтобы TypeScript не терял генерики.

                                            0
                                            К сожалению в случае сложных generic-типов TypeScript очень часто теряет в таком вот pipe все generic-параметры, превращая всё в any

                                            А если что-то вроде такого? По идее тут полегче будет описать корректные возвращаемые типы:


                                            Function.prototype.pipe = function (fn) {
                                              var self = this;
                                              return function () {
                                                return fn(self.apply(null, arguments));
                                              }
                                            }
                                            function dbl (x) { return x*2; }
                                            function p10 (x) { return x+10; }
                                            function hlf (x) { return x/2; }
                                            
                                            var f = dbl.pipe(p10).pipe(hlf);
                                            f(7)
                                              0

                                              Хотя это, скорее, compose получился, но идея ясна

                                                0

                                                Так не пробовал (правка прототипа, да и очень громоздко как-то). У нас пока такая реализация:


                                                /* prettier-ignore */
                                                declare function pipe<A extends unknown, B, C>
                                                  (
                                                    ab: (arg: A) => B,
                                                    bc: (b: B) => C
                                                  ): (arg: A) => C;
                                                /* prettier-ignore */
                                                declare function pipe<A extends unknown[], B, C, D>
                                                  (
                                                    ab: (...args: A) => B,
                                                    bc: (b: B) => C,
                                                    cd: (c: C) => D
                                                  ): (...args: A) => D;
                                                /* prettier-ignore */
                                                declare function pipe<A extends unknown[], B, C, D, E>
                                                  (
                                                    ab: (...args: A) => B,
                                                    bc: (b: B) => C,
                                                    cd: (c: C) => D,
                                                    de: (d: D) => E,
                                                  ): (...args: A) => E;
                                                /* prettier-ignore */
                                                declare function pipe<A extends unknown[], B, C, D, E, F>
                                                  (
                                                    ab: (...args: A) => B,
                                                    bc: (b: B) => C,
                                                    cd: (c: C) => D,
                                                    de: (d: D) => E,
                                                    ef: (e: E) => F,
                                                  ): (...args: A) => F;
                                  0
                                  Я думал, что я не люблю головоломки. Но, кажется, я неправильно понимал про себя, и на самом деле я их люблю. А уж столько головоломок, сколько естественным образом как-то образуется в JS, нарочно не придумаешь. Например, можно написать так:
                                  Y = f => (x => x(x))(x => f(y => x(x)(y)))

                                  WTF?
                                  (да, это Y-комбинатор)
                                  Поставлю пока статью в закладки, потом почитаю, может быть, внимательнее.
                                    0

                                    Причем тут Y-комбинатор?

                                      0
                                      Y = f => (x => x(x))(x => f(y => x(x)(y)))
                                      
                                      Y = f => (
                                        (x => x(x))(
                                          x => (
                                            f(y => (
                                              x(x)(y)
                                            ))
                                          )
                                        )
                                      );
                                      
                                      Y = function(f){ return (function(x){ return x(x); })(function(x){ return f(function(y){ return x(x)(y); }); }) };
                                      
                                      Y = function (f) {
                                        return (function (x) {
                                          return x(x);
                                        })(function (x) {
                                          return f(function (y) {
                                            return x(x)(y);
                                          });
                                        });
                                      };

                                      Даже не знаю что из это меньшее месиво. Мне кажется тут вопрос вовсе не в => vs function. А в разбиении кода на отдельные именованные части.

                                        0
                                        Оставлю тут эту ссылку: habr.com/ru/post/254587
                                        +7

                                        По-скольку ruvds скорее всего все равно до качества статей, то всем новичкам, читающим статью хочу сказать, что здесь есть достаточное количество вредных советов, чтобы к этой статье относиться несерьезно.

                                          +5

                                          Весьма скептично относясь к вопросам, которые указаны в статье, таки не могу удержаться от приведения моего любимого вопроса по JS на собеседованиях


                                          Что вернёт каждый из вызовов?


                                          const array = [1,2,3]
                                          const callback = x => x*2
                                          array.map(callback)
                                          array.map(callback())
                                          array.map(x => callback)
                                          array.map(x => callback())

                                          На удивление, довольно много собеседуемых мной завалились на этом вопросе, что в прочем не оказало решительного влияния на результат собеседования

                                            –2

                                            const callback уже настораживает
                                            Думаю, компилятор откажется это всё выполнять...

                                              +1

                                              А что вас смущает в const callback? Это нормальный код. Единственное, что array.map(callback()) упадёт с ошибкой что NaN это не function.

                                                0
                                                А что вас смущает в const callback?

                                                Я их боюсь просто, не очень силён. ДЖун я.
                                                Но ошибку разглядел, по наитию.

                                              0
                                              Uncaught error: interviewee has left the interview.
                                                +2

                                                Если кандидат (не на junior позицию):


                                                • или валится на таком простом вопросе
                                                • или же считает ниже своего достоинства решать такие простые задачи

                                                То это даже хорошо, что он has left the interview. Не будет тратить ни своё ни чужое время. Сразу продемонстрирует свои soft skills.

                                                  0

                                                  Признаюсь честно, мидлы тоже не очень этот вопрос берут в среднем.
                                                  Да даже опытные сеньоры не из мира фронтенда, иногда пишущие на JS, немного впадают в ступор от, казалось бы, несложного вопроса

                                              +1

                                              "функции обратного вызова" я ещё сумел перевести обратно в "callback", но на этом мой мозг сломался:


                                              Главное в генераторах — это то, что получить следующее значение, возвращаемое генератором, можно только тогда, когда оно нужно в коде, использующем генератор

                                              А когда не нужно — то попробуешь получить, но не получишь? "TypeError: You don't really need this value"

                                                0

                                                Последний пример пал жертвой копипасты. В генераторе надо все же yield использовать.

                                                  0

                                                  Особенности сравнения значений — 1 звезда из 5.
                                                  Зачем нужны анонимные функции — 3 звезды из 5.
                                                  Разница между map и forEach — 4 звезды из 5.


                                                  У вас очень сильно искажено представление того, что такое "непростые вопросы" по JS.

                                                    +1
                                                    var output = (function(x) {
                                                      delete x;
                                                      return x;
                                                    })(0);
                                                    Тут прямо очень захотелось спросить автора, понимает ли он, для чего какие скобки в IIFE?
                                                      0

                                                      А что тут не так со скобками? Скорее здесь оператор delete неправильно используется.

                                                        0
                                                        Первая скобка и парная ей в присвоении (как и в любом другом применении в выражении, хоть в коллбэке, хоть после запятой) не нужны.
                                                          0

                                                          Ну, если вы про скобки, обрамляющие Function Expression, то да, они не обязательны. Но я бы не сказал, что это уж сильно большой недочет, возможно чисто косметический. А вот с delete тут не все здорово, так как delete не удаляет переменные, а удаляет ключи в объекте.

                                                            0
                                                            Ну если в глобальном объекте есть ключ 'x', то delete свою задачу честно выполнит.
                                                      0
                                                      Любое значение, которое не входит в этот список, при его преобразовании к логическому типу, превращается в true

                                                      Простите, нет.
                                                      [0]==true //false
                                                      ['']==true //false
                                                      [null]==false //true


                                                      Но:
                                                      [null,null]==false //false
                                                      И при этом
                                                      [null,null]==true //false

                                                        0
                                                        Простите, нет.

                                                        Да. При чем тут сравнение с true/false, если речь про преобразование? if ([0]) { console.log('yoba'); } else { console.log('ne yoba'); } что выведет?

                                                          0

                                                          Вы правы, не при чём.

                                                        0
                                                        Это однозначно не сложные вопросы, максимум среднего уровня.
                                                        В чём разница между ES6-классами и конструкторами функций?

                                                        И главное как раз упущено- попробуйте унаследоваться от Array или Function с помощью конструктора, а не класса. Механика немножко там другая при инициализации цепочки прототипов.
                                                          0
                                                          Это однозначно не сложные вопросы, максимум среднего уровня.
                                                          В чём разница между ES6-классами и конструкторами функций?

                                                          И главное как раз упущено- попробуйте унаследоваться от Array или Function с помощью конструктора, а не класса. Механика немножко там другая при инициализации цепочки прототипов.
                                                            0
                                                            Статья с разбором примеров базового уровня…
                                                              0
                                                              Это гайд: «Как не нужно задавать вопросы.»
                                                              Ибо я, зная ответы, не могу определить что мне отвечать на не конкретно заданный вопрос.
                                                                0
                                                                Любое значение, которое не входит в этот список, при его преобразовании к логическому типу, превращается в true (такие значения называют «истинными» — truthy). Например:

                                                                function foo() {… } (функции).


                                                                Вообще не пишу на JS, но почему функция равно true, разве функция не возвращает результат своего выполнения, который может быть разным? Или тут идет речь об определении?
                                                                  0

                                                                  Верно, функция является объектом, а любой объект транслируется в true при переводе.


                                                                  !!function(){} === true
                                                                  0
                                                                  Отличная статья, было очень интересно скоротать вечерок за ней. Особенно про «глубокую заморозку»

                                                                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                                  Самое читаемое