Это сайт — моя персональная записная книжка. Интересна мне, по большей части, история, своя жизнь и немного программирование.

«Странный» JS

В ДжаваСкрипте полно странностей, да, но популярная картинка, которая получила сейчас широкое хождение (не буду её приводить, просто суть перепишу) имеет отношение только к безграмотности её авторов. Суть такова:

[] + [] // массив + массив
"" // результат — пустая строка

[] + {} // массив + объект
"[object Object]" // В результате получаем объект? Ну ок

{} + [] // объект + массив
0 // 0? Реально?

{} + {} // объект + объект
NaN // Not a Number? WAT?

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

В ДжаваСкрипте фигурные скобки, помимо объекта, задают ещё и блок кода. В данном случае все «объекты» слева — на самом деле блоки кода. Откроем консоль и попробуем это доказать:

Вот что происходит на самом деле (9.90КиБ)

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

С массивом, возвращающим пустоту при суммировании (конкатенации) всё ещё проще — в данном случае производится попытка вызвать метод toString, который у массива выводит строку его значений через запятую. Так как значений нет, строка пустая.

В общем, ничего сильно странного не происходит.

42 комментария
vladon 2015

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

Евгений Степанищев (bolknote.ru) 2015

Комментарий для vladon:

Синтаксис языка с чёткими и однозначными правилами, есть принятый стандарт, там всё это описано.

Иван 2015

Не надо про стандарт. Это прокол языка, который делали за 10 дней. Я люблю JS, но не стоит писать в духе «так и было задумано».

Евгений Степанищев (bolknote.ru) 2015

Комментарий для Иван:

Я нигде не написал, что «так было задумано». Я считаю, что язык надо знать и знаю, что такое поведение стандартизовано — стандарт есть, гадать не надо, надо просто понимать как оно работает. Я это поведение понимаю и никуда не заглядывая, знаю как сделать так, чтобы слева всё-таки было тоже объект.

Алексей 2015

Да большинство этих WTF-ов от незнания авторами JS и переноса на JS своих знаний других языков или вообще своих фантазий. На wtfjs.com, например первым пунктом вообще идёт какой-то идиотизм. И из всех «wtf-ов» только «The Hungry Variable» не очевиден с самого начала.

Евгений Степанищев (bolknote.ru) 2015

Комментарий для Алексей:

The Hungry Variable

Это больше относится к особенностям «вебкитов»

Иван 2015

Это очень неочевидное поведение. Оно вынуждает часто заглядывать в стандарт. У ПХП такая же история: вал дикостей, на каждую тебя тыкают в стандарт. Можно утверждать, что ты один умный, а остальные дураки, но сообщество все расставило по местам -​-​ люди массово переходят на Питон\Руби. Хотят понятный код, а не тысячу пунктов в стандарте, чудаки! JS держится, поскольку еще нет альтернатив. Хотя уже пишут JS на Кофе- и Кложуре- скриптах.

Евгений Степанищев (bolknote.ru) 2015

Комментарий для Иван:

Это очень неочевидное поведение. Оно вынуждает часто заглядывать в стандарт. У ПХП такая же история: вал дикостей, на каждую тебя тыкают в стандарт.

У ПХП нет стандарта, какой смысл их сравнивать?

Можно утверждать, что ты один умный, а остальные дураки, но сообщество все расставило по местам — люди массово переходят на Питон/Руби

Что-то я этого не наблюдаю. Наоборот, появился совершенно необузданный интерес к серверному ДжаваСкрипту. У Пайтона и Руби своя коробка дикостей и на Пайтоне я запросто напишу код, который с первого раза понять будет очень трудно (а Руби не знаю и знать не хочу)

Мимо проходил 2015

Комментарий для Евгения Степанищева:

Что-то я этого не наблюдаю.

Видимо, имелось в виду, что люди массово переходят на Python/Ruby с PHP, а не с JavaScript.

...на Пайтоне я запросто напишу код, который с первого раза понять будет очень трудно...

Просим, просим.

Артур Мудрик 2015

Питон, Яваскрипт — так правильнее. И, да, знать всякие вот такие нестандартные штучки в ЯП, очень необычные и wtf’шные, совершенно не обязательно — только если повыделываться, а для реального программирования всё это знать не нужно.

o4kapuk 2015

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

Иван 2015

на Пайтоне я запросто напишу код, который с первого раза понять будет очень трудно

Передергиваете. В питоне нельзя сложить список со словарем и получить None, к примеру. Будет либо ошибка, либо потребуется перегружать операторы. Это не превосходство именно питона -​-​ это знак хорошего дизайна языка.

