JavaScript: проверьте свою интуицию


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

    Ответы и свой вариант объяснения почему такое поведение логично я буду скрывать под спойлером. Сразу оговорюсь, что не претендую на непоколебимую истину своих версий и буду рад их обсудить. В разгадывании вам может помочь отличный русский перевод спецификации ECMAScript 5 за который большое спасибо iliakan!


    1. Так что помните, дети, всегда подставляем системы счисления!

    Для начала простой, но прикольной пример. Что вернут такие вызовы parseInt?
    parseInt('fuck');
    parseInt('fuck', 16);
    

    Решение
    parseInt('fuck');     // NaN
    parseInt('fuck', 16); // 15
    

    Как работает parseInt: он посимвольно проверяет переданную строку на соответствие указанной вторым аргументом системе счисления и если находит некорректный символ, то завершает работу. По умолчанию, система счисления считается десятичной. В не самых свежих браузерах если переданная строка начинается с 0, то по умолчанию система считается восьмеричной. В десятичной системе счисления символ ‘f’ недопустим и функция заканчивается свою работу не найдя ни одной цифры. А вот в шестнадцатиричной системе символ ‘f’ допустим и соответствует десятичному числу 15. Символ ‘u’ не допустим и функция завершает свою работу.


    2. Может кто подскажет?

    Чему равно такое выражение?
    "Why am I a " + typeof + "";
    

    Решение
    “Why am I a number”
    

    Оператор “+” обладает большим приоритетом чем “typeof”, поэтому указанная запись эквивалентна следующей:
    “Why am I a ” + (typeof (+ “”)).
    

    Унарный “+” выполняет приведение к числу, поэтому “typeof” от результата его выполнения — это number.


    3. Кто больше?

    Ну и кто больше?
    [1, 2, 4]         <  [1, 2, 5]
    [1, 2, 'd']       <  [1, 2, 5]
    [1, 2, ['d', 5]]  <  [1, 2, [20, 5]]
    [1, 2, 5]         == [1, 2, 5]
    [1, 2, 5]         <= [1, 2, 5]
    [1, 2, 5]         >= [1, 2, 5]
    

    Решение
    [1, 2, 4]         <  [1, 2, 5]        //true
    [1, 2, 'd']       <  [1, 2, 5]        //false
    [1, 2, ['d', 5]]  <  [1, 2, [20, 5]]  //false
    [1, 2, 5]         == [1, 2, 5]        //false
    [1, 2, 5]         <= [1, 2, 5]        //true
    [1, 2, 5]         >= [1, 2, 5]        //true
    

    Согласно спецификации ECMAScript 5 для выполнения операции сравнения аргументы, которые не являются примитивами, должны быть приведены к примитивам (если кому интересно, то пункт 11.8.5 “Алгоритм сравнения абстрактного отношения”). Сначала JavaScript пробует преобразовать аргумент к числу с помощью метода valueOf и только когда это не удается приводит к строковому типу (изначально я хотел написать здесь про естественность сравнения чисел, и что когда мы говорим, что слово А больше слова Б, то имеем в виду длину. Но из беседы с коллегой выяснилось, что это не правда. Когда мы сравнимаем два слова мы инстанцируем связанные с ними образы и сравнимаем их. И только если образов не нашлось, то сравниваем слова не как метки образов, а именно как набор букв). Для массивов метод valueOf не определен и поэтому они сравниваются как строки:
    "1,2,4" < "1,2,5" //true
    "1,2,d" < "1,2,5" //false
    

    Но все вышесказаное верно только для операций сравнения. В случае оператора “==”” приведение к примтивам осуществляется только если один из аргументов примитив, а во всех остальных случаях оператор “==” возвращает true только если оба аргумента ссылаются на один объект (пункт 11.9.3 “Алгоритм сравнения абстрактного равенства”), что в нашем случае не так.

    Следует заметить, что для объектов сравнение работать не будет, т.к. их дефолтный toString возвращает [object Object].


    4. Кто больше?-2

    По мотивам предыдущей загадки, точнее её решения.
    var obj1 = {
      test: 'test',
      toString: function(){ return 10; },
      valueOf: function(){ return 100; }
    }
    
    var obj2 = {
      test: 'test',
      toString: function(){ return 100; },
      valueOf: function(){ return 10; }
    }
    
    obj1 ? obj2
    

    Решение
    obj1 > obj2
    

    Как было сказано в предыдущем решении, при сравнении JavaScript сначала пытается привести непримитивные аргументы к числам, т.е. вызывает метод valueOf. А 100 > 10.


    5. Бесконечности безумия

    Что вернет?
    parseFloat('Infinity');
    Number('Infinity');
    parseInt('Infinity');
    

    Решение
    parseFloat('Infinity'); //Infinity
    Number('Infinity');     //Infinity
    parseInt('Infinity');   //NaN
    

    Число отображается как бесконечность, когда оно превышает максимальное число с плавающей точкой: 1.7976931348623157E+308. Т.е. по сути Infinity это константа с дробным значением и поэтому она определяется parseFloat и Number, но не определяется parseInt.


    6. Игры с плюсами

    Для тех, кто внимательно читал предыдущие решения, не составит труда сказать, что вернет вот такое выражение:
    "foo" + + "bar"
    

    Решение
    ("foo" + + "bar") === "fooNaN" // true

    Выделим унарный +:
     “foo” + (+ “bar”)
    

    Как не трудно видеть, он вернет NaN, т.к. строку нельзя привести к числу. Таким образом:
    "foo" + NaN === "fooNaN".
    



    7. Самое маленькое число

    Кто больше?
    Number.MIN_VALUE ? 0
    

    Решение
    Number.MIN_VALUE > 0
    

    Согласно спецификации MIN_VALUE — это число наиболее приближенное к 0, которое позволяет JavaScript. Приблизительно равно 5e-324. Все числа меньшие по модулю конвертируются в 0. Называть его минимальным, пожалуй, логично, т.к. максимальное отрицательное так и называется “максимально отрицательное”, но все-таки вносит определенную путаницу.


    8. Налог на роскошь

    Что увидим на экране?
    alert(111111111111111111111);
    

    Решение
    111111111111111111000
    

    JavaScript интерпретирует большие числа в экспоненциальной форме:
    111111111111111111111 = 1.11111111111111111111e20.
    

    Но для хранения 1.11111111111111111111 точности не хватает и поэтому
    (1.11111111111111111111e20).toString() == 111111111111111110000
    



    9. “Булева арифметика”

    (true + 1) === 2;​
    (true + true) === 2;
    true === 2;
    true === 1;
    

    Решение
    (true + 1) === 2;​  ​ // true
    (true + true) === 2;  // true
    true === 2;       // false
    true === 1;       // false
    

    Двухместный оператор “+” либо складывает числа, либо конкатенирует строки. При приведении true к числу будет возвращена 1.

    Оператор “===” в отличии от “+” не производит приведение типов и поэтому true строго не равно единице.


    10. Отрицание пустоты

    [] == ![]
    

    Решение
    [] == ![]      //true
    

    Небольшая цитата из правила приведение типов в операторе == из спецификации:

    1. Если Type(x) – Number и Type(y) – String, вернуть результат сравнения x == ToNumber(y).

    2. Если Type(x) – String и Type(y) – Number, вернуть результат сравнения ToNumber(x) == y.

    3. Если Type(x) – Boolean, вернуть результат сравнения ToNumber(x) == y.

    4. Если Type(y) – Boolean, вернуть результат сравнения x == ToNumber(y).

    5. Если Type(x) – либо String, либо Number, и Type(y) – Object, вернуть результат сравнения x == ToPrimitive(y).

    6. Если Type(x) – Object и Type(y) – либо String, либо Number, вернуть результат сравнения ToPrimitive(x) == y.

    Сначала вычислим правую часть равенства и получим:
    [] == false
    

    т.к. объект всегда приводится к true.
    Теперь по правилу 4:
    [] == 0
    

    по правилу 6 и со знаниями из 3 задачки:
    "" == 0
    

    Затем по правилу 2:
    0 == 0
    

    Вуаля!


    Заключение

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

    Если вы знаете другие интересные примеры, то прошу добавлять в комментарии скрывая ответы и объяснения под спойлер. Со своей стороны не могу удержаться, чтобы не добавить в качестве бонуса задачку из комментариев к одной из старых статей:

    Что больше?
    Math.min() ? Math.max()
    

    Решение
    Math.min() > Math.max()
    

    Согласно определению метода Math.max в случае отсутствия аргументов возвращается -Infinity. Аналогично min возвращает просто Infinity.

    И да, главное: не используйте это в боевом коде! Пожалейте тех, кто будет его читать.
    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 68

      +11
      Можно было просто дать ссылку на wtfjs.com/ там это все есть и даже больше, гораздо больше
        0
        Можно легализовать поддержку нотации WTFJS во всех IDE — на уровне с TODO и FIXME :)
        // WTFJS: We parse till we get an error using radix 16 => 0xf => 15 :)
        var fifteen = parseInt('fuckItMan', 16);
        
          +18
          Большая часть примеров оттуда. Я этого не скрываю и даже в метках указал. В качестве цели статьи я ставил разбор логики поведения. По-моему, простой просмотр «смотри какая хрень в JavaScript» не несет пользы, полезно понять почему именно так.
          +1
          Возможно, выступлю в роли кэпа, но все же:

          var x = NaN < 10;
          var y = NaN >= 10;

          x == y;
            +7
            После статьи кликнул на «ответить», чтобы проверить ответ.
          • UFO just landed and posted this here
              +30
              Запишу, пожалуй. В продакшене пригодится.
              +10
              А еще хорошо бы сказать: «НИКОГДА ТАК НЕ ДЕЛАЙТЕ» в своем коде.
              • UFO just landed and posted this here
                  0
                  Все три варианта «The number is 2014»
                    –2
                    Но! Вот так:

                    'The number is ' + + 20 — 14

                    'The number is ' + + 20 + — 14

                    'The number is ' + + 20 — + 14
                  –1
                  Еще одна задачка…

                  Длинна масивов:

                  []
                  [,]
                  [,,,]
                  [,undefined,]
                  [,NaN,]
                  [,NaN,null,0]
                  [,null,NaN,]
                    +2
                    Запись с последней запятой чревата проблемами с подсчетом в старых IE. Но в соответствии с ECMAScript 5 я бы своими словами описал это так:
                    — если нет запятых и нет даже одного элемента, то массив пустой (длина = 0);
                    — если элемент 1 и нет запятых, то длина = 1;
                    — если перечисление элементов заканчивается запятой, то длина массива равна количеству запятых (в старых ие еще +1, а посему avoid!);
                    — если перечисление элементов заканчивается любым значением (хоть undefined), то длина массива равна количеству запятых + 1.
                      +7
                      Прочитать документацию — это читерство. Надо смотреть на примеры и кричать: «я думал иначе!»
                        +1
                        Ctrl+Shift+J — вот где настоящее читерство)
                      0
                      [] //0
                      [,] //1
                      [,,,] //3
                      [,undefined,] //2
                      [,NaN,] //2
                      [,NaN,null,0] //4
                      [,null,NaN,]  //3
                      


                      В массивах допускается завершающая запятая. Тут я могу только сослаться на Флэнагана. Раздел 7.1:
                      «Если литерал массива содержит несколько идущих подряд запятых без значений между ними, создается разреженный массив. Элементы, соответствующие таким пропущенным значениям, отсутствуют в массиве, но при обращении к ним возвращается значение undefined.»
                      «Синтаксис литералов массивов позволяет вставлять необязательную завершающую запятую.»
                      Зачем так сделано мне не понятно. Если учесть, что IE8 и младше падает от лишней запятой, а дефолтные настройки JSLint определяют ее как ошибку, то логично предположить, что это браузер костыль от ошибок в JS коде.
                        +8
                        Зачем так сделано мне не понятно

                        Для записи вида:
                        array = [
                          1,
                          2,
                          3,
                        //  4,
                        ]
                        

                        Чтобы удобно было добавлять/удалять/закомментировать элементы.
                      +1
                      Было недавно еще (справедливо для JS, PHP, C++)

                      >>> parseInt((0.1 + 0.7) * 10);
                      7
                      >>> (0.1 + 0.7) * 10;
                      7.999999999999999
                        0
                        isFinite(1 / Number.MIN_VALUE); // ?
                        (1 / Number.MAX_VALUE) !== 0; // ?
                        

                        Скрытый текст
                        isFinite(1 / Number.MIN_VALUE); // false
                        (1 / Number.MAX_VALUE) !== 0; // true
                        


                        Это не особенность JS как такового, а типа Number (который double).

                        Number.MIN_VALUE равен 5×10-324, 1 / Number.MIN_VALUE по-идее должен бы быть равен 2×10323, но это число слишком большое.

                        А наоборот — пожалуйста, точности хватает.

                        Подробнее о том, почему в отрицательную сторону экспонента немного «вместительнее» — на википедии.

                        *UPD:* Поправил пример
                          –9
                          OMG, зачем ты позволил людям создать язык программирования, который допускает такие ребусы?

                          — Мыкола, як москали жаву кличут?
                          — Як?
                          — йава!
                          — Повбывав бы.
                            –1
                            в тексте 4 привидения **-()
                              +5
                              Стыдно сказать, но правильно решил 3/10.
                              А ведь считаю себя неплохим спецом по JS %)
                              Ну, справедливости ради, стоит сказать, что ни один из приведенных примеров в реальной боевой обстановке не применяется, и не должен применяться.
                              Но все равно, как человеку, читавшему спеку стандарта, стыдно.
                                +4
                                Вы просто издеваетесь над стариком Крокфордом. Он столько времени и сил потратил на то, чтобы приучить людей «не кидаться едой» (где еда — это JavaScript, а «кидаться» — это сравнивать наны с фолсами и складывать объекты с массивами), а употреблять ее по назначению активно пользуясь ножом и вилкой (такими как JSLint и использование только «безопасного» подмножества языка), ан нет — детвора не только продолжает развлекаться, но и устраивает из этого соревнования и меряется тем, кто дальше докинет :c
                                  +7
                                  «Why am I a » + typeof + "";
                                  «Who am I a number»


                                  JS еще и наречие меняет?
                                    +1
                                    Ох, как же набили оскомину статьи подобного рода… Удивительно даже, что здесь нет
                                    [1, 10, 5].sort
                                    
                                    или
                                    [1, 2, 3].map(parseInt)


                                    Или вот ещё недавно всплыло в твитере:
                                    var a,b=[1,2,3][1,2,3]; 
                                    a === b;
                                    


                                    И если с sort, map и parseInt/parseFloat понять ещё можно — люди привыкли, что в других языках действует так, то другие вещи просто удивляют как степенью непонимания данного кода автором, так и бессмысленностью написанного выражения.

                                    Ну вот зачем, например, сравнивать массивы? Давайте ещё функции сравним по приколу, это тоже возможно (навскидку, сравниваются их .length, которые равны арности функции), но какой в этом практический смысл?

                                    Но вообще — читайте документацию и многие нелогичности окажутся просто другим поведением и обретут смысл…
                                      +1
                                      [1, 2, 3].map(parseInt)
                                      

                                      внимательное чтение документации от такого спасет. Хотя лучше всего увидеть такой пример в статье, обратить внимание и, если не запомнить, то в следующий раз знать куда копать.
                                      Для тех кто не знает в чем проблема. Map передает обработчику несколько аргументов. В частности вторым передает номер элемента в массиве. У функции parseInt, есть второй, необязательный, аргумент, который указывает на систему счисления. Таким образом в указанной записи второй и третий элемент массива будут обработаны в единичной и двоичной системе счисления и parseInt от них вернет NaN.

                                      Или вот ещё недавно всплыло в твитере:
                                      var a,b=[1,2,3][1,2,3]; 
                                      a === b;
                                      


                                      так массивы создавать нельзя, поэтому в обоих переменных лежит undefined, который равен сам себе.

                                      Давайте ещё функции сравним по приколу, это тоже возможно (навскидку, сравниваются их .length, которые равны арности функции)

                                      Функции это объекты, поэтому их можно сравнивать только на больше\меньше и тогда перед сравнением они будут приводится к строке. В случае проверки на равенство все так же как для массивов в пункте 3:
                                      В случае оператора “==”” привидение к примтивам осуществляется только если один из аргументов примитив, а во всех остальных случаях оператор “==” возвращает true только если оба аргумента ссылаются на один объект (пункт 11.9.3 “Алгоритм сравнения абстрактного равенства”).


                                      про набили оскомину я косвенно ответил в начале комментария. Глупо отрицать, что в JS есть грабли, но по-моему лучше узнавать их месторасположения из таких статей, а не на боевом коде.
                                        0
                                        Я привёл пример про var a,b=[1,2,3][1,2,3] для того, чтобы показать, что люди уже даже в обычных конструкциях уже почему-то подразумевают магию…

                                        Я понимаю, что некоторые вещи в js неочевидны, но это столько раз было обсуждено, что это я не знаю, как написать статью про -тся/-ться, хотя по этому поводу даже сайты отдельные есть.

                                        Ну и грабли есть во всех языках. Как-то адепты perl ещё не подтянулись, чтобы показать всю суть магии…

                                        • UFO just landed and posted this here
                                            –1
                                            Может быть я не так вас понял, но вы думаете, что так
                                            var a = [1,2,3][1,2,3];
                                            

                                            можно создавать массивы? И что будет лежать в переменной а?
                                              +2
                                              var a = [1,2,3,4][1,2,3];
                                              console.log( a ); // 4
                                              
                                              // равносильно
                                              var a = [1,2,3,4][(1,2,3)];
                                              
                                              // равносильно
                                              var a = [1,2,3,4][3];
                                              


                                              Естественно, что [1,2,3][3] возвращал undefined — в массиве четвёртого элемент просто нету.
                                          0
                                          Ну вот зачем, например, сравнивать массивы?

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

                                            Одно дело, когда пытаешься разобраться как работает работающий код и совсем другое, когда пишешь код, который вроде бы должен работать, но на некоторых данных ведёт себя не логично (исходя из здравого смысла, а не спецификаций) и точное место фэйла неизвестно. Тут ещё большое значение имеет, что для многих (и проектов, и разработчиков) JavaScript не является основным языком и делаются на нём второстепенные задачи типа предварительной валидации или получение данных с сервера и их визуализации.
                                              0
                                              Ну, разработчикам, для которых JavaScript не основной язык, я бы посоветовал использовать готовые решения, благо их более чем достаточно

                                              Я вот ни разу не читал стандартов полностью и очень редко натыкался на какие-то сильные нелогичности в js, которые прям непонятно-непонятно, почему оно так…

                                              Может быть есть какие-то реальные примеры? Напимер, можно было бы описать «магическую» конструкцию и случай, как на неё наткнулся в реальном коде
                                                0
                                                Вот как раз с готовыми решениями основные проблемы и возникают. Даже если как-то умудришься его найти, то, как правило, оно универсальное, делает больше чем требуется, и на изучение нюансов (по сути на реверс кода) уходит уйма времени.

                                                А вообще я скорее не про «наткнулся», а про «написал». А из наткнувшегося долго, например, стопорили работу конструкции типа if (!~smth1.indexOf(smth2)) — пока разберешься во всех крайних случаях, составишь таблицу истинности, учтешь два неявных преобразования типа и т. п. — быстрее самому написать по ощущениям, что это значит.
                                                  0
                                                  так можно же загуглить) ну, или стараться не использовать у себя такие либы, действия которых сложно понять
                                                    0
                                                    !~, конечно, зло. Обычно такими вещами балуются люди недавно прочитавшие про них и не понимающие, что уменьшать объем кода — это работа минификатора, а человек должен писать читабельный код. Про такие вещи было хорошее обсуждение в комментариях к статье про "|0", к сожалению, не смог её сейчас найти.
                                                  +1
                                                  Одно дело, когда пытаешься разобраться как работает работающий код и совсем другое, когда пишешь код, который вроде бы должен работать, но на некоторых данных ведёт себя не логично (исходя из здравого смысла, а не спецификаций) и точное место фэйла неизвестно.

                                                  Вот от такого меня периодическое решение примеров с wtfjs и спасает. При решении обычных рабочих задач мне в голову не придет читать спецификации. Документацию по новому или редко используемому API — да, но не спецификацию. И у многих думаю похожая ситуация. Решая задачки на неочевидные моменты языка я во-первых, проникаюсь логикой языка, во-вторых, создаю в голове некую «карту граблей». И это позволяет мне в рабочем коде намного быстрее понять причины нелогичного поведения на некоторых данных.

                                                  С разработчиками, которые пишут на JavaScript относясь к нему как к второстепенному языку, вообще печальная картина. Мало того, что в результате получается макаронное месиво на JQuery и шутки про «как мне сложить два числа на JS? используй плагин JQuery», так получившееся творение течет и тормозит. И многие считают это нормальным. Почему-то никому не придет в голову отправить JS-ника «по быстрому накидать валидацию» на C++, т.к. понимают какого качестве в большинстве случаев будет полученный код, а вот джависта отправить костылять на JavaScript — это почему-то нормально.
                                                    +1
                                                    С разработчиками, которые пишут на JavaScript относясь к нему как к второстепенному языку, вообще печальная картина.

                                                    Тут скорее не с разработчиками печальная картина, а с их менеджерами :(
                                                      0
                                                      Полностью согласен.
                                                +9
                                                alert((([][[]]+[])[+!+[]])+((!![]+[])[+!+[]+!+[]+!+[]])+((!![]+[])[+!+[]])+(([][[]]+[])[+!+[]+!+[]])+((![]+[])[+!+[]+!+[]+!+[]]));
                                                  0
                                                  Запустил из консоли. Не понял. Как? о_О
                                                    +3
                                                    jsfuck.com
                                                      +2
                                                      Основная идея в том, что берем какую-то штуку которая возвращает, скажем, undefined, теперь чтоб получить 'n' нужно сделать 'undefined'[1], а чтоб получить единичку играемся с приведениями и пишем +!+[]. Остальные буквы получаем таким же образом и конкатенируем это все. Ну и в конце еще дописываем всякой фигни для того чтоб заклинание было пострашнее.
                                                    +1
                                                    В прошлом году в одном из постов на хабре была лекция по Java. Так вот там у лектора я узнал термин, которым он назвал чрезмерное усложнение кода: Job Security Index. Т.е. пишем код так, чтоб его было чрезвычайно трудно сопровождать/рефакторить кому-либо кроме вас.

                                                    Используйте вышеперечисленные примеры в своём коде — и вас никогда не уволят! (Конечно если вы программист в госучреждении/большом предприятии)
                                                      +1
                                                      К сожалению, существуют намного более простые способы писать неподдерживаемый код обеспечивая себе Job Security.
                                                        0
                                                        Конечно если вы программист в госучреждении/большом предприятии

                                                        Странное уточнение. Как раз большие предприятия могут позволить себе нанять хоть несколько человек, которые будут разбираться в написанном таким специалистом коде и приводить его к человеческому виду.
                                                          0
                                                          Вы видимо не работали на таком предприятии
                                                            0
                                                            Работал. И меня плавно заменили, когда не сошлись с начальством в вопросе о том должен ли программист выполнять обязанности первой линии техподдержки.
                                                              0
                                                              Как так «заменили»?
                                                                0
                                                                Наняли человека, который несколько месяцев читал и переписывал мой код. Потом сказали «спасибо, прощайте».
                                                                  +1
                                                                  Интересно бы узнать — переписывал ли он по стандартам или так чтоб его увольняли дольше Вас?
                                                                    0
                                                                    На мой взгляд — второе :)
                                                                    0
                                                                    > Потом сказали «спасибо, прощайте».

                                                                    Я не знаю в какой стране это было, но в России такое не возможно.
                                                                      0
                                                                      Это ещё почему?
                                                                        0
                                                                        По ТК
                                                                          0
                                                                          По соглашению сторон возможно. Мне описали «прелести» работы по ТК (строгий график, отсутствие заданий или иногда самые легкие (читай — неинтересные) при невозможности заниматься своими делами, голый оклад и т. п.), которые меня ждут, я подумал и согласился. Даже не столько из-за «прелестей», сколько из-за нежелания «работать» (если это можно назвать работой) у людей, которые не хотят, чтобы я у них работал.
                                                          +1
                                                          «Сначала JavaScript пробует преобразовать аргумент к числу с помощью метода valueOf и только когда это не удается приводит к строковому типу»

                                                          Не совсем так ведь. Сначала JavaScript вызывает valueOf, а если его нет, то вызывает toString, но как бы это странно не звучало toString() не обязательно должен преобразовывать в строку. Если вы переопределите метод toString(), то результат может поменяться, поэтому это слегка неточно говорить, что массив преобразуется в строку. Это результат при нормальных условиях, а не правило. А правило именно — вызов метода toString(), когда отсутствует valueOf()

                                                          Вот попробуйте это

                                                          Array.prototype.toString = function(){
                                                          return 42;
                                                          }

                                                          console.log([1, 2, 3] < [1, 2, 4]);
                                                            +1
                                                            Да, есть такое дело. Сам иногда даю на собеседованиях задачки на переопределение прототипа. Например, такую:
                                                            Дан код:
                                                            var rx1 = RegExp(/regular/ + /expression/);
                                                            var rx2 = /regularexpression/;
                                                            

                                                            Что нужно добавить в начало программы, чтобы rx1 и rx2 стали идентичными? Сами строчки трогать нельзя.
                                                              –1
                                                              a new вы специально не пишите? это тоже часть задачки?
                                                                0
                                                                Скрытый текст
                                                                with(rx1=/regularexpression/,{rx1:0})
                                                                var rx1 = RegExp(/regular/ + /expression/);
                                                                var rx2 = /regularexpression/;
                                                                

                                                                Но это, наверное, читерство.
                                                                  0
                                                                  Скрытый текст
                                                                  RegExp.prototype.toString = function() {
                                                                      return this.source;
                                                                  }
                                                                  

                                                                  игра с приведением типов и переопределением метода прототипа


                                                                  собственно в ветке обсуждения описан метод решения
                                                                    0
                                                                    Это понятно. Просто хотел поделиться альтернативным решением.
                                                                0
                                                                Все правильно в статье. Если значение не примитив, будет вызван метод valueOf. Если значение, которое вернет valueOf, не примитив, тогда будет вызван метод toString для исходного обьекта. Так как массив не имеет собственного метода valueOf, он наследует Object.prototype.valueOf, который по умолчанию возвращает ссылку на исходный обьект.

                                                                var arr = [];
                                                                arr.valueOf() === arr; // true
                                                                


                                                                9.1 ToPrimitive
                                                                8.12.8 [[DefaultValue]] (hint)
                                                                +1
                                                                Мне штука с NaN != NaN запомнилась, не помню правда из какой статьи.
                                                                  0
                                                                  Есть только одна вещь, которую я боюсь что никогда не пойму… Не уверен насчет того есть ли это в стандарте. Во всех движках захардкодено.

                                                                  NaN === Infinity //false разумеется
                                                                  а дальше интереснее:
                                                                  NaN == Infinity //false
                                                                  NaN > Infinity //false
                                                                  NaN < Infinity //false

                                                                  Поясните мне, ну как это?!
                                                                    –1
                                                                    Вообще результат undefined, но он приводится к false.

                                                                    P.S. похожая ситуация в SQL с NULL.
                                                                      0
                                                                      NaN === NaN // => false
                                                                      0 === -0    // => true
                                                                      

                                                                      Любое сравнение с NaN возвращает false, так как NaN — не число, и что за ним стоит — неизвестно — +'foo' — NaN, как и +{} — NaN. Так же основные алгоритмы сравнения не видят разницы между +0 и -0, хотя, например, 1 / 0 === Infinity, а 1 / -0 === -Infinity, см. спецификацию.
                                                                      Есть внутренний алгоритм сравнения SameValue, в ECMAScript 6 вынесенный наружу как Object.is. Для этого алгоритма сравнения NaN равен NaN, +0 и -0 различаются, а в остальном результаты соответствуют оператору ===.

                                                                      Object.is(NaN, NaN) // => true
                                                                      Object.is(0, -0)    // => false
                                                                      Object.is(42, 42)   // => true
                                                                      Object.is(42, '42') // => false
                                                                      

                                                                    Only users with full accounts can post comments. Log in, please.