Комментарии 166
Легко запомнить, что массивы и объекты передаются по ссылке
Но неправильно. Объекты (и массивы, как их частный случай) — это ссылочный тип данных. Значением переменной является ссылка на объект. Сам объект расположен где-то в памяти, но не в переменной. В переменной всегда ссылка. Вот что надо уяснить.
А никакой передачи по ссылке в JS не существует. Совсем. Вообще.
Надо заметить что === избавляет от большинства таких проблем, но сильно усложняет код в некоторых ситуациях.
Нет, не замечал усложнения. Особенно после перехода на TS удобно. Единственный случай, где нужен ==, это сравнение с null, чтобы ещё и undefined прихватить, но все линтеры об этом кейсе знают и не ругаются.
Сейчас ещё оператор ?? подвезти для null и undefined проверок
Явное лучше неявного.
Не лучше. Никакого неявного там нету. Случай с null == undefined
прямо описан в документации и создан специально для такой проверки, потому он значительно лучше.
А ваш пример вообще можно сломать простым window.undefined = true
, так что он плох вдвойне
А ваш пример вообще можно сломать простым window.undefined = true
Уже не получится. Начиная с ES5 переназначение глобального undefined запретили.
Ну окей. Зато локально — возможно:
(function (undefined) {
var one = 1;
if (undefined == one) console.log("Fail");
})(1)
Чтобы так себе в ногу попасть, нужно специально постараться. А вот рандомный x === undefined
сломать не получится.
if (x == null)
работает всегда корректно, имеет краткую запись, идеальную семантику и прямо описано в документации. Зачем использовать сомнительные решения, если уже есть идеальное?
Смысл в единообразии. Гораздо проще запомнить "пишем везде === и получаем строгое сравнение", чем запоминать отдельное исключение.
К x == null
я отношусь примерно как к ~a.indexOf(b)
– прикольно, но в реальном коде лучше писать понятнее.
К x == null я отношусь примерно как к ~a.indexOf(b) – прикольно, но в реальном коде лучше писать понятнее.
Ну вы молодец, конечно, но это два противоположных случая. Второй — это нестандартное использование битового оператора. Первый — это прямое использование возможности, которая прямо описана в документации. Никакой нестадартщины.
Гораздо проще запомнить "пишем везде === и получаем строгое сравнение", чем запоминать отдельное исключение.
И при этом гараздо сложнее корректно сравнивать на пустой объект. На практике в итоге — сравнивают или только на нул или только на андефайнд, везде по разному, код получается плохим. И всего этого можно избежать если сказать, что на пустоту объект проверяется через == null
. Любой человек уровня джуниора и выше элементарно это запомнит
null и undefined означает отсутствие значения, а false и 0 — вполне валидные, не отсутствующие значения
null !== 0, а как иногда хочется написать как на PHP !(bool) и не париться.
Вы знаете слишком мало языков программирования.
Они нужны для расширения кругозора. Что бы не нести такую наркоманскую дичь, как Вы несёте.
Ни false, ни undefined не нужны ни в одном языке программирования вообще
Они нужны ради семантики. false, 0, null — разная семантика. null+undefined — одна семантика.
Люди стремятся к более строгой типизации, где вместо того чтобы использовать примитивный тип — ты создаешь его значение, а вы наоборот уходите в менее строгую типизацию — вместо использования более подходящего значения хотите использовать более общее.
Да, в старых языках были проблемы с этим, но однозначно не нужно эти проблемы подавать как преимущества — не зря все адекватные языки ушли от этой каши к разделению.
Я сперва было подумал, что вы про какой-нибудь Maybe говорите, что null не нужен, но даже в таком случае — или спор вообще не имеет смысла и мы никогда не проверяем объект на пустоту и всегда используем Maybe (к чему неплохо стремиться, но невозможно достичь в JS) либо в тех случаях, которые проверяем — проверяем корректно — через двойное равно.
null !== 0, а как иногда хочется написать как на PHP !(bool) и не париться.
Вот не получится у вас такое сделать для подстановки значения по-умолчанию:
function doSmth (useFlag) {
if (useFlag == null) useFlag = true;
// ...
}
Результат — doSmth();
эквивалентно doSmth(true);
. Для отрицательного значения — нужно прямо указать doSmth(false);
Такое бывает удобно, когда мы расширяем функцию дополнительным функционалом, но не хотим менять старые вызовы.
аналог goto имеется
Нет.
И он не похож на goto — значительно более ограничен и не имеет его опасностей.
Но смысла в его использовании я тоже не вижу)
Кстати говоря, в Lua есть goto, но нет его опасностей, потому что у label — блочная область видимости.
Странно, что в других языках до такого решения не догадались.
Связка break+label помогает, например, без лишних костылей завершить вложенный цикл.
Обычно в таком случае есть необходимость вынести этот цикл в отдельный метод/функцию и завершать его через ретурн. Ведь если мы используем лейбл, значит выполняем в пределах одного кода одновременно две задачи — поиск элемента и его обработка.
Вот ниже пример (он немного искуственный и со своими проблемами, потому попрошу акцентировать на то, как происходит отказ от break+label, значительно увеличивая читабельность кода)
function processCell (cell) {
var finishCell;
outer:
for (var y = 0; y < level.length; y++) {
for (var x = 0; x < level[y].length; x++) {
if (level[y][x] == 'finish') {
finishCell = new Cell(x, y);
break outer;
}
}
}
if (finishCell == null) {
throw new Error("No finish cell");
}
if (
Math.Abs(cell.x - finishCell.x) <= 2
&& Math.Abs(cell.y - finishCell.y) <= 2
) {
finishGame();
}
}
function findCell (index) {
for (var y = 0; y < level.length; y++) {
for (var x = 0; x < level[y].length; x++) {
if (level[y][x] == index) {
return new Cell(x, y);
}
}
}
return null;
}
function distance (cellA, cellB) {
return new Cell(
Math.Abs(cellA.x - cellB.x),
Math.Abs(cellA.y - cellB.y)
)
}
function areClose (cellA, cellB) {
var dist = distance(cellA, cellB);
return dist.x <= 2 && dist.y <= 2;
}
function processCell (cell) {
var finishCell = findCell('finish');
if (finishCell == null) {
throw new Error("No finish cell");
}
if (areClose(cell, finishCell)) {
finishGame();
}
}
В примере двойной цикл довольно изолированный, его удобно вынести в функцию. Может оказаться более хитрый случай, когда логика внутри циклов завязана на всякие локальные переменные и может их менять, тут уже труднее. Соглашусь, что такое бывает нечасто, но если уж встретилось, то вполне можно заюзать метку. Конечно, и в этом случае вместо метки подойдёт IIFE с return, однако не факт что это читабельнее (тут субъективно).
Простите, я никогда в своей практике не видел такого цикла, о котором вы говорите. Ну может в первые годы своей работы. Покажете пример?
goto
.Ну for тогда тоже аналог goto — его тоже не использовать?
break и без метки, в основном варианте, не так уж хорош. Если можно без него — то лучше без него.
Собственно, моя мысль шла с другой стороны: документация не предписывает использовать всё, что в ней есть.
Плохой пример, ведь break плох именно с точки зрения архитектуры. ==
не имеет такой проблемы с историей.
У == нет проблем кроме того, что когда-то кто-то прочитал, что это плохо, не понял написанного, но продолжает действовать по привычке.
О чём вы говорите? Всегда используется ===
, кроме сравнения с нул == null
, которое отдельно прописано в документации, там не будет никакого приведения типов и автор не может написать так на всякий случай, потому что это прямо прописано в стайл гайдах.
какое из двух значений может x принимать в принципе. Неопределённость кода возрастает
Никакой разницы, семантически между этими значениями нету разницы — может быть любое. А вам какая разница, какое из них?
И какая разница с точки зрения приложения? Умышленно оно пустое или неумышленно? Более того, это банально неправильные определения, ведь undefined тоже может быть умышленно установлено.
Если я вижу возможность null, это указание на то, что тут может быть объект.
Это почему вдруг? В коде такого качества там вполне может быть int или строка.
А указанием на объект должна быть типизация.
- Есть много способов указать тип в ЖС
- Нул не является одним из них
По вашей логике метод find должен возвращать null
, если массив содержит список объектов и undefined
если содержит список интов.
И толку от этой информации?
Если в вашем коде помогает ориентировать "ну если нул, то может объект" и "если андефайнд, то может не объект", то удалите такой код и наймите нормального программиста.
Нету никакой в этом возможности отличить объект от примитива. А у меня в коде прямо указаны типы.
Если в переменной null, значит, там, согласно спецификации, объект
И что? Какой объект? Как изменяет код эта информация. Видимо, у вас каша, а не код, если по тому, что где-то кто-то вравнивает с нулом вы узнаёте, что там объект.
вводит в заблуждение тех, кто идёт по его следам.
Адекватных разработчиков вводит в заблуджение какое-то до дикости абсурдное использование null для указания непонятно на что.
JSON — это не то же самое, что Javascript, там undefined вообще нет, отсюда такой эффект.
Ну вы хотя бы погуглили перед тем как снова ошибаться. JSON.stringify — часть ровно той же спецификации JavaScript, опс.
Ну вот, к примеру, где авторы специкации ДжаваСкрипт в этой самой специкации приводят int к null:
9. If Type(value) is Number
a) If value is finite then return ToString(value).
b) Else, return "null".
Видите, даже по спецификации JS может быть null там, где вы ожидаете int. Так зачем уже целую ветку стараетесь убедить меня в обратном?
Перефразирую: если вы и так из кода совершенно не знаете, что содержится в переменной и где-то проверка на "=== null" (которой может и не быть) даёт вам знание, что в ней содержится объект и вам это хоть как-то помогает — у вас ооочень плохой код.
Просто вы говорите какие-то редчайшие глупости. У меня испанский стыд из-за вас. Вы говорите, что не знаете о содержимом переменной и из случайной строчки "var m = null" вдруг внезапно узнаёте, что в ней лежит объект (при этом совершенно непонятно какой) и это вам даёт хоть какую-то полезную информацию.
Это настолько глупость, что такое просто больно читать.
Я много раз ясно изложил Вам проблему: если я вижу, что переменная сравнивается с null, то в соответствии со спецификацией заключаю, что там лежит объект. Хорошо если так. Но если вдруг там оказывается примитив, возникает вопрос, как и откуда там мог оказаться null. Может ли он вообще там быть? Должен ли я предусмотреть сам его обработку, если это значение идёт на выход из функции? Или автор написал == просто по привычке, понизив определённость кода и создав ненужные вопросы?
Переход на личности ещё менее является адекватным аргументом
Э нет, переход на личносты был бы, если бы я назвал ВАС глупцом или человеком не самой престижной привычки.
А то, что я назвал конкретную глупость глупостью не стало переходом на личности. Я вот могу сказать, что антибиотики — это лекарство против вирусов. Я сказал глупость, но это ещё не значит, что я глупый. Правда, когда мне уже 10 раз тыкнули аргументами, что я ошибаюсь, а я продолжаю танцевать по граблям — заставляет задуматься.
переменная сравнивается с null, то в соответствии со спецификацией заключаю, что там лежит объект
Может объект, а может и не объект. И вообще идея, что эта информация может быть полезна — глупа, ведь мы и так знаем, что за типы лежат в наших переменных. И использовать сравнение в качестве инструмента типизации — это смешно.
как и откуда там мог оказаться null. Может ли он вообще там быть?
Если может быть undefined — может быть и null. То есть в любой Nullable переменной.
Должен ли я предусмотреть сам его обработку, если это значение идёт на выход из функции
Оба пустых значения должны обрабатываться одинаково.
Или автор написал == просто по привычке
По какой привычке? А если автор по привычке базу удалит? Или по привычке штаны на работе снимет? Запретим штаны и базы данных?
Тут нету места заблуждениям и неоднозначностям:
x === undefined // запрещено
x === null // запрещено
typeof x === 'undefined' // запрещено
x == null // разрешено
Это единственный корректный вариант на проверку пустого значения. если написан этот кусок кода — переменная может быть пустой. в типизации это отображается как-то так:
Ещё раз — == null
простая недвухсмысленная, короткая механика, которая не имеет адекватных альтернатив.
Ваше же решение плодит одни вопросы:
- Почему автор проверяет только на null/undefined, но не на вторую часть?
- Что будет, если передадут undefined/null (иное значение)?
- Забыл ли автор проверить на второе значение или ошибочно считает, что оно никогда не может тут появиться?
- Считает ли автор, что
=== null
однозначно указывает, что в качестве альтернативы может быть только объект или автор не использует неподходящие инструменты для типизации? - (Есть ли/должна ли быть) альтернативная ветка, которая проверяет на второе значение? Чем эта ветка должна отличаться от текущей?
- Зачем автор усложняет и почему просто не воспользовался проверкой на
== null
как это доступно в документации?
И вот эти вопросы возникают каждый раз, когда я вижу === null
или === undefined
. Потому единственный способ проверить на пустоту — это однозначное == null
, которое не заставляет увеличивает сложность кода без необходимости.
Правда, когда мне уже 10 раз тыкнули аргументами, что я ошибаюсь
Я-то могу ошибаться. Но спецификация есть спецификация. Если она говорит, что null — это намеренное отсутствие объекта, что я тут могу поделать? В противном случае null вообще лишается какого-либо смысла. Это не значит, что мне это нравится. Будь моя воля, я бы и NaN выкинул, а вместо него эксепшн кидал.
ведь мы и так знаем, что за типы лежат в наших переменных
В наших — обычно знаем. А вот в чужих, из легаси-кода или сторонней библиотеки… Увы, нет. И когда читаешь такой код, == привлекает внимание. Я уже не имею никакой уверенности, что я получу на выходе.
По какой привычке?
По привычке писать == вместо ===. Право же, когда приходит юниор, гораздо проще сказать ему никогда не писать == (и бить по рукам), чем объяснить про особый случай == undefined.
как-то так
Это не Javascript!
В наших — обычно знаем. А вот в чужих, из легаси-кода или сторонней библиотеки…
Странно, что приходится вам объяснять, но… На вещи, на которые вы не можете повлиять — вы не можете повлиять, а на вещи, на которые можете повлиять — можете повлиять.
В любом случае — в чужом коде лучше увидеть однозначное "== null
", чем стрёмное "=== null
", которое вызывает одни вопросы.
Право же, когда приходит юниор, гораздо проще сказать ему никогда не писать == (и бить по рукам), чем объяснить про особый случай == undefined.
Не считайте, что у людей интеллект трёхмесячне щенки и единственный способ обучения — условные рефлексы. Моя практика показывает, что люди довольно просто понимают такие правила. Ну обычно.
Кто вам выработал этот рефлекс слепой ненависти к == null
? Тоже били по рукам?
На вещи, на которые вы не можете повлиять — вы не можете повлиять, а на вещи, на которые можете повлиять — можете повлиять.
Хорошо, давайте говорить о коде, который я оставляю тем, кто его будет поддерживать после меня. Я предпочитаю явно передать этому человеку максимум информации о возможных значениях в коде. Поэтому я не буду делать вид, что в переменной может быть null, если его там никогда не будет, чтобы только сэкономить один символ в записи.
лучше увидеть однозначное "== null"
Очевидно, что оно не однозначно, а трёхзначно: тут может быть null, тут может быть undefined и тут может быть и то и другое. Если же написано === undefined || === null, всё однозначно.
Кто вам выработал этот рефлекс слепой ненависти к == null?
Просто много имел дело с кодом сотрудника, который так писал. И был вынужден тратить время на то, чтобы искать, откуда приходят эти значения, и с какой радости они могут оказаться null-ами. Просто потому что они ими не должны были оказаться по логике приложения, и если бы оказывались, это означало бы какую-то ошибку в коде.
Очевидно, что оно не однозначно, а трёхзначно: тут может быть null, тут может быть undefined и тут может быть и то и другое. Если же написано === undefined || === null, всё однозначно.
Нет, оно однозначно: "тут может быть пустое значение, абсолютно неважно какое, ведь они равнозначны".
=== undefined || === null
сэкономить один символ в записи
Так один символ или много?
if (condition == null)
значительно читабельнее и имеет меньше места для ошибок, чем
if (typeof(condition) === 'undefined' && condition === null)
=== undefined
Не надо так делать — с такими разработчиками сразу начинаешь волноваться, что undefined где-то переопределён на false чисто потому что они так привыкли.
undefined где-то переопределён
Тот, кто это сделал — гнусный извращенец.
Мне на работе ещё в самом начале дали команду исходить из предположения, что с гнусными извращенцами мы не работаем, а потому в глобальном undefined ничего кроме undefined быть не может.
Мне на работе ещё в самом начале дали команду исходить из предположения, что с гнусными извращенцами мы не работаем
Но ведь это противоречит тому, что у вас не используют == null
из-за религиозных соображений.
Да и вообще всем страхам, что вы тут говорили
UPD: на всякий случай объясню — разделять null/undefined как разные значения и переопределять undefined — извращения приблизительно одного уровня гнусности.
По этому вопросу я с Вами согласен, за исключением особого случая, когда null — валидное значение аргумента и должно различаться со значением по умолчанию.
Мне просто интересно узнать такой пример)
На вскидку в качестве примера, могу предложить, сериализацию JSON: null — нужно сериализовать, а undefined игнорировать.
Впрочем, случаи подобной потребности за всю мою карьеру можно пересчитать по пальцам одной руки.
Ну такое поведение лучше через мета-информацию задавать.
Не согласен. Использование метаинформации — это костыль.
Если у меня развесистое дерево вложенных объектов со свойствами, то передавать метаинформацию для каждого свойства — это адский ад. Логичнее поступить так же, как сделано в JSON.stringify.
Ну ведь не случайно во всех языках используется мета-информация (аннотации, декораторі). Да, вы частично решили один вопрос — сериализовать или нет поле. И это решение, увы, не скейлбл. Даже в случае с реплейсером было бы значительно лучше и декларативнее вместо этого использовать символ:
JSON.stringify(data, (k) => {
if (k == "value") {
return JSON.Skip;
}
})
Вместо
JSON.stringify(data, (k) => {
if (k == "value") {
return undefined;
}
})
Но в то же время,
JSON.stringify({a:null, b:undefined})
b — будет проигнорировано. Наверно тоже не случайно?
Ну а в JSON.stringify([undefined])
проигнорировано не будет. Я знаю, что оно так работает. Просто я не согласен, что это хорошо.
Если считать, что это плохо — то придётся считать, что различение undefined и null было ошибкой. Хотя по сути своей, это и есть "пустое значение" с разными метаданными.
Нет, оно однозначно: «тут может быть пустое значение, абсолютно неважно какое, ведь они равнозначны».
Это неправда. Во-первых, спецификация прямо говорит обратное. Во-вторых, они по-разному ведут себя в коде.
Если взять тот же JSON:
JSON.stringify(null)
"null"
JSON.stringify(undefined)
undefined
Или вот:
((x=true)=>x)(undefined)
true
((x=true)=>x)(null)
null
Не надо так делать — с такими разработчиками сразу начинаешь волноваться, что undefined где-то переопределён на false чисто потому что они так привыкли.
А вот это как раз проверяется очень быстро, без лазанья по коду. И это очень ценная информация о разработчиках.
Это неправда. Во-первых, спецификация прямо говорит обратное.
Вы так и не объяснили, какая разница для приложения, кроме однозначной глупости для использования этого для типизации.
Если взять тот же JSON:
Это неважно — никто никогда не сериализует примитивные значения напрямую.
Или вот:
И это никак не влияет — если мы не передаём аргументы — они автоматически подставятся.
Ваши аргументы очень слабы в целом, более того, они аргументы к какому-то другому вопросу, но не к тому, зачем может понадобиться в клиентском коде проверять отдельно на разные значения и уводить в разные ветки.
Я напомню о чём мы спорим, ведь в ваших аргументах — каша.
В клиентском коде нету смысла проверять отдельно на нул и отдельно на андефайнд и пускать код по разным веткам. Конечно, крайне редко такая необходимость может появиться в какой-либо библиотеке и будет неплохо, если мы увидим, что в этом месте — исключение использованием маргинального
typeof(arg) === 'undefined'
(желательно с комментарием, почему не более естественная== null
)
Для проверки на оба пустых значения одновременно, разработчики заложили специально описанный в документации оператор и крайне глупо им не пользоваться:
arg == null
Вы так и не объяснили, какая разница для приложения
Да я же Вам только что привёл два случая, когда эти значения обрабатываются по разному.
Если Вас интересует предметная логика, то тут всё ещё проще. Если у Вас нет собаки (this.dog === null), это совсем другая ситуация, чем если неизвестно, есть ли у Вас собака (this.dog === undefined).
Глупо не пользоваться предоставляемым спецификацией языка значением, и при этом тащить в практику оператор настолько стрёмный, что его применение рекомендуется свести к двум значениям.
это совсем другая ситуация
Почему?
неизвестно, есть ли у Вас собака
Известно — её нет.
что его применение рекомендуется свести к двум значениям.
Много вещей можно использовать неправильно. Это не значит, что их при это нельзя использовать правильно.
Или вы уже отказались от ножей, которыми можно нанести вред человеку?
неизвестно, есть ли у Вас собака
Известно — её нет.
Ага!
Знаете, что сейчас произошло?
У меня был экземпляр класса TheShock, и в нём поле dog.
И вот Вы только что прописали туда null! А там был undefined.
Теперь Вам никуда не деться от понимания, что условие dog == undefined смешивало бы две качественно различные ситуации — людей без собак с людьми, которые мне ещё ничего на этот счёт не докладывали.
Много вещей можно использовать неправильно.
Не-не, Вы неправильно поняли моё замечание. Оно означало, что эта фича в языке маргинальна. Лично я считаю методологически неправильным использовать маргинальные фичи.
Теперь Вам никуда не деться от понимания, что условие dog == undefined смешивало бы две качественно различные ситуации — людей без собак с людьми, которые мне ещё ничего на этот счёт не докладывали.
Опять глупости, ну сколько можно то? undefined может означать и одно, и другое. Как и нул. А факт наличия/неизвестности о наличии нужно знать на этапе компиляции/типизации, а не через какие-то сомнительные навешивания дополнительной семантики (да хотя бы через ЖСДок или нейминг, если уж религия тайпскрипт не позволяет использовать)
Оно означало, что эта фича в языке маргинальна.
Ну вот и глупости вы считаете. Что маргинального в == null
, если оно прямо описано в документации, его использует куча профессиональных разработчиков, кроме тех, кому религия не позволяет и его поддерживают все платформы. Более того, оно имеет одни преимущества в сравнении с альтернативами и никаких недостатков.
Опять глупости, ну сколько можно то? undefined может означать и одно, и другое
Да с чего Вы взяли? Если игнорировать спецификацию, то можно и так думать. И вообще как угодно. Но спецификация явно предусматривает undefined как начальное умолчательное значение, а null как явное, намеренное указание на отсутствие объекта. Почему Вы так решительно отбрыкиваетесь от предоставляемой языком возможности представления разной семантики — загадка.
А факт наличия/неизвестности о наличии нужно знать на этапе компиляции/типизации
Щито? Динамического получения данных Ваша концепция не предусматривает? Это как вообще?
Что маргинального в == null, если оно прямо описано в документации
Я уже сказал (кажется даже дважды): применимость оператора для одного значения из мириадов возможных значений нескольких типов. Да, именно это и есть маргинальность — помнить и использовать целый оператор для одной единственной ситуации.
никаких недостатков
Не очень-то вежливо так игнорировать явно высказанные аргументы.
Почему Вы так решительно отбрыкиваетесь от предоставляемой языком возможности представления разной семантики — загадка.
да потому что:
- Она совершенна безполезна в любой практической разработке
- Она совершенно неочевидна для любого разработчика (крайне сложно догадаться — использовал ли другой разработчик эту сомнительную семантику)
- Она совершенно не соблюдается авторами библиотек
- Вы даже сами запутались в разнице между ними. Ещё немного раньше вы утверждали совершенно иное — чуть выше по ветке null для вас означало объект, а undefined — примитивное значение, а теперь вы внезапно переобулись и уже оно означает совершенно иное. Фу таким быть)
Щито? Динамического получения данных Ваша концепция не предусматривает? Это как вообще?
И что? По вашему статические языки уже с динамическими данными не работают? Я вас просвещу — работают.
помнить и использовать целый оператор для одной единственной ситуации
О ужас, это ведь так сложно запомнить? Вы вот теперь фиг забудете. Я вот сомнительную семантику, которая меняется в каждом комментарии — реально сложно запомнить.
Не очень-то вежливо так игнорировать явно высказанные аргументы.
Ну если бы они были — я бы их не игнорировал.
Разумеется, не бесполезна. Различать неизвестное значение и отсутствие объекта — логичная и естественная потребность.
«Она совершенно неочевидна для любого разработчика»
Она совершенно очевидна как минимум для двух разработчиков — для меня и для того, кто писал спецификацию Javascript. И если Вы перестанете упираться, станет столь же очевидна для Вас.
«выше по ветке null для вас означало объект, а undefined — примитивное значение, а теперь вы внезапно переобулись»
Нет. Вы просто невнимательно читаете. null означает объектное значение, а undefined типа никак не конкретизирует.
«По вашему статические языки уже с динамическими данными не работают? Я вас просвещу — работают.»
Это вот Вы к чему?
«О ужас, это ведь так сложно запомнить? Вы вот теперь фиг забудете»
Уже забыл. Потому что это не нужно. А отличать отсутствие объекта от неизвестности — да без этого вообще жить нельзя.
«Ну если бы они были — я бы их не игнорировал»
Ещё раз: нет собаки и неизвестно, есть ли собака. Нет, на этапе разработки мы об этом не знаем. На этапе разработки у нас вообще нет ни одного потенциального собаковладельца, только его абстракция.
Нет. Вы просто невнимательно читаете. null означает объектное значение, а undefined типа никак не конкретизирует.
Окей. Давайте проверим.
Итак, null одновременно означает:
- то, что тут может быть объектное значение
- намеренное указание на отсутствие значения
Как семантически указать на намеренное отсуствие примитивного значения. Сразу хорошо подумайте, ведь:
Если вы скажете undefined, то этим подтвердите, что смысла в указании намеренности/ненамеренности отсуствия значения нету, ведь в примитивных значениях вы всегда используете undefined и эта семантика не реализовывается
Если вы скажете null, то подтвердите, что сами запутались в семантиках в вашей идее о том, что null указывает на объектность/примитивность
Лично я вам рекомендую выбрать первый вариант — тогда у вас есть шанс занять слабую и неубедительную, но хоть какую-то позицию, что эта семантика нужна для объектов, но, почему-то, вдруг не нужна для примитивных значений.
Вы совершенно правы, что оба указанных Вами варианта нехороши.
Но что такое (семантически) «отсутствие примитивного значения»? Это же примитив, это не объект, это просто значение некоторого признака. Этого значения не может не быть. Может не быть самого признака. Но это не то же самое, что отсутствие объектного значения. Это отсутствие самого свойства в объекте. Эта ситуация отлична и от null и от undefined. Нет такого свойства, нет такого ключа.
Разумеется, такая семантика применима и к объектным свойствам. Например, у человека есть объектное свойство собаки, в котором может быть собака, может быть null (нет собаки), может быть undefined (неизвестно пока, есть ли собака). А у собаки такого свойства нет. Ни с каким значением.
«dog» in new Dog() === false
И свойства гражданства (примитива), например, у неё нет.
Не уверен насчет этого. Таблица равенства для ==
похожа на морской бой, очень легко промахнуться
То ли дело ===
, все интуитивно понятно:
Смутно припоминаю, что эта ветка комментов начиналась со слов "== хорош только для null" (или как-то так), так что о верхней картинке можно забыть.
Начиналась, да, но потом скатилась к вот этому комментарию, на который я и ответил.
Я говорю конкретно про эту часть спецификации:
11.9.3 The Abstract Equality Comparison Algorithm
// ...
If x is null and y is undefined, return true.
If x is undefined and y is null, return true.
Проверку на пустое значение необходимо делать через == null
. Остальные проверки, однозначно, через тройное равно
С одной стороны нормальные вопросы, адекватные. Но все равно есть ощущение что это какое-то ЕГЭ по Javascript.
Не лучше было бы вместо абстрактных вопросов задать какую-то задачку, в процессе решения которой эти моменты сами вылезут?
Люди реально не понимают зачем вообще эти все тонкости. Для многих let и var равносильны, просто var устарело.
Это нормальный ответ. Я эту «тонкость» всё еще на данный момент помню, например, но практического смысла держать её в голове не вижу, как и запоминать, почему в JS typeof(null) === 'object'. Практического полезного смысла в применении var вместо let — нет. На этом можно просто про него забыть.
У большинства кандидатов вызывал полное непонимание вопрос «со звёздочкой», почему в «константном» массиве можно изменять значения элементов а в «константной» строке нельзя изменять текст.
А вот это я бы не назвал вопросом «со звёздочкой». Возможно имеет смысл его переформулировать чуть более прямолинейно, и спрашивать, где там в JS ссылки, а где не ссылки, и в какой момент заканчивается одно и начинается другое. Это довольно важный вопрос для фронтэндера, он тесно примыкает к еще более важному вопросу — как менять ссылки там, где надо, и не менять там, где не надо.
С одной стороны рождает массу способов выстрелить в ногу себе и ни в чём неповинным коллегам, с другой позволяет писать лаконичный код.
Без примеров вторая часть звучит очень сомнительно. Я не видел «лаконичного кода», который был бы лаконичен именно потому, что там применили == против ===. Единственное исключение — это == null, но оно реально ровно одно.
Соответственно, ответ в духе «просто пользуемся === и не рассуждаем» я тоже считаю в высшей степени нормальным. Про это не надо помнить, от «креативного» применения == не по делу выигрыш на уровне погрешности, а баги бывают совершенно разрушительные.
Обычно проблемы возникают на вложенных функциях.
Вы немного лукавите — ваш пример не про замыкания и this, а про отсутствие значения this и в конечном счете вообще про strict mode. При наличии чего-нибудь в this как раз таки всё крайне банально.
Я бы не назвал этот вопрос очень уж важным. Это как раз скорее «со звёздочкой», потому что в 2020 году практически все по умолчанию работают в strict mode, и с ходу на ваш пример хочется ответить «ошибка будет выведена».
Насчёт остальных вопросов согласен с вами — это нормальные вопросы, и ожидать на них правильные ответы вообще-то абсолютно естественно.
You Don’t Know JS ©
На вопрос про типы данных в JS и их сравнение есть только один правильный ответ: B-A-AA-T-M-A-A-N!!! и всегда "===", за исключением суровых случаев когда ты точно знаешь что делаешь.
P.S.: Мне известен случай про одного программиста, который любого собеседующего с говном смешает по теории, но при этом код писать не умеет приблизительно вообще. Просто потому что заучка зазубрил.
Олимпиадные задачи на собеседовании это откровенный маразм. Ну разве что вы ищете человека писать методичку для олимпиадников.
Я считаю, что собеседование лучше бы строить из разных вопросов, а не только нюансов языка — по хорошему, собеседование должно строиться от резюме кандидата и реальных задач проекта, а не вещах, над которыми программист думает раз в полгода, когда прилетает какая-то специфическая задача.
Это были вопросы с собеседования мидла с зарплатой в районе 2к$ на руки. На них не могли ответить соискатели с годами опыта. Что же пошло не так?..
У меня такое ощущение, что неудача была уже заложена тем, что интервьюер считает «вполне рабочими» «ситуации вида 'Abc' > 'abc' или 0.1 + 0.2 == 0.3» или что === «сильно усложняет код в некоторых ситуациях». А уж позицией, что нормально гуглить такие вещи как filter, every, some, indexOf, includes, splice, Вы прямо-таки подаёте сигнал: ламеры, давайте сюда! Уж извините. Я не считаю себя супер-разработчиком, но это же всё азбучные вещи.
А уж позицией, что нормально гуглить такие вещи как filter, every, some, indexOf, includes, spliceА что плохого в гуглении, если в данный момент времени вылетело из головы название метода или подзабыл сигнатуру из-за того, что давненько не пользовался? Может это и легко помнить тем, кто пишет на одном языке и работал всего с 5-10 технологиями, но когда попишешь на 5-10 языках и на 50-100 технологиях, то держать все в голове уже не кажется естественным, ибо повидаешь тысячи методов.
Вы (автор) еще и виноваты останетесь, что оскорбляете кандидатов слишком сложными и непрактическими вопросами.
Я не так уж уверен, что автор сам понимает почему так? Не «потому, что так положено», а почему так так сделано
Не «потому, что в случае объекта константна ссылка на объект, но не поля», а именно почему так? То то полно статей где умники недоумевают почему дуракам непонятно.
tech.geekjob.ru/js-const
Умники в своем то недоумении демонстрируют свое высокомерие, ибо такое поведение константных обьектов не очень то интуитивно, но в реальной жизни программист быстро обнаруживает, что вот с константными объектами так а не иначе… Почему? Потому, что так устроено…
У Вас же вопрос «почему» задаются несчастным кандидатам
Безусловно вопрос «почему» имеет право на жизнь, но уж не на собеседовании.
Вполне возможно поинтересоваться вообще известно ли им о такой разнице.
Между прочим совершенно не факт, что в следующих стандартах такое поведение не будет изменено
Архитектурные причины реализации, как мне кажется, сейчас вообще уже не получится достоверно установить.
Так вот в том то и дело, что я не знаю! А кто знает почему сделано так а не иначе? Вот Вы знаете?
На самом деле вы не совсем правы. Исчерпывающий ответ на вопрос автора — "потому что строки — неизменяемые (immutable), а массивые — изменяемые (mutable)".
Вы же задаёте уже следующий вопрос — почему строки неизменяемые?
Ответ — скорее всего, потому что они были неизменяемые в Java и в JS сделано по аналогии.
Следующий вопрос — а почему они неизменяемые в Java?
Ответ на такой вопрос уже можете гуглить. Из тех вариантов, что я нагуглил мне больше всего нравится два ответа:
- это было сделано для того, чтобы они были потокобезопасными.
- это было сделано для того, чтобы хеш строк можно было кешировать.
— изменяемые (mutable)“
Ну знаете ли это все равно, что сказать „Это дурно потому что плохо“
На любой ответ можно взять любое слово из ответа и задать вопрос "почему" и рано или поздно дойти до областей, которые впринципе до сих пор недоступны человечеству. К примеру "а почему у частиц проявляется корпускулярно-волновой дуализм?".
Да и вообще это на вопрос 2*2? имеется очевидный ответ,
Ну, кстати, тоже нет. Оно может равняться как 4
, так и 11
. Но вы вот тоже допускаете какие-то дефолтные вещи. Это абсолютно нормально, когда ты отвечаешь прямо на вопрос в меру своего понимания дефолтности, а если человек уже хочет уточнить — пусть уточняет.
У неизменяемых строк есть и другие бонусы, например, можно пошарить строковые буферы. Мистер Алеф, помнится, писал о некоторых деталях реализации в V8, например, когда берем substr, новая строка юзает тот же буфер.
Кстати говоря:
почему в «константном» массиве можно изменять значения элементов а в «константной» строке нельзя изменять текст
Ровно потому же, почему в НЕ константном массиве можно поменять элемент, а в НЕ константной строке нельзя изменить текст, а можно только заменить всю строку целиком.
Объясните пожалуйста почему в примере
function a() {
this.counter = 1;
function b() {
this.counter = 2;
}
b();
console.log(this.counter);
}
const b = {a: a};
b.a();
выводится 1 а не 2? Почему функция b
принимает контекст window
а не контекст текущего вызова функции b.a()
?
Потому что если функция вызывается не в контексте объекта или не как функция конструктор то внутри тела этой функции this будет ссылаться на window.
"a" вызывается как метод объекта "b", у объекта "b" создаётся свойство counter потом вызывается внутренняя функция "b", которая ни к какому объекту не привязанна. Внутри функции "b" у объекта window создаётся свойство counter. Далее мы запрашиваем свойство counter которое смотрит на объект "b".
С учётом того, что JS это всё-таки язык логики интерфейсов, пытаться писать на нём сложные вычисления как минимум странно. Разумным кажется использовать язык именно по назначению, а не пытаться с маниакальным упорством побороть его кажущиеся недостатки чтобы применять в финансовых расчётах или умном доме.
Любопытное утверждение. Интересно как автор до него дошел и как в его голове это утверждение согласуется с окружающей действительностью.
и ещё выйграть в скорости
Не однозначно. Неизвестно, кто скомпилирует более быстрый код, привычный Вам компилятор или JIT-компилятор V8. Это уже не от языка зависит, а от потрохов компилятора.
Вот только сорцы интерпретируются очень редко. Если попадается неоптимизируемый случай.
В норме выполняется байткод для LLVM. И вопрос скорости становится неоднозначным.
Да и динамическая природа JS не то, чтобы располагает к оптимизациям (не просто так идея куцего ASM.js появилась в свое время).
Вообще-то нет. Всегда запускается параллельно интерпретация и компиляция, и только если интерпретация завершается раньше, чем компиляция, она берётся за основной метод. И JS, если не использовать некоторые редкие особенности, вполне оптимизируется. Не просто так идея asmjs не была принята Гуглом — они сразу нацелились на ускорение работы всего JS.
А вот про '==' или '===' я вообще не спрашиваю даже сеньоров, так как это реально сложные особенности языка в которых очень легко запутаться. Хочешь завалить любого? Начти спрашивать про сраврение разных типов.
Главное как кандидат мыслит, а не то что он помнит или не помнит. Если понимаешь механизм, но что-то забыл, всегда можно нагуглить, но если считаешь что animate сделан на for, то хорошего кода можно не ждать.
Плох тот солдат, что не хочет стать генералом. Ничего удивительного в том, что кандидаты метят на сеньорские позиции. Пришли к вам на собеседование, узнали какие вопросы задают на сеньоров, подготовились к следующему разу получше и в итоге прошли.
За пол года подобрали всего пару человек в команду и это отнюдь не вундеркинды. У нас не завышенные требования. Просто на позиции senior ожидается, что человек не просто пользуется инструментом, а копает глубже и понимает как инструмент работает и вникает в то для чего сделана та или иная конструкция, а не использует что-то потому что так принято.
Сейчас же 90% сеньоров это джуны или мидлы заучившие react life cycle и набор популярных библиотек без понимания как они работают.
Прошу прощения, если кого обидел.
Такие наставники, потом и растят новое поколение уверенное, что let ввели ради хайпа и оно ничем от var не отличается (это из реальных ответов).
Ну справедливости ради при нормальной архитектуре без огромных функций различия у них и правда минимальные. А если в циклах использовать const
то и вообще на практике нету разницы между let
и var
А если в циклах использовать const то и вообще на практике нету разницы между let и varА как же редекларация? Let не даст вам случайно назвать переменную так же, как уже существующая.
Вопросы на подумать и объяснить почему что-то работает именно так а не иначе, очень хорошие, с этим соглашусь. Только это ситуацию не меняет.
Пока на собеседовании не будет никакого входного барьера, вроде тестового задания, на него так и будут приходить псевдо-сениоры. А стоит ввести задание, так пойдут возмущения "я – адекватный разработчик, ничего за бесплатно вам решать не буду". Где тут найти баланс – непонятно.
Недавно стал тимлидом и тоже столкнулся с подобной ситуацией. Грустно. Мне кажется отчасти это результат завышенных зарплат в нашей отрасли, которые приманивают людей этим всем не горящих.
Потом будем полагать, что нормально притворяться поповским сыном для поступления в гимназию…
Как правило, for (… in ..), for (… of ..) требуются для написания обходов сложных структур и сборки данных из нескольких разнотипных источников.Как часто вам нужны именно они, и не подходят
Object.entries()
, Object.keys()
и Object.values()
, результат которых потом можно обрабатывать через map(), reduce() и прочие? Не люблю for, особенно, операторный, потому что с ним код менее структурирован, и весь функционал работает на сайд-эффектах.
JS и его запретные тайны