image

JavaScript — это мощный язык, который является частью фундамента интернета. У этого мощного языка также есть некоторые свои особенности. Например, знаете ли вы, что значение 0 === -0 равно true, или что Number("") дает 0?

Дело в том, что иногда эти причуды могут заставить вас почесать в затылке или даже задаться вопросом, был ли Брендан Эйч под кайфом в тот день, когда он изобретал JavaScript. Что ж, дело здесь не в том, что JavaScript — плохой язык программирования или он — зло, как говорят его критики. Со всеми языками программирования связаны какие-то странности, и JavaScript не является исключением.

В этом материале мы покажем подробное объяснение некоторых важных вопросов на интервью по JavaScript. Моя цель будет состоять в том, чтобы тщательно объяснить эти вопросы, чтобы мы могли понять лежащие в их основе концепции.

1 – Более пристальный взгляд на операторы + и -


console.log(1 + '1' - 1); 

Можете ли вы угадать поведение операторов + и — в ситуациях, подобных описанной выше?

Когда JavaScript встречает 1 + '1', он обрабатывает выражение с помощью оператора +. Одним интересным его свойством является то, что он предпочитает объединение строк, когда один из операндов является строкой. В нашем случае '1' — это строка, поэтому JavaScript неявно преобразует числовое значение 1 в строку. Следовательно, 1+'1' становится '1'+'1', в результате чего получается строка '11'.

Теперь наше уравнение равно '11' - 1. Поведение оператора — совершенно противоположное. Он отдает приоритет числовому вычитанию, независимо от типов операндов. Когда операнды не относятся к типу number, JavaScript выполняет принуждение для преобразования их в числа. В этом случае '11' преобразуется в числовое значение 11, и выражение упрощается до 11 - 1.

Собирая все это воедино:

'11' - 1 = 11 - 1 = 10

2 – Дублирующие элементы массива


Рассмотрите следующий код JavaScript и попытайтесь найти какие-либо ошибки в нем:

function duplicate(array) {
for (var i = 0; i < array.length; i++) {
array.push(array[i]);
}
return array;
}

const arr = [1, 2, 3];
const newArr = duplicate(arr);
console.log(newArr);

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

Функция duplicate использует цикл для перебора каждого элемента в заданном массиве. Но внутри цикла он добавляет новый элемент в конец массива, используя метод push(). Это делает массив длиннее каждый раз, создавая проблему, при которой цикл никогда не остановится. Условие цикла (i < array.length) всегда остается верным, потому что массив продолжает увеличиваться. Это приводит к тому, что цикл продолжается вечно, в результате чего программа зависает.

Чтобы решить проблему бесконечного цикла, вы можете сохранить начальную длину массива в переменной перед входом в цикл. Затем вы можете использовать эту начальную длину в качестве ограничения для итерации цикла. Таким образом, цикл будет выполняться только для исходных элементов массива и не будет затронут ростом массива из-за добавления дубликатов.

Вот измененная версия кода:

function duplicate(array) {
var initialLength = array.length; // Store the initial length
for (var i = 0; i < initialLength; i++) {
array.push(array[i]); // Push a duplicate of each element
}
return array;
}

const arr = [1, 2, 3];
const newArr = duplicate(arr);
console.log(newArr);

На выходе будут дублированные элементы, и не будет бесконечного цикла:

[1, 2, 3, 1, 2, 3]

3 – Разница между prototype и __proto__


prototype — это атрибут, связанный с функциями конструктора в JavaScript. Функции конструктора используются для создания объектов. Когда вы определяете функцию-конструктор, вы также можете присоединить свойства и методы к ее свойству prototype. Затем они становятся доступными для всех объектов, созданных из этого конструктора. Таким образом, prototype служит общим хранилищем для методов и свойств, которые совместно используются экземплярами.

Рассмотрим следующий фрагмент кода:

// Constructor function
function Person(name) {
this.name = name;
}

// Adding a method to the prototype
Person.prototype.sayHello = function() {
console.log(`Hello, my name is ${this.name}.`);
};

// Creating instances
const person1 = new Person("Haider Wain");
const person2 = new Person("Omer Asif");

// Calling the shared method
person1.sayHello(); // Output: Hello, my name is Haider Wain.
person2.sayHello(); // Output: Hello, my name is Omer Asif.

В этом примере у нас есть функция-конструктор с именем Person. Расширяя Person.prototype с помощью метода sayHello, мы добавляем его в цепочку всех экземпляров Person. Это позволяет каждому экземпляру Person получать доступ к общему методу и использовать его. Вместо того, чтобы каждый экземпляр имел свою собственную копию метода.

