Может ли в JavaScript конструкция (a==1 && a==2 && a==3) оказаться равной true?

https://codeburst.io/javascript-can-a-1-a-2-a-3-ever-evaluate-to-true-aca13ff4462d
  • Перевод
Недавно по Твиттеру и Реддиту гулял интересный кусок кода на JavaScript. Вопрос, связанный с ним, заключался в следующем: «Может ли выражение (a==1 && a==2 && a==3) вернуть true?». Ответ на вопрос, как ни странно, был положительным.

image

Сегодня мы разберём этот код и постараемся его понять.

Вот он:

const a = {
  num: 0,
  valueOf: function() {
    return this.num += 1
  }
};
const equality = (a==1 && a==2 && a==3);
console.log(equality); // true

Если вы используете Google Chrome, откройте консоль инструментов разработчика с помощью комбинации клавиш Ctrl + Shift + J в Windows, или Cmd + Opt + J в macOS. Скопируйте этот код, вставьте в консоль и убедитесь в том, что на выходе и правда получается true.

В чём тут подвох?


На самом деле, ничего удивительного тут нет. Просто этот код использует две базовые концепции JavaScript:

  • Оператор нестрогого равенства.
  • Метод объекта valueOf().

Оператор нестрогого равенства


Обратите внимание на то, что в исследуемом выражении, (a==1 && a==2 && a==3), применяется оператор нестрогого равенства. Это означает, что в ходе вычисления значения этого выражения будет использоваться приведение типов, то есть, с помощью == сравнивать можно значения разных типов. Я уже много об этом писал, поэтому не буду тут вдаваться в подробности. Если вам нужно вспомнить особенности работы операторов сравнения в JS — обратитесь к этому материалу.

Метод valueOf()


В JavaScript имеется встроенный метод для преобразования объекта в примитивное значение: Object.prototype.valueOf(). По умолчанию этот метод возвращает объект, для которого он был вызван.

Создадим объект:

const a = {
  num: 0
}

Как сказано выше, когда мы вызываем valueOf() для объекта a, он просто возвращает сам объект:

a.valueOf();
// {num: 0}

Кроме того, мы можем использовать typeOf() для проверки того, действительно ли valueOf() возвращает объект:

typeof a.valueOf();
// "object"

Пишем свой valueOf()


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

a.valueOf = function() {
  return this.num;
}

Здесь мы заменили стандартный метод valueOf() для объекта a. Теперь при вызове valueOf() возвращается значение a.num.

Всё это ведёт к следующему:

a.valueOf();
// 0

Как видно, теперь valueOf() возвращает 0! Самое главное здесь то, что 0 — это то значение, которое назначено свойству объекта a.num. Мы можем в этом удостовериться, выполнив несколько тестов:

typeof a.valueOf();
// "number"
a.num == a.valueOf()
// true

Теперь поговорим о том, почему это важно.

Операция нестрогого равенства и приведение типов


При вычислении результата операции нестрогого равенства для операндов различных типов JavaScript попытается произвести приведение типов — то есть он сделает попытку привести (конвертировать) операнды к похожим типам или к одному и тому же типу.

В нашем выражении, (a==1 && a==2 && a==3), JavaScript попытается привести объект a к числовому типу перед сравнением его с числом. При выполнении операции приведения типа для объекта JavaScript, в первую очередь, попытается вызвать метод valueOf().

Так как мы изменили стандартный метод valueOf() так, что теперь он возвращает значение a.num, которое является числом, теперь мы можем сделать следующее:

a == 0
// true

Неужто задача решена? Пока нет, но осталось — всего ничего.

Оператор присваивания со сложением


Теперь нам нужен способ систематически увеличивать значение a.num каждый раз, когда вызывается valueOf(). К счастью, в JavaScript есть оператор присваивания со сложением, или оператор добавочного присваивания (+=).

Этот оператор просто добавляет значение правого операнда к переменной, которая находится слева, и присваивает этой переменной полученное значение. Вот простой пример:

let b = 1
console.log(b+=1); // 2
console.log(b+=1); // 3
console.log(b+=1); // 4