Евгений Степанищев (bolknote.ru) 2015

Комментарий для Мимо проходил:

Просим, просим.

http://bolknote.ru/all/1842/

Евгений Степанищев (bolknote.ru) 2015

Комментарий для Артур Мудрик:

Питон, Яваскрипт — так правильнее.

Питон — это змея, а Пайтон назван в честь «Монти Пайтона», устал писать каждый раз. А «Яваскрипт» — так я не говорю.

И, да, знать всякие вот такие нестандартные штучки в ЯП, очень необычные и wtf’шные, совершенно не обязательно — только если повыделываться, а для реального программирования всё это знать не нужно.

Знать такие вещи — обязательно. Не для того, чтобы их писать, а для того чтобы уверенно разбираться что происходит в коде. Иногда что-то из этой «экзотики» встречается в коде. И вовсе не потому что человек выпендривался, потому что он и не знал, что выпендривается — он код писал.

Евгений Степанищев (bolknote.ru) 2015

Комментарий для Иван:

Передергиваете. В питоне нельзя сложить список со словарем и получить None, к примеру.

И тут нельзя, вы статью не читали что ли? Вот вам загадка из «Пайтона» (из второго, третий я не знаю): почему object больше пустого массива, но меньше пустого списка?

>>> () > object() > []
True

o.mokhov@ya.ru 2015

А как же классика жанра? http://www.youtube.com/watch?v=FqhZZNUyVFM

angru 2015

Python3
Traceback (most recent call last):

  File «print.py», line 3, in <module>
    print(() > object() > [])

TypeError: unorderable types: tuple() > object()
[Finished in 0.2s with exit code 1]

Евгений Степанищев (bolknote.ru) 2015

Комментарий для angru:

из второго, третий я не знаю

Алексей 2015

Комментарий для Евгения Степанищева:

При чём тут вебкит? Это просто факт, что range в регулярке работает по коду символа, а не по фантазиям автора.
Оно так и в перле работает

Евгений Степанищев (bolknote.ru) 2015

Комментарий для Алексей:

А… не разобрался я.

Иван 2015

почему object больше пустого массива, но меньше пустого списка?

Без понятия. Это прокол языка -​-​ в тройке исправили. Я трачу свое время эффективней -​-​ не сравниваю объекты разных типов и другим не даю в код-ревью.

Евгений Степанищев (bolknote.ru) 2015

Комментарий для Иван:

Ну так и в JS никто не плюсует блок кода с объектом или массивом.

Кстати, я понятие имею. Объект меньше списка, потому что его первая буква «o» (object) идёт до «t» (tuple), а больше массива потому, что буква «l» (list) идёт ещё раньше. То есть сравниваются имена типов.

Все эти вещи нужно знать, не для того, чтобы писать так (я уже писал это выше, напишу ещё раз), а чтобы быстро разобраться в проблеме, когда она возникнет — когда что-то кто-то передаст не туда по ошибке.

Max 2015

[] + {}
Почему получается «[object Object]» а не NaN ?

Max 2015

Получается, что при +{} фигурные скобки воспринимаются как блок кода, а при  « » + {} фигурные скобки воспринимаются как объект и вызывается метод toString() ?

Евгений Степанищев (bolknote.ru) 2015

Комментарий для Max:

[] + {}
Почему получается «[object Object]» а не NaN ?

Это не арифметическое сложение, а строковое (конкатенация). У обоих объектов дёргается toString(), потом преобразуется в строку. Можно в этом убедиться, если выполнить:

[] + {toString: function() { alert(’1’)} }

Евгений Степанищев (bolknote.ru) 2015

Комментарий для Max:

Получается, что при +{} фигурные скобки воспринимаются как блок кода

Нет. +{} — вызывается унарная операция (второго-то аргумента нет), {} воспринимается как объект. Унарная операция «+» не над числом даёт NaN.

а при « » + {} фигурные скобки воспринимаются как объект и вызывается метод toString() ?

Нет, тут просто не арифметическое сравнение, так как аргументы операции «+» не числа.

Max 2015

Я так понимаю, получается, что если перед {} ничего нет — то это воспринимается как блок кода. Если же перед {} что-то написано, то это уже воспринимается как объект?
{} + {}  — первый элемент — это просто блок кода.
({} + {})  — «[object Object][object Object]» т. е. первый элемент воспринимается уже как объект
Я правильно понял?

Евгений Степанищев (bolknote.ru) 2015

Я правильно понял?