С другой стороны, свойство __proto__, часто произносимое как «dunder proto», существует в каждом объекте JavaScript. В JavaScript всё, кроме примитивных типов, может рассматриваться как объект. Каждый из этих объектов имеет прототип, который служит ссылкой на другой объект. Свойство __proto__ — это просто ссылка на этот объект-прототип. Он используется в качестве резервного источника для свойств и методов, когда исходный объект ими не обладает. По умолчанию, когда вы создаете объект, его прототипу присваивается значение Object.prototype.

Когда вы пытаетесь получить доступ к свойству или методу объекта, JavaScript выполняет процесс поиска, чтобы найти его. Этот процесс включает в себя два основных этапа:

  1. Собственные свойства объекта: JavaScript сначала проверяет, обладает ли сам объект непосредственно нужным свойством или методом. Если свойство найдено внутри объекта, оно используется напрямую.
  2. Поиск по цепочке прототипов: Если свойство не найдено в самом объекте, JavaScript просматривает прототип (на который ссылается свойство __proto__) и выполняет поиск свойства там. Этот процесс продолжается рекурсивно вверх по цепочке до тех пор, пока свойство не будет найдено или пока поиск не достигнет Object.prototype.

Если свойство не найдено даже в Object.prototype, JavaScript возвращает undefined, указывая, что свойство не существует.

4 – Области применения


При написании кода на JavaScript, важно понимать концепцию области видимости. Область видимости показывает доступность переменных в различных частях вашего кода. Прежде чем перейти к примеру, если вы не знакомы с “поднятием” (hoisting) и с тем, как выполняется JavaScript-код, вы можете узнать об этом по этой ссылке. Это поможет вам более подробно понять, как работает JavaScript-код.

Давайте подробнее рассмотрим фрагмент кода:

function foo() {
console.log(a);
}

function bar() {
var a = 3;
foo();
}

var a = 5;
bar();

Код определяет 2 функции foo() и bar() и переменную a со значением 5. Все эти операции делаются в общей области. Внутри функции bar() объявляется переменная a, которой присваивается значение 3. Итак, когда вызывается функция bar(), как вы думаете, какое значение a она выведет?

Когда движок JavaScript выполняет этот код, объявляется глобальная переменная a и ей присваивается значение 5. Затем вызывается функция bar(). Внутри функции bar() объявляется локальная переменная a, которой присваивается значение 3. Эта локальная переменная a отличается от глобальной переменной a. После этого функция foo() вызывается из функции bar().

Внутри функции foo() оператор console.log(a) пытается записать значение a. Поскольку в области видимости функции foo() не определена локальная переменная a, JavaScript просматривает цепочку областей видимости, чтобы найти ближайшую переменную с именем a. Цепочка областей относится ко всем различным областям, к которым функция имеет доступ, когда она пытается найти и использовать переменные.

Теперь давайте обратимся к вопросу о том, где JavaScript будет искать переменную a. Будет ли поиск в рамках функции bar или будет охватывать глобальную область? Как оказалось, JavaScript будет выполнять поиск в глобальной области видимости, и такое поведение определяется концепцией, называемой лексической областью видимости.

Лексическая область относится к области функции или переменной на момент ее написания в коде. Когда мы определили функцию foo, ей был предоставлен доступ как к ее собственной локальной области видимости, так и к глобальной. Эта характеристика остается неизменной независимо от того, где мы вызываем функцию foo — внутри функции bar или если мы экспортируем ее в другой модуль и запускаем там. Лексическая область не определяется там, где мы вызываем функцию.

Результатом этого является то, что выходные данные всегда будут одинаковыми: значение a, найденное в глобальной области видимости, которое в данном случае равно 5.

Однако, если бы мы определили функцию foo внутри функции bar, возник бы другой сценарий:

function bar() {
var a = 3;

function foo() {
console.log(a);
}

foo();
}

var a = 5;
bar();

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

Когда этот код запускается, foo находится внутри функции bar. Такое расположение изменяет динамику масштаба. Теперь, когда foo попытается получить доступ к переменной a, он сначала выполнит поиск в своей собственной локальной области видимости. Поскольку он не находит там a, он расширит свой поиск до области действия функции bar. О чудо, там есть a со значением 3. В результате консольный оператор выведет значение 3.

5 – Принуждение объекта