Как видите, каждый раз, когда мы используем оператор присваивания со сложением, значение переменной увеличивается! Используем эту идею в нашем методе valueOf():

a.valueOf = function() {
  return this.num += 1;
}

Вместо того чтобы просто возвращать this.num, мы теперь, при каждом вызове valueOf(), будем возвращать значение this.num, увеличенное на 1 и записывать новое значение в this.num.

После того, как в код внесено это изменение, мы наконец можем всё опробовать:

const equality = (a==1 && a==2 && a==3);
console.log(equality); // true

Работает!

Пошаговый разбор


Помните о том, что при использовании оператора нестрогого равенства JS пытается выполнить приведение типов. Наш объект вызывает метод valueOf(), который возвращает a.num += 1, другими словами, возвращает значение a.num, увеличенное на единицу при каждом его вызове. Теперь остаётся лишь сравнить два числа. В нашем случае все сравнения выдадут true.

Возможно, полезно будет рассмотреть происходящее пошагово:

a                     == 1   -> 
a.valueOf()           == 1   -> 
a.num += 1            == 1   -> 
0     += 1            == 1   ->
1                     == 1   -> true
a                     == 2   -> 
a.valueOf()           == 2   -> 
a.num += 1            == 2   -> 
1     += 1            == 2   ->
2                     == 2   -> true
a                     == 3   -> 
a.valueOf()           == 3   -> 
a.num += 1            == 3   -> 
2     += 1            == 3   ->
3                     == 3   -> true

Итоги


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

Уважаемые читатели! Если вы знаете о каких-нибудь курьёзах из области JavaScript — просим ими поделиться.

