Pull to refresh

Comments 31

Интересно, а от каких преобразований типов зависит реальный код решающий реальные задачи? Я подозреваю, что если сохранить только преобразования между строками, чистами и логическими значениями, а преобразования массивов, объектов, null и undefined запретить, то все будет работать нормально. Правда со строками и числами веселия уже хватает.

Частично такие проблемы решаются линтерами на JS (eslint) и на TS (tslint). Скажем запрет на == или запрет на складывание чисел и строк в TS.

Преобразование null и undefined к bool использовать довольно удобно во многих местах.
Например в 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>.

UFO just landed and posted this here
Немного рассуждений на тему…
Изначально учил паскаль и С/С++, что-то по мелочи писал, затем Java'у, в частности Java ME, несколько лет на ней, затем ActionScript и MXML… сейчас вот решил изучить JS — возможно придётся пописать на нём… Ко всему подхожу фундаментально, не люблю/не могу хватать вершки, «галопом по европам», так сказать, поэтому начал гуглить и искать какие-то учебники по языку, чтобы проникнуться в основы. И вот первое на что натыкаешься везде и повсюду, так это на приведение типов в JS: все хватаются за голову, пишут какие-то приколы, куча мемов на эту тему и т.п., я даже немного был удивлён и озадачен по началу. Начал читать элементарные темы «приведение типов» и т.п. и всё стало ясно-понятно, если понимать/знать как работает приведение типов в JS. Не совсем понятно, что тут такого особенного и сверхъестественного, с этими задачками, что столько народу так носится с данной темой и бугуртит… :)
Естественно, все эти задачи носят чисто методический учебный или тестовый характер и совершенно не нужны в реальном применении. Ну такого ведь в любом языке хватает… Я хочу сказать, что ведь это не магия какая-то и не что-то из ряда вон выходящее. По большому счёту, здесь нет никакой хитрости, если знать «правила» и знать основы языка.… Или я чего-то не понимаю… :)
В любом случае я сомневаюсь, что когда-то придётся писать/использовать нечто подобное :) Ибо, скорее всего везде и всегда будет использоваться явное приведение типа, ибо всегда надо отдавать себе отчёт в том, что ты делаешь и что хочешь получить…
Благодаря такой «гибкости» банальные ошибки вместо того, чтобы просто выплюнуть в стектрейс что-то информативное, вызывают интересные спец.эффекты где-то в неожиданном месте приложения. Такое вот искусственное препятствие в языке.
UFO just landed and posted this here

Последний пример с датой. Результат зависит от того, прибавляете вы ноль или вычитаете.
Где ещё можно встретить такое поведение?

UFO just landed and posted this here
В большинстве языков эти «опечатки» не будут фатальны, ибо приведение типов просто вызовет ошибку на месте.

Не представляю как можно случайно написать
Представьте, что каждое значение возвращает функция или является полем какого-нибудь объекта — станет не так очевидно.
P.S. За статью спасибо! Добавил в закладки :)
&ltzanuda&gt
Функционал авто позволяет на полной скорости врезаться в стену. Но мы сознательно так не делаем, потому что это может привести к печальным последствиям.
&lt/zanuda&gt

Но как справочник – неплохо.
К счастью (за редким исключением), у автомобилей не бывает так, что вы, например, нажимаете тормоз и сигнал левого поворота одновременно, а автомобиль срывается с места и в стену.
Хорошо преподнесли данный материал, спасибо за статью.
UFO just landed and posted this here
Да при чём тут качество кода? Никто не заставляет так писать. Это обычные тестовые задания на выявление понимания основ языка, то что человек знает мат часть, так сказать, а не просто нахватался вершков и что-то там делает, возможно, по примерам, но в некоторых случаях совершенно не понимает как и почему это работает. Например мне совершенно не понятно, как можно программировать на каком-либо языке, не зная как там и что к чему кастится и в каком случае… Что, например, в логическом «И» операнды выполняются слева направо, пока какой-то из них не станет false, а в логическом «ИЛИ», наоборот, пока один из них не окажется true. И это не бесполезные знания, на мой взгляд.
мне совершенно не понятно, как можно программировать на каком-либо языке, не зная как там и что к чему кастится и в каком случае

Легко. Я так уже лет 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%
:-)
Странно другое: не увидел ссылок на автотесты, позволяющие распознать в коде подобные конструкции. Полагаю, как и в других языках, программист просто закончит чтение, когда увидит в коде анекдот «1 + 1 = 2, потому что 1000 / синий». Но потраченное время не вернёшь.
Какие автотесты в динамически типизированном ЯП?!
Только если полное множество значений всех типов…
А так «run and pray» :-)

Скажем так, статичная типизация не позволяет делать некоторых вещей.
Это не панацея, а просто некоторая строгость.
Есть сомнения по поводу примера 0 || «0» && {}
Результат правильный, но объяснение с неточностями. В объяснении вы используете группировку операндов с || — (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             // внутреннее преобразование
==> {}
Sign up to leave a comment.