Одним из интригующих аспектов, требующих изучения, является то, как JavaScript обрабатывает преобразование объектов в примитивные значения, такие как строки, числа или логические значения. Это интересный вопрос, который проверяет, знаете ли вы, как принуждение работает с объектами.

Это преобразование имеет решающее значение при работе с объектами в таких сценариях, как объединение строк или арифметические операции. Чтобы достичь этого, JavaScript полагается на два специальных метода: valueOf и toString.

Метод valueOf является фундаментальной частью механизма преобразования объектов JavaScript. Когда объект используется в контексте, требующем примитивного значения, JavaScript сначала ищет метод valueOf внутри объекта. В случаях, когда valueOf либо отсутствует, либо не возвращает соответствующее значение, JavaScript возвращается к методу toString. Он отвечает за предоставление строкового представления объекта.

Возвращаясь к нашему исходному фрагменту кода:

const obj = {
valueOf: () => 42,
toString: () => 27
};

console.log(obj + '');

Когда мы запускаем этот код, объект obj преобразуется в примитивное значение. В этом случае метод valueOf возвращает значение 42, которое затем неявно преобразуется в строку из-за объединения с пустой строкой. Следовательно, на выходе код будет равен 42.

Однако в случаях, когда метод valueOf либо отсутствует, либо не возвращает соответствующее примитивное значение, JavaScript возвращается к методу toString. Давайте изменим наш предыдущий пример:

const obj = {
toString: () => 27
};

console.log(obj + '');

Здесь мы удалили метод valueOf, оставив только метод toString, который возвращает число 27. В этом сценарии JavaScript прибегнет к методу toString для преобразования объекта.

6 – Понимание объектных ключей


При работе с объектами в JavaScript важно понимать, как обрабатываются и назначаются ключи в контексте других объектов. Рассмотрим следующий фрагмент кода и потратим некоторое время на то, чтобы угадать результат:

let a = {};
let b = { key: 'test' };
let c = { key: 'test' };

a[b] = '123';
a[c] = '456';

console.log(a);

На первый взгляд может показаться, что этот код должен создавать объект a с двумя различными парами ключей и значений. Однако результат совершенно иной из-за особенностей JavaScript.

JavaScript использует метод toString() по умолчанию для преобразования объектных ключей в строки. Но почему? В JavaScript объектные ключи всегда являются строками (или символами), или они автоматически преобразуются в строки с помощью неявного принуждения. Когда вы используете любое значение, отличное от строки (например, число, объект или символ), в качестве ключа в объекте, JavaScript внутренне преобразует это значение в его строковое представление, прежде чем использовать его в качестве ключа.

Следовательно, когда мы используем объекты b и c в качестве ключей в объекте a, оба преобразуются в одно и то же строковое представление: [object Object]. Из-за такого поведения второе присвоение a[b] = '123'; перезапишет первое присвоение a[c] = '456'; (прим. переводчика: возможно, ошибка в оригинале: «Due to this behaviour, the second assignment, a[b] = '123'; will overwrite the first assignment a[c] = '456';» Но в коде все ровно наоборот, вторая запись a[c]='456' перезаписывает a[b]='123'). Давайте разберем код шаг за шагом:

  1. let a = {};: Инициализирует пустой объект a.
  2. let b = { key: 'test' };: Создает объект b с ключом свойства, имеющим значение 'test'.
  3. let c = { key: 'test' };: Определяет другой объект c с той же структурой, что и b.
  4. a[b] = '123';: Присваивает значение '123' свойству с ключом [object Object] в объекте a.
  5. a[c] = '456';: Обновляет значение до '456' для того же свойства с ключом [object Object] в объекте a, заменяя предыдущее значение.

В обоих назначениях используется идентичная ключевая строка [object Object]. В результате второе присвоение перезаписывает значение, установленное первым присвоением.

Когда мы регистрируем объект a, мы наблюдаем следующий вывод:

{ '[object Object]': '456' }

7 – Оператор двойного равенства


console.log([] == ![]); 

Это немного сложно. Итак, как вы думаете, каков будет результат? Давайте рассмотрим шаг за шагом. Давайте сначала рассмотрим типы обоих операндов:

typeof([]) // "object"
typeof(![]) // "boolean"

