Comments 31
Частично такие проблемы решаются линтерами на JS (eslint) и на TS (tslint). Скажем запрет на ==
или запрет на складывание чисел и строк в TS.
Например в React.js null и undefined это допустимые значения, не рендерящие ничего, и зачастую используется выражение:
var component = someFuncReturningUndefined()
...
render() {
return (
<div>{component}</div>
)
}
Преобразование типов — это один из базовых механизмом JavaScript, знание которого является основой продуктивной работы
95% этой статьи состоит из разного мусора, вроде что будет если сложить пару массивов и пустой объект. Это никак не повышает продуктивность вашей работы. Поиск по \b==\b
в моём коде выдаёт ровно 0 результатов, хоть я и не пользуюсь TypeScript или Flow. Значит ли это, что я пишу непродуктивный код? А если я явным образом (например Number(someStringNumber)
) привожу типы к нужным мне видам, значит ли это, что я просто чайник, и мне следует распечатать таблицы преобразования и заучивать их?
Честно говоря, всякий раз, когда в статье с тегом JavaScript
я вижу что-то вроде !+[]+[]+![]
, я начинаю относиться к ней более чем предвзято, предполагая, что это очередной набор бессмысленной чепухи от JavaScript-хейтера, или же <irony>
очень полезный материал для начинающих</irony>
.
Изначально учил паскаль и С/С++, что-то по мелочи писал, затем Java'у, в частности Java ME, несколько лет на ней, затем ActionScript и MXML… сейчас вот решил изучить JS — возможно придётся пописать на нём… Ко всему подхожу фундаментально, не люблю/не могу хватать вершки, «галопом по европам», так сказать, поэтому начал гуглить и искать какие-то учебники по языку, чтобы проникнуться в основы. И вот первое на что натыкаешься везде и повсюду, так это на приведение типов в JS: все хватаются за голову, пишут какие-то приколы, куча мемов на эту тему и т.п., я даже немного был удивлён и озадачен по началу. Начал читать элементарные темы «приведение типов» и т.п. и всё стало ясно-понятно, если понимать/знать как работает приведение типов в JS. Не совсем понятно, что тут такого особенного и сверхъестественного, с этими задачками, что столько народу так носится с данной темой и бугуртит… :)
Естественно, все эти задачи носят чисто методический учебный или тестовый характер и совершенно не нужны в реальном применении. Ну такого ведь в любом языке хватает… Я хочу сказать, что ведь это не магия какая-то и не что-то из ряда вон выходящее. По большому счёту, здесь нет никакой хитрости, если знать «правила» и знать основы языка.… Или я чего-то не понимаю… :)
В любом случае я сомневаюсь, что когда-то придётся писать/использовать нечто подобное :) Ибо, скорее всего везде и всегда будет использоваться явное приведение типа, ибо всегда надо отдавать себе отчёт в том, что ты делаешь и что хочешь получить…
Последний пример с датой. Результат зависит от того, прибавляете вы ноль или вычитаете.
Где ещё можно встретить такое поведение?
Не представляю как можно случайно написатьПредставьте, что каждое значение возвращает функция или является полем какого-нибудь объекта — станет не так очевидно.
Функционал авто позволяет на полной скорости врезаться в стену. Но мы сознательно так не делаем, потому что это может привести к печальным последствиям.
</zanuda>
Но как справочник – неплохо.
мне совершенно не понятно, как можно программировать на каком-либо языке, не зная как там и что к чему кастится и в каком случае
Легко. Я так уже лет 8 работаю. Что в PHP, что в JS я не знаю наизусть таблицы приведения типов (кроме самых базовых вещей, вроде приведения к boolean, и операций со строками и числами).
true + false
12 / "6"
"number" + 15 + 3
15 + 3 + "number"
[1] > null
"foo" + + "bar"
'true' == true
false == 'false'
null == ''
!!"false" == !!"true"
[‘x’] == ‘x’
[] + null + 1
0 || "0" && {}
[1,2,3] == [1,2,3]
{}+[]+{}+[1]
!+[]+[]+![]
new Date(0) - 0
new Date(0) + 0
Я правильно отвечу лишь на пару пунктов. Остальное считаю дикой дичью. Такой код не должен проходить вменяемое review.
Что, например, в логическом «И» операнды выполняются слева направо, пока какой-то из них не станет false, а в логическом «ИЛИ», наоборот, пока один из них не окажется true. И это не бесполезные знания, на мой взгляд.
Ну это прописные истины, без знания которых работать вообще невозможно. Но статья то совсем не о них. Статья про {}+[]+{}+[1]
и !!"false" == !!"true"
прочие грабли.
Это обычные тестовые задания на выявление понимания основ языка
Если на тестовом задании вы будете задавать тот список выше, не удивляйтесь тому, что большая часть middle и senior специалистов может вам "не перезвонить". Даже если знает ответы на эти вопросы.
я не знаю наизусть таблицы приведения типов
О каких таблицах речь? Я так понял, существует просто набор правил, если так можно выразиться и часть там да, надо запомнить, а остальное просто понимать.
Такой код не должен проходить вменяемое review.
Дак это и не «рабочий» код какой-то. Это тестовые учебные задачи. Вы тестирования что ли никакого никогда не проходили по любому ЯП? Почти везде есть подобное.
Но статья то совсем не о них. Статья про {}+[]+{}+[1] и !!«false» == !!«true» прочие грабли.
Да какие тут грабли? Такие же правила обычные, которые, вроде как, не мешало бы и знать, на мой взгляд:
Для явного преобразования используется двойное логическое отрицание !!value или вызов Boolean(value).
Строка, если не пустая, то она true при кастинге. Вот и всё, что необходимо знать, чтобы понять что получится в случае !!«false» == !!«true»… Вы не знаете что ли, что + по дефолту операнды кастит к строке, если один операнд строка? Что, чтобы сделать явное приведение к числу, то надо + впереди поставить (+«10»)? Что [] — ссылка на массив, а {} — на объект...? Ну и т.д. и т.п.
Я согласен что есть какие-то хитрые заморочки с подобным {}+[]+{}+[1], но на то они и тестовые/контрольные задачки :) А остальные вещи по моему довольно просты…
Это тестовые учебные задачи
Тестовые задачи, имхо, должны проверять что-нибудь полезное, а не складывание массивов с объектами. Правило простое ― не важно чему равно [] + {}
("[object Object]"
), если оно равно чепухе. Только воздух сотрясать.
В реальном коде у вас вообще могут быть .valueOf
, get
& set
, Proxy
, with {}
(если сильно не повезёт) и прочие хитрые штуки. В лучшем случае вы встретите что-нибудь вроде + new Date()
вместо Date.now()
.
В серьёзном JS приложении, чем меньше вы хаков примените, тем проще потом будет с этим всем работать. Вот о чём стоило бы написать красным, заглавными буквами, так это о том, что данные нужно приводить к корректному виду как можно раньше (на ранних этапах обработки) при помощи явных преобразований (Boolean()
, String()
, Number()
и пр.), а затем следить за чистотой работы с ними.
Скажем, получив некий ID
из location.query
, нужно преобразовать его в Number
(если это number) сразу на этапе парсера queryString, в противном случае, возможны трудноуловимые сюрпризы.
Я считаю подобные статьи, где акцент делается не на том, как избежать проблем, а на зоопарке хитрых преобразований, откровенно вредны. Неопытный разработчик начнёт так писать в своём коде, и вообще думать, что нормально. Разработчики других же языков натыкаясь на такие статьи думают, что у нас в JS мире царит жесточайший дурдом.
Для явного преобразования используется двойное логическое отрицание !!value или вызов Boolean(value).
Небольшое количество здравого смысла в статье присутствует, тут соглашусь. Но в основном она про то, как "foo" + + "bar"
превращается в "fooNaN"
.
но на то они и тестовые/контрольные задачки
Давайте ещё крышки люков обсуждать. Лично я на собеседованиях задаю те задачи, которые дают мне понять понимает ли человек то, чем занимается, или пишет по принципу попугая (бездумная копипаста и "лишь бы работало"). Если человек не знает и знать не хочет, чем равно 4 - - '2'
, зато хорошо понимает что такое имутабельность, асимптотика, может написать бинарный поиск, умеет в свою экосистему, умеет правильно решать прикладные задачи, и вообще горит энтузиазмом, то мне дела нет до его 4 - - '2'
, потому, что он не напишет этого в коде.
Естественно, все эти задачи носят чисто методический учебный или тестовый характер и совершенно не нужны в реальном применении. Ну такого ведь в любом языке хватает…
…
В любом случае я сомневаюсь, что когда-то придётся писать/использовать нечто подобное :) Ибо, скорее всего везде и всегда будет использоваться явное приведение типа, ибо всегда надо отдавать себе отчёт в том, что ты делаешь и что хочешь получить…
Т.е., как бы естественно, что в реальном приложении/коде такое не надо использовать! :)
Если человек не знает и знать не хочет, чем равно 4 — - '2', зато хорошо понимает что такое имутабельность, асимптотика, может написать бинарный поиск, умеет в свою экосистему, умеет правильно решать прикладные задачи, и вообще горит энтузиазмом, то мне дела нет до его 4 — - '2', потому, что он не напишет этого в коде.
Ну я вот просто уверен, что такой человек без проблем ответит и на такой вопрос! :) А тестовые задания (приложения или реализация как-то конкретного функционала), вроде как, итак даются, в конце-концов, при приёме на работу :)
И вообще, если уж речь зашла о приёме на работу (не понятно с чего, кстати :))), то я тоже считаю, что это всё фигня полная :)
А для новичка подобные тесты, возможно, помогут лучше понять основы языка, проникнуться, так сказать :) Возможно, благодаря им, он даже заполнит у себя какие-нибудь пробелы в знаниях :)
Ну я вот просто уверен, что такой человек без проблем ответит и на такой вопрос! :)
А я нет. Выше про список писал. На многие из этих FooNaN
-ов я не отвечу, скорее всего. Почему? Ну если человек не имеет привычки отнимать от чисел строки, и вообще заниматься подобной непотребщиной, то он может и не знать, что получится в результате. Он возможно даже когда-то знал это, но за полной бесполезностью эти знания ушли. Я вот не был уверен, что получится 6
, т.к. привык относиться к таким вещам так: "получится дичь, обязательно переделать". И на всякий случай проверил в консоли.
А для новичка подобные тесты, возможно, помогут лучше понять основы языка, проникнуться, так сказать
В таком случае должны быть явные disclaimer-ы, что так делать нельзя, и почему так делать нельзя, и что это имеет скорее исследовательско-просветительский характер, нежели какой-либо практический.
Просто работают законы Мерфи.
«Если что-то можно сделать не правильно, то это обязательно будет сделано не правильно»
Несмотря на кажущуюся «искусственность» примеров, встретится с чем-то подобным в реальных проектах стремится к 100%
:-)
Результат правильный, но объяснение с неточностями. В объяснении вы используете группировку операндов с || — (0 || «0») && {}, что есть не совсем так. У оператора && приоритет выше, чем у || поэтому будет так: 0 || («0» && {}) Operator precedence.
Лоический опертар «И» возвращает первое falthy значение или последнее значение, если все операнды — truthy. В даном случае и строка и пустой объект — truthy значения, поэтому вернётся последний — {}.
Теперь имеем 0 || {}. Логический оператор «ИЛИ» возвращает первое truthy значение или значение последнего операнда, если все операнды falthy. Первый операнд 0 — falthy, второй {} — truthy, поэтому он и вернётся. Результат — {}.
Пожалуйста, поправьте, если где-то ошибся.
Мне все-таки непонятно с примером {}+[]+{}+[1]
. Ну т.е. объяснение, что первые скобки игнорируются как пустой блок кода, вроде бы, работает:
> {}+[]+{}+[1]
< "0[object Object]1"
> {}+[]
< 0
Но:
> {}+[]+{}
< "[object Object][object Object]"
Вот что тут произошло? С чего вдруг первые скобки стали рассматриваться не как пустой блок кода, а как объект?
0 || «0» && {}
.Оператор && имеет более высокий приоритет по отношению к ||
Правильный вариант:
0 || "0" && {}
==> 0 || ("0" && {})
==> false || (true && true) // внутреннее преобразование
==> 0 || {}
==> false || true // внутреннее преобразование
==> {}
Неявное преобразование типов в JavaScript. Сколько будет !+[]+[]+![]?