RUVDS.com 599,98
RUVDS – хостинг VDS/VPS серверов
Поделиться публикацией
Похожие публикации
Комментарии 95
  • +10
    stackoverflow.com/questions/48270127/can-a-1-a-2-a-3-ever-evaluate-to-true тут больше вариантов добиться такого поведения.
    • +31
      Может ли в JavaScript конструкция (a==1 && a==2 && a==3) оказаться равной true?

      Чего тут думать, в джаваскрипте любая конструкция с участием == может оказаться равной чему угодно :)

      • +21

        Я бы сказал, в JS почти любая конструкция может дать почти любой результат :)

      • +1
        Из комментария выше можно выделить два метода: с .valueOf и совсем отмороженный с with. Так вот второй работает и с ===:
        with({
            _a: 0,
            get a() {
                return ++this._a;
            }
        }) {
            console.log(a === 1 && a === 2 && a === 3); // true
        }
        
      • +10
        Почему в заголовке фигурирует только JavaScript? Такого же поведения можно добиться и в других языках. Вопрос только какого типа 'a'?
        В C++ это, например, можно сделать так
        #include <iostream>
        
        class A {
            int _a;
        public:
            A():_a(0) {};
            ~A() {};
            
            bool operator ==(int i)
            {
                return ++_a == i;
            }
        };
        
        int main(int argc, const char * argv[]) {
            
            A a;
            if (a == 1 && a == 2 && a == 3) {
                std::cout << "Hello, World!\n";
            }
            return 0;
        }

        • +23

          В С++ все еще проще:


          struct {
              bool operator ==(int i)
              {
                  return true;
              }
          } a;
          • +3
            Так по красивее :)
            #include <iostream>
            
            #include "happy_debugging_lol.h"
            
            auto main() -> decltype(0)
            {
                int a = 1;
            
                if (a == 1 && a == 2 && a == 3)
                    std::cout << "WTF? Why am I seeing this?" << std::endl;
            }
            

            Не верите, что это работает? Вот содержимое файла happy_debugging_lol.h
            #pragma once
            
            class FakeInt {
            public:
                FakeInt(int value)
                    : value(value)
                {
                }
                operator int() const
                {
                    return value;
                }
                template <typename T>
                bool operator==(const T& other) const
                {
                    return true;
                }
            
            private:
                int value;
            };
            #define int FakeInt
            
            
            • 0
              operator int() const

              — вроде лишнее? Нет же в коде приведения к типу int.
              • 0
                Ну это я так на всякий случай написал, чтоб он был более похож на int по интерфейсу, конечно ещё надо реализовать операторы сложения, умножения и т.д.
              • +1

                а чем объявление


                auto main() -> decltype(0)

                красивее


                int main()

                ???

                • +3
                  Тем, что int переопределен, а decltype(0) — нет :-)
                  • 0
                    Оно не красивее, просто в заголовочном файле стоит #define int FakeInt и препроцессор заменяет все вхождения int в том числе и int main() меняется на FakeInt main(), поэтому я сделал так, можно было ещё поменять на int32_t, например. Хотя сейчас посмотрел, можно было даже так
                    decltype(0) main()

                    Или так:
                    Int main()

                    и в заголовочном файле перед define добавить:
                    using Int = int;
                    • 0
                      Да можно даже signed main написать! Но в любом случае отличие от «стандартной» формы должно насторожить…
              • +4
                А смысл этого примера? В нормальном коде этого всё равно делать никто не будет. Подобную хрень можно практически на любом языке написать, на C++ можно перегрузить оператор сложения, например.
                • –2

                  Всё-же нарваться на такой пример неприятно:


                  var aᅠ = 1;
                  var a = 2;
                  var ᅠa = 3;
                  if(aᅠ==1 && a== 2 &&ᅠa==3) {
                      console.log("Why hello there!")
                  }
                  • +2
                    Ни один разработчик(если он не троллит тех, кто после него будет смотреть этот код) не напишет такой херни. Поэтому вероятность наткнуться на такое не высока.
                    • 0

                      Да с пол-пинка. Копипаст откуда-нибудь. Никогда не встречали в интернет-формах банков "не вводите номер копипастом заполняйте руками"? Я такое встречаю регулярно и часто так и получается — копипаст почему-то переносит неправильно.

                    • 0
                      Объясните пожалуйста данный пример, почему так? Спасибо.
                    • 0
                      Это работает только за счет того как браузер отображает символы такие, норм редактор кода или даже консоль девтулз всё показывает.
                  • +14
                    На всех собеседованиях этой страны :)
                    • +4
                      Достаточно баянистый, но тем не менее любопытный пример:
                      '5'-3        // выводит 2
                      '5'+3       // выводит "53"     
                      '5'+-3      // выводит "5-3"
                      

                      • +12
                        Мой любимый пример, когда я начинаю объяснять, почему мне не нравится JS (и вообще слабая типизация). Это, возможно, лично мое мнение, но python (как пример динамической типизации) со своей сильной типизацией, и, как следствие, невозможностью выполнения такого кода — менее подвержен ошибкам.
                        • –1

                          В последнее время даже не пытаюсь объяснить, просто говорю js sucks

                          • 0
                            И часто вы вычитаете число из строки?
                            Нет, ну правда. За годы, что я пишу на js, у меня сформировалась в голове ide, которая следит за всеми типами и предупреждает, если я складываю или вычитаю что-то, что может быть строкой.
                            А писать явное преобразование в Number / String везде, где может быть ошибка, вам вообще ничего не мешает. Как вы делаете это на других языках. Разве что тут вы это можете проигнорировать, а там — нет.
                            • 0
                              (В профиле написано, «можно и нужно обращаться на ты»)
                              Ты привязываешься к конкретному примеру? Я в целом говорю, что это возможно (как и к массиву прибавить интовую 1, и внезапно получить строковую «1»), и считаю это неправильным. Если программист действительно хочет к строке «прибавить» число, тогда он должен явно указать, он хочет получить сумму чисел или произвести конкатенацию строк (т.е. один операнд привести к типу второго).

                              А по поводу часто или нет. Нет, ну серьезно, скорее всего такое нечасто встречается в отполированном коде. Но в момент, когда идет действительно очень активная работа с кодом, когда над тобой давлеет дедлайн — можно по ошибке что-то не туда скопипастить, либо просто не туда вписать. Программист — не машина, иногда он теряет концентрацию и может допустить глупую ошибку. Но JS в этом случае не помогает говоря: "парень, вот тут ты уточни, ты действительно так хотел написать?". Он просто проходит некорректный участок кода, как будто так и надо, говоря: "это абсолютно не мои проблемы, я просто к пустому массиву прибавлю число, и верну строку.".
                              • +4
                                И часто вы вычитаете число из строки?

                                Да регулярно, когда идет работа с пользовательским вводом


                                const a = 0.1;
                                const x = document.querySelector('input[name="x"]').value;
                                const y = document.querySelector('input[name="y"]').value;
                                
                                console.log(x - a * y)

                                a*y скастуется в число, а x останется строкой.


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

                                • +1

                                  Ну и сами вы себе злой буратино. Зная, что в JS такое поведение, и что input value — это всегда строка, кто мешает вам, опять же зная, что вы будете производить мат. операции над этим инпутом, самому сконвертировать.


                                  const a = 0.1;
                                  const x = +document.querySelector('input[name="x"]').value; // Вот теперь это число
                                  const y = +document.querySelector('input[name="y"]').value; // И это тоже
                                  
                                  console.log(x - a * y)

                                  Хотя parseInt(n, 10) все-же более наглядно и конвертирует исключительно в десятичное число (т.к. +'0xFF' === 255).


                                  По-хорошему, нужно x и y сконвертировать в число в момент присвоения

                                  С чего бы это? Инпут хранит исключительно значение как строку, а если вы про input[type="number"] — то это не имеет никакого отношения к JS, это лишь валидация пользовательского инпута, как и type="email", а в JS это приходит как обычная DOMString.

                                  • +1

                                    И нет, "a" — не останется строкой, т.к. "-" тоже кастует в Number

                                    • 0

                                      (простите, опечатался, не "a", а конечно же "x" станет числом тоже).


                                      И в целом то, это проблема не JS, а вообще динамических языков, и языков с хоть чуть-чуть возможностью переопределять операторы или (и) неявно приводить типы.


                                      # Python
                                      >>> "5" * 10 + "8"
                                      '55555555558'

                                      Т.е. тут вопрос скорее к невнимательности. И тут TS или Flow, бы решили эту проблему (ну или попытались :) ).

                                      • 0

                                        Справедливости ради, конечно же не все динамические языки одновременно и слабо типизированы, но надеюсь вы поняли, о чем я.

                                        • 0
                                          Кстати, хорошо что вы Python упомянули. Дело в том, что этот язык — с сильной типизацией, а не со слабой как javascript. И все равно в нем, как видно, можно забыть о типах и насчитать чепуху.

                                          Более того, аналогичную ерунду можно получить на любом языке с возможностью перегрузки операторов — даже на статически типизированных C++ или C# :-)
                                          • 0
                                            При определённой сноровке можно чепуху нагородить в любом языке. Другое дело, что язык с сильной типизацией, в данном примере, выдаст ошибку в рантайме, а со статической — вообще при компиляции. Правда, стоит упомянуть, что
                                            var a = "5"+1;

                                            тоже корректный код в C#, но только с оператором "+" и только если один из операндов — строка. Вот так уже нельзя:
                                            int a = "5" + 1;

                                            Т.е. всё это дело приводится к строке (toString()). Даже так можно написать:
                                            var a = "5"+new Object();

                                            Но это, как я полагаю, сделано для читабельного кода при конкатенации строк с «нестроками».
                                      • 0

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

                                        • 0

                                          Вы абсолютно правы, что язык должен в первую очередь быть инструментом, а не целью. Но в большинстве языков: Python — input(), C — getchar(), Ruby — gets, инпут — это строка.


                                          Так и в JS, input Dom node — содержит исключительно строки.

                                          • 0

                                            Именно поэтому я бы перевел код на Typescript (рабочий пример).


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

                                            • 0

                                              Тут трудно не согласиться (я об это тоже сказал чуть выше). Но мы все-же говорим в контексте JS.


                                              Собственно, надеюсь, я правильно понял вашу позицию. TS действительно крутой :).

                                              • 0

                                                Тем не менее, document.getElementById('layer1').style.opacity += 0.01 из обсуждения ниже в Typescript успешно скомпилируется и сделает ерунду. Более того, аналогичный глупый код скорее всего скомпилируется даже на C# или Java, а может даже на C++ (тут уже смотря как библиотека будет сделана).

                                        • +1
                                          Хотя parseInt(n, 10) все-же более наглядно.

                                          Мне больше всего нравится Number(n)
                                    • 0
                                      Согласен. Правда в Python есть свои странности :)
                                  • +2
                                    На C# свойства тоже позволяют так делать:

                                            private static int _a = 0;
                                            private static int a { get => ++_a; }
                                    
                                            static void Main(string[] args)
                                            {
                                                if (a == 1 && a == 2 && a == 3)
                                                {
                                                    Console.WriteLine("True");
                                                }
                                                Console.Read();
                                            }
                                    

                                    • 0

                                      Только все-таки лучше писать private static int a => ++_a;

                                      • +3
                                        лучше вообще такое не писать никогда
                                        • +1
                                          alkozko, я с Вами полностью согласен. Как и с комментариемdevalone:
                                          А смысл этого примера? В нормальном коде этого всё равно делать никто не будет. Подобную хрень можно практически на любом языке написать, на C++ можно перегрузить оператор сложения, например.

                                          Просто хотел подчеркнуть, что тема актуальна не только для JS. И уж точно не является руководством к действию.
                                          • 0
                                            Почему столь категорично? У нас есть что-то похожее в тестовом DSL как заменитель автоикремента в БД.
                                            • +3

                                              Нарушается семантика свойства. Свойство — это "эмулятор поля", предполагается что его значение не должно меняться просто от факта обращения к нему.


                                              Вот метод вида int a() => ++_a; — это нормально. А подобное свойство — нет.

                                        • +3
                                          Есть способ гораздо проще и стабильнее:

                                          class MyClass
                                          {
                                              public static bool operator ==(MyClass left, int right) => true;
                                              public static bool operator !=(MyClass left, int right) => true;
                                          }
                                          
                                          var a = new MyClass();
                                          if(a == 1 && a == 2 && a == 3)
                                              Console.WriteLine("Equals");
                                          
                                        • +12
                                          #include <stdio.h>
                                          
                                          #define a (__COUNTER__+1)
                                          
                                          int main(void) {
                                              if (a == 1 && a == 2 && a == 3) printf("123123123");
                                              return 0;
                                          }
                                          • +3

                                            Не совсем понимаю за что вас минусуют. Если вы не против, я добавлю комментарии к вашему коду, что-бы читателям стало понятно.


                                            Для начала, это абсолютно валидный код с точки зрения C.


                                            #define директива — это по-сути замена во время компиляции найденого токена (или вызова) на его значени. В случае выше, любая найденная a будет заменена на (__COUNTER__+1)


                                            __COUNTER__ — это "магическое" макро значение, как и __FILE__, __LINE__, или __FUNCTION_. И в C / C++ эти значения опять же заменяются на этапе компиляции (см. выше). Сам же __COUNTER__ "магичен" из-за того, что при каждом новом вызове, он будет инкриментировать свое значение (от 0).
                                            В итоге выражение (__COUNTER__+1) станет (0+1), затем (1+1), затем (2+1).


                                            И весь код будет выглядеть так:


                                            // Т.к. `include` тоже макрос, тут будет содержимое этого файла.
                                            
                                            int main(void) {
                                                if ((0+1) == 1 && (1+1) == 2 && (2+1) == 3) printf("123123123");
                                                return 0;
                                            }

                                            А дальше все еще круче, если кому интересно, компилятор просто схлопнет (оптимизирует) константные выражения, и на выходе останется:


                                            // Т.к. `include` тоже макрос, тут будет содержимое этого файла.
                                            
                                            int main(void) {
                                                printf("123123123");
                                                return 0;
                                            }

                                            Так-что не спешите кидаться на zawodskoj

                                            • +4
                                              А заминусовали меня скорее всего потому, что я скопипастил этот код из своего ideone, который содержал слово, которое даже матом не считается, и не успел быстро отредактировать.
                                              Первый минус проставил тот, кто увидел, а второй кто-то «НУ ПОТОМУ ЧТО ТУТ МИНУС СТОИТ, ЗНАЧИТ ПЛОХОЙ КОММЕНТ, НАДО ТОЖЕ МИНУСНУТЬ»
                                              Вот и пиши после этого комменты вообще
                                              • 0
                                                Если бы все было как вы написали — то и двух плюсов вы бы не получили.

                                                Все проще. Два человека увидели версию с матом и поставили минус. Не забывайте, каждый видит ту версию комментария которая была на момент последнего обновления комментариев или загрузки страницы.
                                              • 0
                                                Забей, я как-то написал, что правильно не «Силиконовая долина», а «Кремниевая долина» и меня заминусили(самые обиженные даже в карму пошли минусовать).
                                                • –3
                                                  Мне на гиктаймсе на 10 пунктов слили кармы за то, что я вместо «последняя книга автора» написал «крайняя книга автора», а потом на комментарий с претензией ответил, что считаю этот вариант вполне допустимым и он мне больше нравится.
                                                  • –1
                                                    Это какая-то местная фишка, минусовать)) Плюсовать в карму никто не идет почему-то ))
                                                    • 0
                                                      А нельзя плюсовать выше +4 если нет публикаций.
                                                  • –4
                                                    Я ему минус за мат ставил, сейчас мата в комментарии уже нет.
                                                  • 0
                                                    __COUNTER__, несмотря на поддержку многими компиляторами, не входит в стандарт языка (к сожалению).
                                                    • 0

                                                      Можно даже сразу:


                                                      #include <stdio.h>
                                                      
                                                      #define if(cond)
                                                      
                                                      int main(void) {
                                                          if (a == 1 && a == 2 && a == 3)
                                                              printf("Yes");
                                                          return 0;
                                                      }
                                                    • 0
                                                      Извините за очень нескромный вопрос, но во всех ли интерпретаторах проверка будет идти слева на право?
                                                      • 0

                                                        Если интерпретатор написан по-стандарту (и здравому смыслу), && будут идти слева-направо. V8 / SpiderMonkey / Chakra работают именно так

                                                        • 0
                                                          здравый смысл не нужен, приоритет операторов и ассоциативность. developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Operators/Operator_Precedence что интересно — в стандарте ecma не нашел ни ассоциативности ни приоритетов.
                                                        • +1
                                                          «В JS нельзя true = false, поэтому если хотите отомстить коллегам — вот пара идей..»
                                                          • 0
                                                            можно просто
                                                            b = 0;
                                                            Object.defineProperty(window, 'a', {
                                                            set: (v) => {

                                                            },
                                                            get: () => {
                                                            return b++;
                                                            }
                                                            });
                                                            • 0
                                                              Или даже так:

                                                              const a = {
                                                                num: 0,
                                                                [Symbol.toPrimitive]: function() {
                                                                  return ++this.num;
                                                                }
                                                              };
                                                              
                                                            • 0
                                                              Я думаю так можно на любом языке, например на C#

                                                              class A
                                                                  {
                                                                      private int a = 0;
                                                              
                                                                      public static implicit operator Int32(A o)
                                                                      {
                                                                          o.a++;
                                                                          return o.a;
                                                                      }
                                                                  }
                                                              
                                                                  class Program
                                                                  {
                                                                      static void Main(string[] args)
                                                                      {
                                                                          var a = new A();
                                                                          if (a == 1 && a == 2 && a == 3)
                                                                          {
                                                                              Console.WriteLine("Неожиданный поворот");
                                                                          }
                                                                          Console.WriteLine("Hello World!");
                                                                      }
                                                                  }
                                                              • +2

                                                                Зашел увидеть эту ссылку — и не нашел. Пусть тут полежит:


                                                                Return true to win


                                                                Предупреждение: not safe for work! Не в том смысле, а просто работа может остановиться :-)

                                                                • +1
                                                                  Ну и php, куда же без него.

                                                                  $a = true;
                                                                  if ($a == 1 && $a = 2 && $a = 3 ) {
                                                                      echo 'I am here';
                                                                  }
                                                                  • +1
                                                                    У вас в условии два присваивания. Но да, если бы вместо них были сравнения, выражение по-прежнему было бы истинно.
                                                                  • –7
                                                                    Не покидает ощущение, что JavaScript был когда-то осквернен женской логикой… Ведет он себя совсем не по машинному… Еще чуть-чуть и станет выдавать ошибки наподобии «ой, всее...»
                                                                    • +3

                                                                      Причем здесь Javascript?


                                                                      Выше в комментариях смогли реализовать такой же пример на С++ и C#. Если этого мало, вот тут еще есть варианты на Ruby, Python и Java.

                                                                      • –4
                                                                        Тема поста у нас JS. Это мое ощущение языка, как дилетанта. Конкретный пример тут не при чем. Я пробовал писать простенькие вещи в проядке ознакомления на некоторых языках и только JS пока мне показался таким туманным, чтоли, со своими типами. Очевидные вещи не хотели работать, примерно: а = 2 + 2; иф (а == 4) зен не выполнялось, так как а == 4,0000000001. Я даже на форумах знатаков спрашивал что-то элементарное, почему не работает, а мне не верили, говорили должно работать и все.
                                                                        • +3
                                                                          а = 2 + 2; иф (а == 4) зен не выполнялось, так как а == 4,0000000001

                                                                          А вам это не приснилось? В JS обыкновенные double числа. Никакой магии. Пока вы работаете с целой частью — проблем нет. Начинаете работать с дробной — привет степени двойки и прочие радости double. В принципе, всё как и везде.


                                                                          According to the ECMAScript standard, there is only one number type: the double-precision 64-bit binary format IEEE 754 value (numbers between -(253 -1) and 253 -1)
                                                                          • –1
                                                                            Может, я как-то там работал с таймерами чтоли, может и перешел незаметно к дабл типу, ну это ладно, еще можно понять. Раз знатоков много, воспользовался случаем и отрыл свой старый проблемный код, я так и не понял почему он у меня не работал.

                                                                            var layer_int;
                                                                            function layer_anim() {
                                                                            document.getElementById('layer1').style.opacity -= 0.01;
                                                                            document.getElementById('layer2').style.opacity += 0.01;
                                                                            layer_int = setTimeout('layer_anim()', 20);
                                                                            }

                                                                            Работает он так, что первый слой пропадает, значение падает с 1, а второй слой появляется, с нуля лишь один раз, и остается на уровне 0.01. Как такое может быть, что это за выборочное исполнение команд? Удалось решить как-то, путем замены += на более развернутую форму. Ну что это как не женский каприз (надеюсь никого не обидеть)?
                                                                            • +1
                                                                              document.getElementById('layer1').style.opacity
                                                                              // === ''

                                                                              Пустая строка. Неопределённое значение. Так? Складывать строки с числами не самая умная затея. Ок, допустим вы туда принудительно вбили '1'.


                                                                              document.getElementById('layer1').style.opacity += 0.01
                                                                              "0.990.01"

                                                                              Сложили сладкое с горячим — получили ерунду.


                                                                              JavaScript это язык со слабой динамической типизацией. Это говорит о том, что типы приводятся к друг другу в ряде случаев. В том числе и строки с числами. Отняв от строки '1' число 0.01 вы получите 0.99. А вот добавив — ту пургу выше. Нравится это или нет, но это так. Подобным образом работают все языки со слабой динамической типизацией. Но, скажем, в PHP для "складывания строк" (конкатенации) предусмотрен оператор .
                                                                              А в JS же используется +. Отсюда и ваши проблемы.


                                                                              Я даже на форумах знатаков спрашивал что-то элементарное, почему не работает, а мне не верили, говорили должно работать и все.

                                                                              Видимо такие знатаки были.

                                                                              • 0
                                                                                Благодарю ) Поумнел. Видимо тогда казалось очевидным, что раз от 0 до 1, то должно быть число.
                                                                                • +1

                                                                                  Все CSS-поля строки. А style в DOM API это проекция на язык CSS из JS-окружения.

                                                                                  • –1
                                                                                    Поставил бы вам «лайк» если бы мог ) Спасибо
                                                                              • 0

                                                                                Дык style.opacity — это ж строка...

                                                                                • +1
                                                                                  Ну что это как не женский каприз (надеюсь никого не обидеть)?
                                                                                  Это слабая типизация. Вообще все языки делятся на два класса:
                                                                                  — Ну что ж он такой тупой? (языки с сильной типизацией)
                                                                                  — Ну что это за женские капризы? (языки со слабой типизацией)
                                                                                  На самом деле удобнее и проще работать с первым типом, но новички неизменно выбирают второй. Просто потому что это у человека с опытом реакция на всякие сообдения типа cannot concatenate 'str' and 'int' objects это «ну что ж он такой тупой», а новичка реакция «и что ж мне теперь с этим делать?»

                                                                                  А что «женские капризы» появляются… ну так перепишем += на более развёрнутую форму — авось пропадут. Думать и разбираться для того, чтобы что-то кое-как работающее сотворить не требуется.

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

                                                                                  Так что языки «с женской логикой» никуда не денутся пока количество программистов растёт в геометрической прогрессии…
                                                                        • 0
                                                                          Скрытый текст
                                                                          image
                                                                          • –3
                                                                            this is js magic…
                                                                            • –1
                                                                              Заинтересовал заголовок, залез под кат чтобы понять причину.
                                                                              После того как увидел функцию ValueOf вспомнил старый баян =)
                                                                              старый баян
                                                                              <******> к вопросу о вчерашних скриптостраданиях. Только что кодер знакомый прислал, нашёл в коде программы, написанной уволенным коллегой незадолго до ухода:
                                                                              <******> #define TRUE FALSE //счастливой отладки суки
                                                                              * ****** такого извращённого юмора ещё не встречал
                                                                              • +1

                                                                                Хорошо иллюстрирует общую проблему всех динамических языков. Если совершенно корректную функцию вызвать с параметрами не тех типов, для которых она была написана, то на выходе можно получить произвольное значение произвольного типа. Которое потом будет сохранено в переменную, передано в другие функции… В итоге получается каскад ошибок, в котором концов не найдешь.


                                                                                Сишникам такое неопределенное поведение и не снилось.

                                                                                • 0

                                                                                  Для того чтобы этого не происходило достаточно делать строгое сравнение с учетом типа.

                                                                                  • +3
                                                                                    Э, нет. С точки зрения сишников, это всего лишь unspecified behavior, а не undefined. И концы в этом каскаде ошибок при некоторой сноровке найти можно.

                                                                                    Undefined Behavior — намного хуже, поскольку с момента UB программа перестает поддаваться логике: у условных операторов может быть исполнено обе ветви или ни одной, бесконечный цикл завершается не начавшись и т.п.
                                                                                  • 0
                                                                                    В PHP тоже можно:

                                                                                    class A {
                                                                                        private $val = 0;
                                                                                    
                                                                                        public function __toString()
                                                                                        {
                                                                                            $val = $this->val++;
                                                                                            return (string) $val;
                                                                                        }
                                                                                    }
                                                                                    
                                                                                    $a = new A();
                                                                                    
                                                                                    if ($a == '0' && $a == '1' && $a == '2') {
                                                                                        echo 'Its true!';
                                                                                    }
                                                                                    • 0

                                                                                      Имхо проще $a присвоить функцию, и при каждом вызове функция будет возвращать числа. И не надо в таком случае закатывать 1 в кавычки.

                                                                                      • 0
                                                                                        Не совсем понял. Как вызывать функцию без ()? И как в функции сохранять предыдущее значение?
                                                                                    • –1
                                                                                      Можно ещё и так ногу отстрелить:

                                                                                      int a;

                                                                                      bool func1(int b) {
                                                                                      a++;

                                                                                      return b == b;
                                                                                      }

                                                                                      a = NO_ERROR;

                                                                                      if ((func(a) && a==NO_ERROR))
                                                                                      {
                                                                                      #code never executed…
                                                                                      }

                                                                                      А всё почему? А потому что вызов функций который приводят к смене значений переменных внутри if-ов прямой путь к ошибкам.
                                                                                      Так же такое приводит к различным весёлым ошибкам при создании/сериализации в ООП

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

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