[] это объект, что ясно. Поскольку все в JavaScript является объектом, включая массивы и функции. Но почему операнд ![] имеет тип boolean? Давайте попробуем разобраться в этом. Когда вы используете! при использовании примитивного значения выполняются следующие преобразования:

  1. Ложные значения: Если исходное значение является ложным значением (например, false, 0, null, undefined, NaN или пустая строка ''), применение ! преобразует его в значение true.
  2. Истинные значения: Если исходное значение является истинным значением (любое значение, которое не является ложным), применение ! преобразует его в значение false.

В нашем случае [] — это пустой массив, который является истинным значением в JavaScript. Поскольку [] является истиной, ![] становится ложью. Таким образом, наше выражение становится:

[] == ![]
[] == false

Теперь давайте продвинемся вперед и разберемся с оператором ==. Всякий раз, когда сравниваются 2 значения с использованием оператора ==, JavaScript выполняет Алгоритм Сравнения Абстрактного Равенства. Алгоритм состоит из следующих шагов:

image

Abstract Equality Comparison Algorithm

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

Для нашего случая давайте обозначим x как [], а y как ![]. Мы проверили типы x и y и обнаружили, что x является объектом, а y — boolean. Поскольку y является boolean, а x — объектом, применяется условие 7 из алгоритма сравнения абстрактного равенства:

Если Type(y) – Boolean, верни результат сравнения x == ToNumber(y).

Это означает, что если один из типов является логическим, нам нужно преобразовать его в число перед сравнением. Каково значение ToNumber(y)? Как мы видели, [] — это истинное значение, отрицание делает его false. В результате Number(false) равно 0.

[] == false
[] == Number(false)
[] == 0 

Теперь у нас есть сравнение [] == 0, и на этот раз вступает в игру условие 8:

Если Type(x) является String или Number, а Type(y) — Object, верни результат сравнения x == ToPrimitive(y).

Исходя из этого условия, если один из операндов является объектом, мы должны преобразовать его в примитивное значение. Вот тут-то и появляется алгоритм ToPrimitive. Нам нужно преобразовать x, который равен [], в примитивное значение. Массивы — это объекты в JavaScript. Как мы видели ранее, при преобразовании объектов в примитивы в игру вступают методы valueOf и toString. В этом случае valueOf возвращает сам массив, который не является допустимым примитивным значением. В результате мы переходим к toString для получения выходных данных. Применение метода toString к пустому массиву приводит к получению пустой строки, которая является допустимым примитивом:

[] == 0
[].toString() == 0
"" == 0

Преобразование пустого массива в строку дает нам пустую строку "" и теперь мы сталкиваемся со сравнением: "" == 0.

Теперь, когда один из операндов имеет тип string, а другой — тип number, выполняется условие 5:

Если Type(x) — строка, а Type(y) — число, верни результат сравнения ToNumber(x) == y.

Следовательно, нам нужно преобразовать пустую строку "" в число, которое дает нам 0.

"" == 0
ToNumber("") == 0
0 == 0

Наконец, оба операнда имеют одинаковый тип, и выполняется условие 1. Поскольку оба имеют одинаковое значение, конечный результат равен:

0 == 0 // true

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

8 – Замыкания


Это один из самых известных вопросов, задаваемых в интервью, связанных с замыканиями:

const arr = [10, 12, 15, 21];
for (var i = 0; i < arr.length; i++) {
setTimeout(function() {
console.log('Index: ' + i + ', element: ' + arr[i]);
}, 3000);
}

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

Index: 0, element: 10
Index: 1, element: 12
Index: 2, element: 15
Index: 3, element: 21

Но здесь дело обстоит иначе. Из-за концепции замыканий и того, как JavaScript обрабатывает область видимости переменной, фактический вывод будет другим. Когда обратные вызовы setTimeout выполняются после задержки в 3000 миллисекунд, все они будут ссылаться на одну и ту же переменную i, которая будет иметь конечное значение 4 после завершения цикла. В результате вывод кода будет следующим:

Index: 4, element: undefined
Index: 4, element: undefined
Index: 4, element: undefined
Index: 4, element: undefined

Такое поведение возникает из-за того, что ключевое слово var не обладает видимостью блока, а обратные вызовы setTimeout фиксируют ссылку на ту же переменную i. Когда выполняются обратные вызовы, все они видят конечное значение i, равное 4, и пытаются получить доступ к arr[4], которое undefined.

Чтобы достичь желаемого результата, вы можете использовать ключевое слово let для создания новой области видимости для каждой итерации цикла, гарантируя, что каждый обратный вызов фиксирует правильное значение i:

