Pull to refresh

Почему WAT

JavaScript *
Translation
Original author: Adam Iley
Недавний доклад Гарри Бернхардта на CodeMash оказался довольно популярен (прим. пер.: в том числе и на хабре). В докладе он шутит по поводу некоторых особенностей поведения ruby и javascript.
Вряд ли я смогу вас убедить, что есть какой-то смысл в том, на что жалуется Гарри. Тем не менее, надеюсь, я смогу вам объяснить, почему javascript ведёт себя именно так.

Символ +


Символ + в javascript может обозначать три вещи.

Это может быть бинарный оператор сложения, применяющийся к двум числам.
Это может быть бинарный оператор сложения строк, применяющийся к двум строкам.
Это может быть унарный оператор «это число положительное», применяющийся к одному числу.

Всё немного усложняет автоматическое приведение типов в javascript. Например, если перед или после символа + находятся не строки или числа, javascript автоматически преобразует операнды либо к строкам, либо к числам.

boolean, null, undefined преобразуются к числам (true = 1, false = 0, null = 0, undefined = NaN)
объекты, массивы, функции преобразуются к строкам.

Так что теперь, когда вы видите
[] + []

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

[] + {}

В этом случае пустой объект справа также будет преобразован в строку. Так как у него не реализован метод toString, в соответствии с наследованием, будет использован метод toString Object-а, который возвращает строку "[object Object]" для объектов. Поэтому в данном примере мы складываем пустую строку со строкой "[object Object]".

Пустые блоки кода


Теперь рассмотрим странности парсера javascript. Вы, возможно, заметили, что мы используем фигурные скобки в javascript для двух различных целей. Чтобы обозначить блоки кода (например, после if или как часть определения функции) и чтобы записывать объекты в объектной нотации (её ещё называют JSON). В некоторых языках программирования блоки кода могут использоваться во многих местах внутри существующего кода, чтобы определить новую область видимости. Javascript (пока что) не использует область видимости на уровне блоков кода, тем не менее, он позволяет использовать блоки кода внутри кода. А значит, иногда, когда вы пишете {}, парсер javascript воспринимает это как пустой блок кода, а иногда — как пустой объект.
Вообще говоря, если фигурная скобка находится в начале выражения, она будет интерпретирована как начало блока кода. Вы можете заставить парсер javascript обработать её как объект, обрамив в обычные скобки.

{} + []

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

Дополнительная уловка заключается в том, что javascript приводит объект, функцию или массив к числу, предварительно приводя их к строке. Пустой массив приводится к нулю. Вот, например:

+ [] === 0 /* потому что пустой массив становится пустой строкой "", которая становится 0 */
+ [1] === 1 /* потому что массив с единицей становится строкой "1", которая становится  1 */
isNaN(+[1, 2] ) /* потому что массив, содержащий 1, 2, становится строкой "1,2", которая не является числом */
+({toString: function() {return "3"}}) === 3

Итак, пустой объект приводится к строке "[object Object]", которая приводится к NaN, когда ей из-за префиксного оператора + приходится преобразовываться в число.
{} + {}


Символ −


В отличие от +, символ − довольно прост. У него всего два значения: бинарный оператор, применяющийся к двум числам, и префиксный оператор, применяющийся к одному числу. Как вы уже знаете, строки, которые не представляют собой числа, приводятся к NaN, если они должны быть использованы как числа. Поэтому вас не должно удивлять, что
isNaN("wat" - 1) // true


Почему это важно?


Лишь некоторые из обозначенных моментов могут встретиться и встречаются на практике. Давным-давно, когда люди использовали eval, чтобы парсить JSON, им приходилось оборачивать свой JSON в скобки, чтобы парсер случайно не воспринял его как блок кода. Часто важно помнить о типе переменных и преобразовывать ваши строки в числа, потому что иначе люди будут удивлены, увидев 3 + 1 = 31. Знание случаев, когда происходит приведение типов, сделает большое количество правил по поводу == false гораздо менее запутанными. И, самое главное, это позволит вам находиться среди слушателей доклада «javascript сошёл с ума» и вести себя так, как будто всё происходящее для вас совершенно логично (а таких, похоже, было мало).
Tags:
Hubs:
Total votes 68: ↑60 and ↓8 +52
Views 39K
Comments Comments 68