Вертикальная черта, затем ноль

    Заголовок, выраженный словами, понадобился только для поисковой находимости. Но речь пойдёт о роли символьной конструкции «|0» в JavaScript.

    Впервые на неё я обратил внимание, когда переводил FAQ про asm.js и читал спецификации этого подмножества языка JavaScript. Там «|0» служит, например, для указания типа значения, возвращаемого из функции: увидели «|0» после значения — значит, перед нами знаковое целое.

    Вдругорядь я заметил конструкцию «|0» в примере кода на Гитхабе, где происходило преобразование к целому числу результата деления на 1024².

    Тогда глаза мои открылись, и я увидел прекрасные возможности:

    ( 3|0 ) === 3;       // целые числа не изменяет
    ( 3.3|0 ) === 3;     // у дробных чисел отбрасывает дробную часть
    ( 3.8|0 ) === 3;     // не округляет, а именно отбрасывает дробную часть
    ( -3.3|0 ) === -3;   // в том числе и у отрицательных дробных чисел
    ( -3.8|0 ) === -3;   // у которых Math.floor(-3.3) == Math.floor(-3.8) == -4
    ( "3"|0 ) === 3;     // строки с числами преобразуются к целым числам
    ( "3.8"|0 ) === 3;   // при этом опять же отбрасывается дробная часть
    ( "-3.8"|0 ) === -3; // в том числе и у отрицательных дробных чисел
    ( NaN|0 ) === 0;     // NaN приводится к нулю
    ( Infinity|0 ) === 0;     // приведение к нулю происходит и с бесконечностью,
    ( -Infinity|0 ) === 0;    // и с минус бесконечностью,
    ( null|0 ) === 0;         // и с null,
    ( (void 0)|0 ) === 0;     // и с undefined,
    ( []|0 ) === 0;           // и с пустым массивом,
    ( [3]|0 ) === 3;          // но массив с одним числом приводится к числу,
    ( [-3.8]|0 ) === -3;      // в том числе с отбрасыванием дробной части,
    ( [" -3.8 "]|0 ) === -3;  // и в том числе с извлечением чисел из строк,
    ( [-3.8, 22]|0 ) === 0    // но массив с несколькими числами вновь зануляется
    ( {}|0 ) === 0;                // к нулю также приводится пустой объект
    ( {'2':'3'}|0 ) === 0;         // или не пустой
    ( (function(){})|0 ) === 0;    // к нулю также приводится пустая функция
    ( (function(){ return 3;})|0 ) === 0;    // или не пустая
    

    Итак, во-первых, перед нами удобное средство отбрасывания дробной части.

    • По отношению к отрицательным числам оно полезно тем, что дробное число превращается не в ближайшее меньшее целое число (возрастая по модулю), как это случилось бы после «Math.floor()», а в ближайшее меньшее по модулю целое число (возрастая по значению). Нередко именно это и требуется.
       
    • По отношению к положительным числам оно полезно уж тем одним, что конструкция «|0» более чем на порядок короче по сравнению с «Math.floor()». Поэтому она может и должна вызывать у разработчиков привыкание не меньшее, чем та принятая в jQuery запись «$()», о которой я говорил четыре дня назад, что с неё никто добровольно не перейдёт обратно на «document.getElementsByClassName()», например.

    Во-вторых, перед нами удобное средство преобразования различных типов к целым числам.

    • Оно умеет выковыривать целые числа (и отбрасывать дробную часть у дробных чисел), извлекая их из строк (с отбрасыванием начальных и конечных пробелов) и даже из одноэлементных массивов.
       
    • По отношению к данным всех остальных типов (которые не умеет извлечь) это средство действует как универсальный обнулитель. Нередко именно это и требуется.

    Наблюдая действие этого средства, следует сопоставлять его с тем приёмом +» перед значением), который рекомендуется в «JavaScript Garden» (а за ним и в «JavaScript Гарден») для преобразования к числу.

    При таком сопоставлении тотчас явствует, что плюс преобразует к дробному числу, делая возможными и такие экзотические варианты дробных чисел, как минус бесконечность (получаемая, например, из «+[" -Infinity "]») или NaN (из «+{}»), тогда как «|0» преобразует к целому числу, и оттого экзотические варианты обнуляются.

    Рекомендую этот приём к широкому применению в вашем джаваскриптовом коде по мере нужды.
    Поделиться публикацией

    Комментарии 93

      +3
      Что насчет кроссбраузерности? Во всех IE корректно работает?
        –11
        Думаю нет ) Это же ИЕ.
          +5
          Во всех, только нужно помнить о больших числах.
            +3
            Побитовые операторы появились задолго до IE, а таблицы неявного приведения типов существуют с «бородатых» лет первой спецификации. (9 Type Conversion)
            +13
            Можно ещё использовать ~~
              +16
              ~~null;      // => 0
              ~~undefined; // => 0
              ~~0;         // => 0
              ~~{};        // => 0
              ~~[];        // => 0
              ~~(1/0);     // => 0
              ~~false;     // => 0
              ~~true;      // => 1
              ~~1.2543;    // => 1
              ~~4.9;       // => 4
              ~~(-2.999);  // => -2
              
                +3
                Ну да, действует точно так же, что и |0: это тоже битовый оператор, который требует целочисленный тип.
                  +7
                  Важным достоинством «~~» является другой приоритет, который в ряде случаев экономит усилия по расстановке скобок:

                  ( "-3.8"|0 ) === -3;    // true
                  "-3.8"|0 === -3;        // -3
                  ~~"-3.8" === -3;        // true
                  

                  Правда, зато «~~» приходится записывать не по порядку операций (наподобие того, как «(x / 1024)|0» означает «сперва поделить, затем отбросить дробную часть»), а спереди.

                  (А ещё «~~» ничего не означает в asm.js, но за пределами asm.js это не недостаток.)
                +2
                Я про это писал на Хабре ажно три года назад. Тогда не оценили :-)
                  0
                  Дык оттого и не оценили, что Вы сами тогда, находясь под влиянием замеров скорости работы джаваскриптов, принизили значение своего сообщения, высказали «не ведитесь на короткую крутую запись» и проч.

                  С тех пор скорости работы движков, JavaScript исполняющих, существенно изменились, причём в лучшую сторону, тогда как средняя скорость пробежки по клавиатуре босыми пальцами рук ничуть не переменилася.

                  Вывод: предпочитать следует такие приёмы, которые экономят труд программиста, даже если вычислительно они представляются несколько малоэффективными.
                +12
                блин, век живи — век учись…
                  +25
                  Я просто оставлю это здесь: Bitwise Operators
                    +20
                    Не пострадает ли от этого читабельность исходников? Я бы не стал использовать подобные трюки — один и тот же синтаксис для совершенно разных операций, и семантика совершенно не очевидна. Исходники в идеале должны читаться как связный текст. Лучше я для каждого из этих вариантов напишу всё в лоб, через if() или функцию с говорящим названием, если там что-то не совсем примитивное и очевидное.

                    Хотя для спортивного программирования в стиле «умести крутую демку в 2048 байт» может пригодиться, да.
                      0
                      Не пострадает ли от этого читабельность исходников?


                      Вот именно.

                      Для отсекания дробной части в Harmony предложен Math.trunc(). Поэтому пишем полифилл, и вперёд — такое моё мнение.
                      +34
                      Стоит упомянуть про подводные камни с большими числами:
                      4242334634634634523523 | 0; // => 368574464
                      3234534534 | 0; // => -1060432762
                      
                      

                        +18
                        Только помните:
                        ~~"12345678901" // -539222987
                        "12345678901"|0 // -539222987
                        "12345678901"*1 // 12345678901
                        parseInt("12345678901", 10) // 12345678901
                        
                          +10
                          Хорошо, что Вы напомнили про это, но стоило бы ещё и упомянуть, почему это так. А так оно потому что согласно стандарту операнды битового оператора приводятся в 32-х битному знаковому числу, и, как следствие,
                          Результат является знаковым 32-битовым целым.
                          0
                          Кстати об использовании таких штук, по моему гораздо удобнее это писать перед выражением, то есть

                             var a = 0 ^ x   
                                , b = 0 | x
                          


                          Тогда сразу видно приведение типов.
                            +1
                            Да, и можно использовать как отключатель первой строки при отладке:
                            if(0|'test_srting_1'||'test_string_2')
                            	alert('test_2');
                            if('test_srting_1'||'test_string_2')
                            	alert('test_1');
                            
                              +2
                              это можно и короче.

                              var debug = false
                              
                              debug && alert(' hit me!')
                              
                            +24
                            ИМХО, стоит всё-таки знать меру в использовании особенностей таких конструкций в JS. Кстати говоря, я более, чем уверен, что у многих программистов на JS есть проблема с тем, чтобы разобраться в чужом JS-коде, в том числе из-за использования таких конструкций. Если вам нужно сократить объем кода, то в современных реалиях намного проще будет использовать тот же Google Closure Compiler и учитывать, как он работает, нежели писать такой «код» самому. Из моего опыта, понятность кода (даже на JS) намного важнее лишних пары килобайт, которые будут «выжаты» путем такой «оптимизации».
                              +13
                              Код пишется для людей, а не для интерпретатора же. Нужно писать его так, чтобы его легко было прочитать. Разве что только код пишется специально для конкурса обфускаторов.

                              Даже в таком слабо типизированном языке с динамической типизацией как javascript в ваших программах вы всё равно знаете, откуда к вам приходят данные и какого типа. От этого уже можете плясать.
                                +1
                                // Но иногда удобнее писать
                                if( ~str.indexOf('sub') ){
                                }
                                
                                // чем
                                if( str.indexOf('sub') != -1 ){
                                }
                                
                                  0
                                  А если вам нужно рядом инвертировать условие, и реализовать логику «нет в подстроке», то как вы будете писать?
                                    0
                                    if (!~str.indexOf('sub')) { }
                                    не?
                                    вообще, если один раз запомнить, то такое легко читается.
                                      +4
                                      Лично меня `!~` пугает. Я всё-таки выражения читаю по большей части слева-направо: «в строке индекс подстроки не равен -1»
                                        0
                                        Вы правы, я тоже никогда так не писал, а потом стал набирать много кода на jquery и приучился писать так:

                                        if (!~$.inArray(str, arr)) { }

                                        согласитесь, тут больше смысла. хотя да, пугает иногда.
                                          –5
                                          Вы используете библиотеку для поиска вхождения одной строки в другую?
                                          Теперь я понимаю кто такой «программист на jquery»
                                            0
                                            «$.inArray()» — это не поиск подстроки в строке, это поиск элемента в массиве.
                                              +2
                                              Странный вопрос и выводы.

                                              Я не использую библиотеку для поиска вхождений одной строки в другую. С чего вы взяли? В коде написан поиск строки в массиве. Но если вам интересно, то и так я уже давно не пишу. Просто добавляю к проекту SugarJS и использую стандартный indexOf(). Но раньше, когда моё бывшее начальство краснело от идеи изменения прототипов, мне приходилось для поиска внутри массива использовать jQuery, чтобы это везде работало. (до 9 версии IE её не поддерживал). А поиск строки в строке вроде очень давно во всех браузерах реализован, так что использовал его, ну уж точно не $.inArray(), акститесь.
                                                0
                                                ясно, я неверно вас понял
                                                да и не знал, что с indexOf для Array у ie была беда
                                                  0
                                                  у меня аж руки затряслись когда прочитал что indexOf() до 9 ИЕ не поддерживался. shift+ctrl+F по проекту — 189 вхождений, ПАНИКА!
                                                  Работает все в восьмом, напугали блин.
                                                    0
                                                      +2
                                                      Что это, Бэрримор?
                                                      В смысле зачем? Расширить прототип я и сам могу.
                                                      А indexOf работает начиная с IE8, что собственно и требуется.
                                                        +1
                                                        Чтобы не беспокоится ни о чем и ВНЕЗАПНО не бросаться проверять код на «а вдруг я использую фишку из es6 которую {browser} не поддерживает».
                                                        Просто вы так панически среагировали :)
                                                          0
                                                          Ааа, ну в моем случае это мертвому припарки. Нашего монстра проще пристрелить чем хоть как-то покрыть тестами.
                                        +6
                                        Удобнее, но не читабельнее. Я вообще предпочту написать так:
                                        if( str.contains('sub') ){
                                        }
                                        
                                          –4
                                          С 0 выйдет конфуз у вас
                                            +5
                                            А, это у меня конфуз вышел, contains() же :-S
                                              0
                                              Ну, contains.
                                                +1
                                                Похоже, есть смысл говорить «вне Firefox 19 конфуз выйдет».
                                            0
                                            Согласен, но на участках с очень большим количеством итераций, лучше перестраховаться.
                                            + Это не кроссбраузерно, а ломать голову, расширен ли прототип на проекте не охота.
                                              –1
                                              + Это не кроссбраузерно, а ломать голову, расширен ли прототип на проекте не охота.

                                              Как это не кроссбраузерно? А вы ломаете голову, подключён ли у вас jQuery, объявлена ли функция foo или bar при её вызове? Метод contains точно такой же, как любой другой. Или у вас есть этот метод или нету.

                                              Согласен, но на участках с очень большим количеством итераций, лучше перестраховаться.

                                              Этот аргумент вообще не понял. По-моему нету ничего более очевидного, чем слово contains — это самая лучшая перестраховка.
                                                0
                                                Насчёт кросс-браузерности полезна библиотека Underscore.string и запись «_.str.include(str, 'sub')».
                                                  +1
                                                  Chrome 25 — www.rubaxa.org/screenshot/6b15eb6a74e217165b52df651dd2.png

                                                  И да, когда я разрабатываю код, который будет работать в неизвестном мне месте, стараюсь писать так, чтобы понял и IE6. + Даже подключенный shim будет на 10 порядково медленней.
                                                    +1
                                                    Спичечные оптимизации? Может, вы функции вообще не пишете, а сразу инлайните код, потому что так быстрее?
                                                      0
                                                      В браузере — да, под V8 — реальный прирост производительности. + если рассматривать старые браузеры, то там это играет значимую роль. Всё зависит от количества таких мест.
                                          +4
                                          за if (~arr.indexOf(val)) { /* ... */ } на меня косо смотрят, а вот за такое наверное вообще убьют :)
                                            0
                                            А шутка с!!! прокатывает?
                                            –1
                                            Вообще у этого «трюка» есть немного иное назначение, не только для приведения к int, но и замене Math.round — `(x + .5)|0` или Math.floor. С такой же целью это используется в ActionScript.
                                              –1
                                              Пожалуйста, внимательно прочитывайте блогозапись до конца перед сочинением комментариев к ней. Вы пропустили характеристику «удобное средство отбрасывания дробной части».
                                                +1
                                                Простите меня.
                                                  –1
                                                  Охотно прощаю.
                                                    +1
                                                    Но прошу заменить, в статье не написано, что `(x + .5)| 0` по скорости быстрей Math.round (если рассматривать синтетический тест).
                                                      0
                                                      Да, не написано, Вы правы.
                                                        +4
                                                        А потом кто-то после вас застрянет на этом месте и будет думать: «а что же имел в виду автор?». Данная конструкция заметно ухудшает читабельность кода.
                                                        Указанный метод быстрее, но оба округления настолько быстры, что экономить стоит только в тех случаех когда у вас миллионы округлений в секунду.

                                                        P.S.
                                                          0
                                                          Спасибо за тесты.

                                                          В Файерфоксе они показывают одинаковые значения скорости того и другого округления.
                                                            +1
                                                            Добавил еще:
                                                            Chrome 25 vs FF 19 vs IE 10
                                                            image
                                                            +1
                                                            Друзья, я просто пытаюсь показать, что данная практика имеет свои предпосылки, а не только из-за короткой записи. Писав маленькое приложение под iPhone где приходилось работать с пикселями (canvas) такие микро оптимизации помогали. Это всё было 3 года назад, надеюсь сейчас ситуация изменилась.
                                                              +1
                                                              Да, но вы же не указали, что это микро оптимизации. Кто-то решит «вау, круто» и начнет везде лепить подобную конструкцию.
                                                              Ведь изначально, код надо писать понятным, а оптимизировать уже по мере нужды.
                                                              +3
                                                              тест этот совсем не замеряет скорости округления, потому что loop invariant code motion выносит инвариант цикла за цикл.

                                                              замеряется по сути дела скорость кручения пустого цикла.

                                                              почитайте: mrale.ph/blog/2012/12/15/microbenchmarks-fairy-tale.html
                                                                0
                                                                Спасибо, хорошая статья. Тест и вправду не показателе, но как я уже говорил ранее, это реальная практика, которая давала эффект в JS/ActionScript, 3 года назад точно.
                                                    +7
                                                    Конструкция Math.floor() гораздо очевидней, чем |0. Это уже повод не использовать второй путь, даже без учета сообщений выше про проблемы с большими числами. И вообще, не зря битовые операции по умолчанию запрещены в jslint и jshint
                                                      0
                                                      До известной степени это впечатление справедливо.

                                                      Я замечу, однако, что программист до известной степени способен воздействовать на собственное впечатление от «|0» при помощи мнемотехники, то есть мысленно связать этот оператор с некоторым запоминающимся символом с той целью, чтобы надёжнейше обеспечить себе его очевидность, причём надолго.

                                                      На ум приходит, например, идея рассматривать вертикальную черту как острый и безжалостный нож гильотины, отсекающий последующую цифру — что метафорически символизирует отсечение всей дробной части числа. Такой символ очень неплохо передаёт всю суть происходящего — не хуже, чем английское слово «floor» передаёт суть названного им метода.
                                                      +8
                                                      WAT
                                                        +1
                                                        Вдругорядь

                                                        куда-куда?)
                                                          +1
                                                          Не «куда?», а скорее «как?».

                                                          Вдругорядь.
                                                            +20
                                                            Mithgol
                                                              0
                                                              Да ладно, вы вот слово «патч» упоминаете и ничего (я наскоро посмотрел ваши комментарии), совершенно нерусское, корявое слово, а «вкруголядь» хоть русское.
                                                                +2
                                                                но, блин, такое сложное.
                                                              +1
                                                              Эта конструкция делает следующее: объект.toString().перевод_в_integer, если_это_число_иначе_в_ноль. Сразу становится понятно почему массив из двух элементов преобразуется в ноль. Потому что [1,2].toString() это «1,2».
                                                                0
                                                                Бесконечность?
                                                                Может кто-нибудь объяснить зачем она нужна и как ее использовать? Чес слово не могу придумать.
                                                                  +2
                                                                  А что возвращать при 1/0?
                                                                    +8
                                                                    Синий экран.
                                                                  +1
                                                                  > не округляет, а именно отбрасывает дробную часть
                                                                  Раньше это называлось округлением вниз по модулю. Да, есть разные способы окрулять.
                                                                    +2
                                                                    Что-то я серзьезно не понимаю, а где вы увидили магию? Битовое «или». Яваскрипт просто округляет вниз перед произведением этой операции. 5.6|0 = 0111 | 0000 = 5
                                                                    > но массив с несколькими числами вновь зануляется
                                                                    У него, скорее всего, спрашивается toString(), а этот метод для массива [1,2] вернет «1,2», а дальше срабатывает обычное приведение типов яваскрипта к целому числу, и для этой строки вернет нуль.

                                                                    А вообще я бы поостерегся использовать такие уловки в коде — при первом быстром взгляде может и мозг сломать:)
                                                                      +4
                                                                      Ну дык это и не магия — а просто несколько нестандартное использование привычного инструмента.
                                                                      Так, например ''+x всегда возвращает строку, +x — число, !!x — булевское значение. Тоже, казалось бы, ничего магического — но при этом удобная штука для быстрого приведения типов.
                                                                      0
                                                                      (вдруг кто не знал)

                                                                      Из подобных вещей я чаще всего использую || для обработки undefined и null / false значений:

                                                                      function GetRow (index)
                                                                      {
                                                                        return $("selector").eq (index || 0);
                                                                      }
                                                                      


                                                                      Аналогично для строк:
                                                                      $("selector").val (n || "")
                                                                      


                                                                      Особенно это актуально когда конструируем data для ajax запроса, т.к. там если в поле передадим «undefined», то на сервер это в null не переведёт и будет ругаться (по крайней мере для MVC2 это так).
                                                                        +2
                                                                        оператор || возвращает первый положительный вариант или последний из условия, если все остальные дают false

                                                                        То есть вы можете писать и такие конструкции
                                                                        > null || 0 // => 0
                                                                        > null || 0 || 1 // =>1
                                                                        > null || 1 || 0 // => 1
                                                                        > null || 0 || 0 // => 0
                                                                        > null || 0 || null // => null
                                                                        > null || 0 || null || undefined // => undefined
                                                                        > null || 0 || '' || [] || undefined // => []
                                                                        > null || 0 || '' || false || undefined // => undefined
                                                                        > null || 0 || '' || false || undefined || 1 // => 1
                                                                        > null || 0 || '' || false || 1 || undefined // => 1
                                                                        
                                                                        +3
                                                                        Хоть бы разок упомянули об операторе побитовое «или»:
                                                                        developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/Bitwise_Operators#.7C_(Bitwise_OR)
                                                                        А не про «символьную конструкцию «|0» в JavaScript»
                                                                          –1
                                                                          Я это нарочно.

                                                                          Я опасался вот чего: если думать про «|0» в терминах «побитовое ИЛИ с нулём», то этот оператор воспринимается в качестве NOP-подобной «мусорной команды», потому что из курса двоичной арифметики каждому известно, что побитовое ИЛИ с нулём не меняет значение другого (ненулевого) операнда.

                                                                          Роль этого оператора в джаваскрипте определяется правилами предварительного преобразования операнда к целому числу, а не смыслом самóй операции. Но мне не хотелось упоминать и об этом: смысл статьи сильнее дойдёт до читателя, если читатель сам проделает часть пути к его разгадке.
                                                                            +1
                                                                            Воистину эти слова почти цитатой можно (нужно) запостить апдейтом :)
                                                                              0
                                                                              А я вот думаю, что не нужно, причём по тем же соображениям.
                                                                          0
                                                                          На jsPerf, оказывается, уже есть тест в тему: jsperf.com/bitwise-or-vs-math-floor
                                                                            +1
                                                                            Вы должны учитывать то что в этом тесте не учитывается оверхед на вызов функции Math.round
                                                                            Вот подобный, но учитывающий вызовы: jsperf.com/math-round-vs-x-0-5-0/2
                                                                              +1
                                                                              Но в том и смысл, что это и не нужно выносить в функцию, а можно использовать инлайн. И в предыдущем тесте сравнивает прирост по сравнению с floor, а не с round
                                                                            –1
                                                                            Просто так

                                                                            Python 2.7.3 |EPD_free 7.3-2 (32-bit)| (default, Apr 12 2012, 14:30:37) [MSC v.1500 32 bit (Intel)] on win32
                                                                            Type «copyright», «credits» or «license()» for more information.
                                                                            >>> 3|0
                                                                            3
                                                                            >>> 3.8|0

                                                                            Traceback (most recent call last):
                                                                            File "<pyshell#1>", line 1, in 3.8|0
                                                                            TypeError: unsupported operand type(s) for |: 'float' and 'int'
                                                                            >>> -3|0
                                                                            -3
                                                                            >>> '3'|0

                                                                            Traceback (most recent call last):
                                                                            File "<pyshell#3>", line 1, in '3'|0
                                                                            TypeError: unsupported operand type(s) for |: 'str' and 'int'
                                                                            >>>
                                                                              +1
                                                                              А чего вы еще ожидали, питон строго типизирован.
                                                                              0
                                                                              Вдругорядь я заметил конструкцию «вдругорядь», и тогда глаза мои открылись, и я увидел, что это Mithgol
                                                                                0
                                                                                ааа, мои глаза…
                                                                                После этого вы говорите, что код Perl вам трудно понять?
                                                                                :)
                                                                                  +1
                                                                                  Вроде уж совсем основы.
                                                                                  n|0 или n>>0 выполнят преобразование ToInt32, n>>>0 — ToUInt32, для отбрасывания дробной части использовать аккуратно — с большими числами будут проблемы — (Math.pow(2,31)|0)===-2147483648.
                                                                                  Т.к. внутренние операции преобразования типов наружу не вынесены, приходится крутиться
                                                                                  function ToInt32(val){
                                                                                    return val>>0}
                                                                                  function ToUInt32(val){
                                                                                    return val>>>0}
                                                                                  function ToUInt16(val){
                                                                                    return (val>>>0)%65536}
                                                                                  var ToInteger=Number.toInt||Number.toInteger||function(val){
                                                                                    return val=+val,val!==val?0:val!==0&&val!==Infinity&&val!==-Infinity?(val>0||-1)*Math.floor(Math.abs(val)):val}
                                                                                  
                                                                                    –1
                                                                                    да чего уж там
                                                                                    Math.floor = function (x){
                                                                                    return x|0;
                                                                                    }
                                                                                    

                                                                                    //никогда не делайте так со своим кодом
                                                                                      –1
                                                                                      Кроме Math.floor есть ещё round и ceil, также существует parseInt, и все они работают в несколько раз быстрее у меня сейчас в хроме.

                                                                                      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                                                      Самое читаемое