Правильно. Можно ещё написать так: ({}) + {}. Это надо делать по той же причине, по которой оборачиваются вызов анонимной функции на месте её описания в скобки — чтобы обозначить, что тут выражение. Причём годится любой способ указания, что это выражение, просто скобки — более универсальный.

Если же перед {} что-то написано, то это уже воспринимается как объект?

Не что-то, а то, что указывает на выражение. Например, function a() {} + {}. Тут {} — блок кода, будет NaN, потому что JS разобъёт эту конструкцию на две — определение функции и +{}, а (function a() {}) + {} — тут уже будет выражение и пустой объект. Вернётся конкатенация строковых представлений.

Евгений Степанищев (bolknote.ru) 2015

Тут {} — блок кода

я имел ввиду, что {} после function блок кода, а после плюса — объект.

Roman 2015

Комментарий для Евгения Степанищева:

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

Не обязательно. Практика показывает, что человек, который быстро делает работающее приложение на Angular/Ember/Node.js/Whatewer, пусть даже и с багами (которыми можно заняться потом), гораздо полезнее того, кто заморачивается всякими мелочами. Идеальное знание языка в принципе может понадобиться, когда делаешь библиотеку, предназначенную для разработчиков, и то не всегда.

Евгений Степанищев (bolknote.ru) 2015

Комментарий для Roman:

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

Возможно мы просто разными продуктами занимаемся. У нас продукт большой длительности (годы и десятилетия).

Артур Мудрик 2015

Комментарий для Евгения Степанищева:

человек в критической ситуации сходу понимающий где проблема
А такие люди (которые быстро понимают где проблема) невозможны без опыта знания тонкостей.

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

Вообще, когда вот читаю такие статьи в вашем блоге (про магию языка и что это на самом деле не магия, а нужно просто знать свой язык идеально иначе ты не прогарммист... ну и пошло-поехало), то всегда такое чувство что вы выпендриваетесь и преувеличиваете собсвтенную важность. Выпендрежность заключается в том, что вы пытаетесь блеснуть знаниями и искренне выразить свое фи и удивляться, мол, «как!? это жы элементарщина, это жы азы языка, это жы должен знать любой программист, иначе он не программист, фу, ужас!!1». Такое чувство, что вы специально читаете документацию и лезете в дебри языка, чтобы потом написать в блоге, что это элементарщина, что это вообще прямо очевидно даже слепому не-программисту и позор тем программистам, кто не знает всякие такие необычные штучки в языке. Вы начитались где-то вот о таких необычных тонкостях языка и теперь выпендриваетесь и пытаетесь искусственно искренне удивляться, когда кто-то не знает о том, что знаете (теперь) вы. Программистское ханжество, вот, придумал, это так называется.

Евгений Степанищев (bolknote.ru) 2015

Комментарий для Артур Мудрик:

Это претенциозные надуманные глупости.

Это мой опыт.

Roman 2015

Комментарий для Евгения Степанищева:

Я вообще-то тоже только долгосрочными продуктами занимаюсь. Про вас я в курсе. Ужасный у вас продукт.

Евгений Степанищев (bolknote.ru) 2015

Комментарий для Roman:

Про меня вы не в курсе совершенно.

Евгений Степанищев (bolknote.ru) 2015

Комментарий для Roman:

Ужасный у вас продукт.

Какой из?

Roman 2015

Комментарий для Roman:

Абсолютно все. Конечно, я имею в виду компанию, в которой вы работаете. Хотя в вашем Гитхабе я тоже ничего полезного не обнаружил.

Евгений Степанищев (bolknote.ru) 2015

Комментарий для Roman:

Абсолютно все. Конечно, я имею в виду компанию, в которой вы работаете.

Ни один работающий в нашей компании сотрудник, не знает подробностей о качестве всех продуктов. Вам-то откуда знать?

Хотя в вашем Гитхабе я тоже ничего полезного не обнаружил.

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

Roman 2015

Комментарий для Евгения Степанищева:

Я примерно представляю, каким может быть российский продукт, разрабатываемый десятилетиями на ПиЭйчПи, да еще и за гос. бабло. Интересно, много людей туда наколхозило? Хотя, я могу ошибаться, может у вас там «ЕкмаСкрипт Сикс» давно, тогда могу только порадоваться.

Евгений Степанищев (bolknote.ru) 2015

Комментарий для Roman:

Теперь всё встало на свои места.

pavelpat 2015

Элементарно ж.
Если питон не может сравнить 2 объекта (у них не обределены методы для сравнения) то он превращает их (одного из них) в строки (берет имя типа) и сравнивает строки через strcmp (object.c:794).