const arr = [10, 12, 15, 21];
for (let i = 0; i < arr.length; i++) {
setTimeout(function() {
console.log('Index: ' + i + ', element: ' + arr[i]);
}, 3000);
}

С помощью этой модификации вы получите ожидаемый результат:

Index: 0, element: 10
Index: 1, element: 12
Index: 2, element: 15
Index: 3, element: 21

Использование let создает новую привязку для i на каждой итерации, гарантируя, что каждый обратный вызов ссылается на правильное значение.

Часто разработчики знакомятся с решением, использующим ключевое слово let. Однако собеседования иногда могут пойти еще дальше и предложить вам решить проблему без использования let. В таких случаях альтернативный подход предполагает создание замыкания путем немедленного вызова функции (IIFEImmediately Invoking Function Expression) внутри цикла. Таким образом, каждый вызов функции имеет свою собственную копию i. Вот как вы можете это сделать:

const arr = [10, 12, 15, 21];
for (var i = 0; i < arr.length; i++) {
(function(index) {
setTimeout(function() {
console.log('Index: ' + index + ', element: ' + arr[index]);
}, 3000);
})(i);
}

В этом коде немедленно вызываемая функция (function(index) { ... })(i); создает новую область видимости для каждой итерации, захватывая текущее значение i и передавая его в качестве параметра index. Это гарантирует, что каждая функция обратного вызова получит свое собственное отдельное значение index, предотвращая проблему, связанную с закрытием, и предоставляя вам ожидаемый результат:

Index: 0, element: 10
Index: 1, element: 12
Index: 2, element: 15
Index: 3, element: 21

Спасибо вам за чтение. Я надеюсь, что этот пост будет полезен вам в процессе подготовки к собеседованию.



Возможно, захочется почитать и это:


Комментарии (35)


  1. Rusty_Fox
    25.10.2023 15:28
    +15

    Опять квизы под вопросы замаскировали. Ну, такое. На любителя.


  1. IvanTheCrazy
    25.10.2023 15:28
    +21

    Половина вопросов - срань бесполезная.


    1. jodaka
      25.10.2023 15:28
      +9

      Не половина. Все


  1. LabEG
    25.10.2023 15:28
    +15

    Сеньор? Серьезно? Это вопросы из учебника для джуниоров. Сеньоры в это области даже не работают.


    1. 0Bannon
      25.10.2023 15:28

      Да даже я, не js разраб, и то ответил как минимум на 6 вопросов. Элементарщина, которую надо знать любому джуну, как работает язык под капотом.


  1. Cere8ellum
    25.10.2023 15:28
    +3

    Статья конечно интересная. Но уровень Senior...? А джун тогда кто?


    1. Frevv
      25.10.2023 15:28
      -1

      Джун то же самое только без опыта. И может ещё сказать "не знаю" на некоторые вопросы. А вот кто давно работает не может сказать не знаю", вот и вся разница на собеседование, плюс он должен показать портфолио.


      1. SergTsumbal
        25.10.2023 15:28

        Без опыта это трейни, джун как раз должен знать подобные тонкости. Сеньор их просто уже не помнит


  1. Akuma
    25.10.2023 15:28

    Все кроме последнего можно выкинуть к чертям. Никакого смысла они не несут.


  1. YegorP
    25.10.2023 15:28
    +16

    Чему равно [] == ![]?

    Либо я угадаю и мои тесты пройдут; либо не угадаю и тесты обвалятся, и я тут же поправлю. Если вообще когда-нибудь напишу такое. А если напишет кто-то другой, то на ревью его заверну. Вот и всё сеньорство.


  1. meet2code
    25.10.2023 15:28
    +4

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


    1. yerzintimur
      25.10.2023 15:28
      +4

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


      1. meet2code
        25.10.2023 15:28
        +2

        В том и проблема, что хабр имеет авторитет при поиске информации.

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

        Про звоночек согласен. Вероятность сильно повышается, что технологии и процессы в команде такие же актуальные как и вопросы :)


      1. IvanTheCrazy
        25.10.2023 15:28
        +3

        Это не звоночек, это прям колокол


    1. den_rad
      25.10.2023 15:28
      +2

      Помню первое собеседование, интервьювер задавал кучу вопросов, вроде "что будет если написать fn(--i++), на что я ему ответил что если такой код напишу в проекте, мне нужно по рукам бить. Даже прошел собеседование )


      1. meet2code
        25.10.2023 15:28

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

        Просто статья в контексте собеседования, а не только про особенности работы JS. Хорошо бы какой-то пример привести из реальности в каждом пункте.

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


      1. alexerisov
        25.10.2023 15:28
        +4

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

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


  1. ImagineTables
    25.10.2023 15:28
    +4

    console.log(1 + '1' - 1);
    Можете ли вы угадать поведение операторов + и — в ситуациях, подобных описанной выше?

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

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


  1. surarus
    25.10.2023 15:28
    +3

    В отличии от джуна, сеньор на такую чепуху наверное и не ответит, но по факту сделает в миллион раз больше продуктовых задач и проектов от 0 до production.

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

    Истинный сеньор шлет всех подальше на первом примере console.log(1 + '1' - 1);


    1. Aquahawk
      25.10.2023 15:28
      +1

      вера в святые патиерны это та ещё проблема


  1. i360u
    25.10.2023 15:28
    +6

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


  1. funca
    25.10.2023 15:28
    +2

    Мне кажется это не для синьеров, а для грандов). Весь пост пронизан тем самым теплым ламповым джаваскриптом - без strict modе, let/const, arrow functions, async/await и статического код анализа. Эх, где ты мой 2003?


  1. Boilerplate
    25.10.2023 15:28
    +3

    Во втором вопросе не только вопрос туп, так еще и ответ неверный. Функция duplicate(arr) кроме того, что создаёт новый массив, еще и меняет старый... Бред какой-то, если честно


    1. aleksandy
      25.10.2023 15:28
      +1

      А где там создаётся новый массив? Метод возвращает изменённый переданный массив.


      1. Boilerplate
        25.10.2023 15:28
        +1

        Ну по ТЗ у них было создание: "В этом фрагменте кода нам требуется создать новый массив с дублированными элементами. На первым взгляд, код создает новый массив newArr путем дублирования каждого элемента из исходного массива arr.  "

        Другое дело, что они по ссылке меняли объект, в общем функция не делает то, что нужно и делает то, что не нужно. Причём как исходная, так и полученная...


  1. Pest85
    25.10.2023 15:28

    Мдя. Человек который собеседует либо пытается узнать что ты знаешь чтобы понять как это применить в проекте, либо показать как много он знает по сравнению с тобой.
    Здесь явно второй вариант. Хуже чем собесевование с таким человеком может быть только работа с ним\ней.


  1. exchange12rocks
    25.10.2023 15:28
    +1

    Настоящий сеньор глядя на такое говорит, что так просто делать не надо - код должен быть явным. А там, где по какой-то причине неявное поведение надо, должен быть понятный комментарий, описывающий что и почему.


  1. Andy_Francev
    25.10.2023 15:28
    +3

    Простите, но что за срань-то? Сейчас, в свете популярности TypeScript ответ на вопрос:

    что выведется в "1 + '1' - 1"

    будет однозначный:

    TS2362: The left-hand side of an arithmetic operation must be of type  any ,  number ,  bigint  or an enum type.


  1. souls_arch
    25.10.2023 15:28
    +2

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


  1. savostin
    25.10.2023 15:28
    +1

    1. Не использовать var

    2. Не смешивать типы

      Я синьор?


  1. pavelsc
    25.10.2023 15:28
    -1

    уже по наличию var и хренотятине в duplicate, в результате которой новый массив так и не получили, я понял, что статью выдавил из себя июнь или трейни, и дальше чисто до комментов долистал )


  1. VasiliyMakogon
    25.10.2023 15:28

    ❯ 7 – Оператор двойного равенства

    Какой же это ужас. Почему до сих пор никто не озадачился создать внятный и интуитивно понятный JS какой-нибудь "второй версии" без всей этой мозговыносящей дряни? Это же какое-то хождение по мукам: интуитивно не понятно, сложно. Ради чего всё это?

    Вот в контексте языка PHP:

    <?php
    var_dump([] == ![]); // false

    тут не нужен рассказ на лист А4, тут достаточно знания, что пустой массив, приводимый к типу bool является false.

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


  1. Leetc0deM0nkey
    25.10.2023 15:28
    +1

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


  1. fathergorry
    25.10.2023 15:28
    +2

    Спасибо, никогда не поздно напомнить, что js - это ад на земле, но альтернатив нету. Добавлю свой пример, который стоил мне двух дней дебага : оказывается в for(var i in []) i - это строка. То, что во всех других циклах - число и в этом контексте не может быть ничем иным кроме числа, внутри такого цикла даётся как строка.


  1. JordanCpp
    25.10.2023 15:28

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