5 популярных JavaScript-хаков

Существует несколько JavaScript-хаков, которыми постоянно пользуются опытные программисты. Они не совсем очевидны, особенно для новичков. Эти хаки используют возможности языка, имеющие некоторые побочные эффекты. В этой статье я объясню, как работают 5 таких распространённых хаков.

Использование оператора !! для конвертации в логическое значение


Всё в JavaScript может быть интерпретировано как истинное или ложное. Это означает, что если вы поместите объект в условный оператор if, то он выполнит либо true-ветку кода (когда объект имеет значение true), либо выполнит false-ветку (соответственно, когда объект имеет значение false).

0, false, "", null, undefined, NaN — это ложные значения. Все остальные значения возвращают true. Иногда вам может потребоваться конвертировать переменную в логическое значение. Это можно сделать с помощью оператора !!:

var something = 'variable';
!!something // returns true


С другой стороны, вместо if (x == "test") можно просто написать if (x). Если же x будет пустой переменной, то просто выполнится код из блока else.

Конвертация строки в число с помощью оператора +


В JavaScript + — это унарный оператор, который возвращает числовое представление операнда или NaN, если операнд не имеет такового. Например, с помощью этого оператора можно проверить, является ли переменная x числом (такой код можно увидеть в библиотеке underscore): x === +x.

Такой способ не очевиден. Скорее всего, вы бы применили методы parseFloat и parseInt.

Определение значения по умолчанию с оператором ||


В JavaScript || является примером выполнения короткого замыкания. Этот оператор сперва анализирует выражение слева от него, и, если оно ложно, анализирует выражение справа. В любом случае, он возвращает первое истинное выражение. Рассмотрим следующий пример:

function setAge(age) {
  this.age = age || 10 
}

setAge();


В этом примере мы вызываем функцию setAge() без аргументов, таким образом age || 10 вернет 10 (!!age == false). Такой способ весьма хорош для того, чтобы задавать значения переменных по умолчанию. На самом деле, такой подход эквивалентен следующему примеру:

var x;
if (age) {
   this.age = age;
} else {
   this.age = 10;
}


Первый пример с оператором || более лаконичен, поэтому именно такой способ используется во всем мире.

Лично я часто использую этот способ. Мне нравится его лаконичность и простота. Однако стоит заметить, что с таким способом вам не удастся задать переменной значение 0, так как 0 является ложным выражением. Поэтому я советую при необходимости использовать такой способ:

this.age = (typeof age !== "undefined") ? age : 10;


Использование void 0 вместо undefined


Ключевое слово void принимает один аргумент и всегда возвращает undefined. Почему просто не использовать undefined? Потому что в некоторых браузерах undefined — это просто переменная, которая может быть переопределена. Поэтому void 0 даёт нам больше уверенности в том, что ничего не будет случайно сломано. Хотя вы можете найти этот хак в исходниках многих библиотек, я бы не рекомендовал использовать его регулярно, так как все ES5-совместимые браузеры не позволяют перезаписывать значение undefined.

Инкапсуляция с помощью паттерна (function() {...})()


В ES5 есть только 2 типа областей видимости: глобальная область видимости и область видимости функции. Всё, что вы пишите, принадлежит к глобальной области, которая доступна из любого места кода. Она включает в себя объявление переменных и функций. Однако, что если вы захотите инкапсулировать большинство кода, а в глобальной области видимости оставить только интерфейс? Тогда вам следует использовать анонимную функцию. Рассмотрим следующий пример:

(function() {
  function div(a, b) {
    return a / b;
  }

  function divBy5(x) {
    return div(x, 5);
  }

  window.divBy5 = divBy5;
})()

div // => undefined
divBy5(10); // => 2


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

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

Некоторые из хаков, рассмотренных в статье, могут быть решены элегантнее с помощью ES6 (следующая версия JavaScript). Например, в ES6 вместо age = age || 10 можно написать следующее:

function(age = 10) {
    // ...
}


Другой пример — паттерн (function() {...})(), который вы уже вряд ли станете использовать после того, как модули ES6 станут поддерживаться современными браузерами.

Дополнительные материалы


Если вы хотите погрузиться в тему JS-хаков ещё глубже, вам могут пригодиться следующие ресурсы:


Оригинал статьи: JavaScript hacks explained
Автор статьи: Yanis T
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

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

    –11
    typeof age !== "undefined" плохой вариант. Лучше использовать сравнение непосредственно с undefined
    age !== undefined

    Но надо учитывать, что, теоретически, кто-то где-то в коде может определить глобальную переменную undefined и всё сломается.
    На такой случай (ну и вообще) удобно использовать «хак» с инкапсуляцией:
    (function(window, $, undefined){
    // ваш код
    // window и jQuery – просто для примера
    // главное, чтоб на вход всегда ничего не передавалось для переменной undefined
    }(window, jQuery))
    
      +5
      Мне кажется стоит тогда уж с void 0 сравнивать.

      age !== void 0
      
      • НЛО прилетело и опубликовало эту надпись здесь
          +3
          Удивительно что заминусовали толковый комментарий. Действительно, проверка на неравенство null гарантирует, что программа не выпадет с исключением при попытке получить какое-нибудь свойство переменной. Это очень удобное сравнение.
          • НЛО прилетело и опубликовало эту надпись здесь
        +5
        В статье кстати сказано про undefined и создание одноименной переменной — как вариант, предлагается использовать void 0.
          +4
          typeof age !== «undefined» плохой вариант. Лучше использовать сравнение непосредственно с undefined
          age !== undefined


          А не наоборот?

          (function() {
              var undefined = 1;
              console.log(undefined); // 1
              console.log(typeof undefined); // number
              console.log(typeof a); // undefined
          })();
          
            +11
            И словить ошибку, если переменная age не определена ни в одной из областей видимости. Отлично решение! Вам надо писать больше статей для новичков!
              –2
              Способов выстрелить себе в ногу в JS огромное количество. Для защиты от необъявленных переменных я бы посоветовал, в статье для новичков, использовать линтер.
                +12
                Почему минусуют человека? Все правильно сказано же — дереференс необъявленной переменной, в коде типа age !== undefined, выбьет ReferenceError: age is not defined же. typeof age !== "undefined" лишен этого недостатка.

                При проверке же свойств имеет смысл проверять напрямую — foo.bar != null, потому что свойства ReferenceError не выбрасывают.
                  0
                  Я бы сказал, что это не недостаток, а преимущество. Т.к. если переменная не определена, то ваш кусок кода ничего не делает и его нужно убрать.
                +4
                А вы можете себе представить человека в здравом уме, который переопределил undefined, и требует чтобы код продолжал работать?

                Кстати, в глобале ещё полно глобальных переменных, почему от их переопределения никто не огораживается? Развлечения ради можете вбить в REPL node.js «Math = 0» и посмотреть, как его раскосячит.

                Совсем другое дело — оптимизация на скорость. Вроде как, используя void 0, можно избежать долгого разрешения переменной undefined из глобала через всю цепочку скопов. Но в последнем хроме void 0 работает даже чуть медленнее.
                  +1
                  >> можно избежать долгого разрешения переменной undefined из глобала через всю цепочку скопов

                  а это точно происходит? Разве движки это не оптимизируют?
                    0
                    Дык, ссылочка на jsperf в конце коммента как раз и говорит о том, что использование void 0 вместо undefined в качестве микро-оптимизации зачастую абсолютно бесполезно.
                +4
                Паттерн (function() {...})() стоило сразу же назвать IIFE. Про "||" написано, а про "&&" почему то нет.
                  0
                  Вы про такую конструкцию?:
                  (function(){
                    var sad = true;
                    function makeMeHappy() {
                      console.log('I\'m happy now!');
                      return false;
                    }
                    sad = sad && makeMeHappy();
                  })();
                  
                    +8
                    Я думаю имелось в виду проверка на != null. Что то типа a && a.b && a.b.c && a.b.c().
                  +7
                  Есть ещё один удобный (ИМХО) хак, который я всегда использую вместо оператора switch:
                  var options = {
                    case1: function () { ... },
                    case2: function () { ... },
                    ...,
                    'default': function () { ... }
                  };
                  options[value||'default']();

                  Вместо функций можно непосредственные значения указывать, зависит от ситуации.
                    +6
                    Это не хак, а простая реализация паттерна «стратегия».
                      +4
                      Использовал терминологию статьи. :)
                      +1
                      Это паттерн-матчинг, разумная замена конструкции switch. Например в питоне этой конструкции вообще нет, вместо нее делают паттерн-матчинг. А в эрланге — так вообще нормальная практика, никаких ветвлений не нужно.
                        +1
                        Совсем недавно оно в JS ещё и работало намного быстрее, чем switch. В новых версиях браузеров уже оптимизировали.
                          +2
                          Без разрушающего присваивания (или разрушающего связывания) от паттерн-матчинга остается одно название.
                          +3
                          Тогда уж лучше (options[value] || options['default'])() использовать.

                          А не то options[value||'default']() дурно ведёт себя, когда value пусть не == false, но и имени ни одного из свойств options не соответствует.
                            +3
                            От value === «toString» все равно не застрахуешься…
                              0
                              Object.create(null)
                                0
                                Это убивает всю прелесть использования литерала объекта. Проще уж hasOwnProperty использовать, пусть это и далеко не так красиво выглядит.
                                  0
                                  Согласен. И это минус языка. На IE11+ можно уже использовать {__proto__: null, ...}, но всё равно не то.
                              +1
                              Да, согласен, поторопился.
                            +6
                            А как же хак «вертикальная черта — ноль»? Который позволяет получить целую часть от числа (или конвертировать строку в число и вытащить целую часть):

                            "8.97"|0; // 8
                            8.97|0; // 8
                            "8.01"|0; // 8
                            "a801"|0; // 0
                            "801a"|0; //0
                            
                              +1
                              Ещё можно парсить числа так:
                              x *= 1;
                              
                                +3
                                Способ веселый, но на очень больших числах начинает врать.
                                  0
                                  Обычное переполнение интеджера, уходит в отрицательное.
                                +8
                                мой любимый хак:
                                if (~arr.indexOf('val')) {
                                    // Элемент в массиве есть
                                }
                                
                                  0
                                  Ещё говорят, что это тоже хак:
                                  var arr = [1,2,3,4];
                                  arr.length = 0; // очищает массив
                                  
                                    0
                                    сомнительный хак.
                                    var arr = [1,2,3,4];
                                    arr.length = 0; // 15 символов
                                    arr = []; // 9 символов
                                    
                                    // Толи дело:
                                    var arr = [4, 9, "e", 123, 321];
                                    arr.length = 2;
                                    arr; // [4, 9]
                                    
                                      +3
                                      В этом вопросе есть несколько тонких моментов.
                                        +7
                                        Надо заметить, что arr.length изменяет существующий массив, в то время как присваивание пустого массива создает новый массив. Иногда это может быть важно.
                                          –1
                                          var arr = [1, 2, 3, 4];
                                          arr.testProperty = true;
                                          arr.length = 0;
                                          arr.testProperty; // -> true
                                          arr === arr; // true
                                          
                                          var old = arr;
                                          
                                          arr = [];
                                          arr.testProperty; // -> undefined
                                          
                                          old === arr; // false
                                        +6
                                        Использование большинства подобных хаков приводит к небольшому уменьшению количества символов и сильному ухудшению читаемости и понятности кода. Если у тебя, %программист%, есть уверенность, что твой код будут читать и править люди, чья квалификация не уступает твоей, то ты можешь использовать эти хаки. Если такой уверенности нет, напиши гораздо более понятное
                                        if (!age) {
                                          age = 10;
                                        }
                                        
                                          +4
                                          В tutorial’ах по значительной части динамических языков этот трюк описан и иногда даже рекомендован: из тех языков, что я знаю так делают Perl, Python, Ruby, Shell¹, Lua. В последнем так вообще вместо тернарного оператора (которого нет) часто используется a and b or c (в отличие от того же Python b здесь не должно быть всего лишь nil и false, что сильно расширяет область применимости).

                                          Так что понимание такого кода достаточно вероятно.

                                          ¹ Речь идёт о только кодах возврата (т.е. о “присвоении” $?), что практически бесполезно. Конструкция $(( a || b )) может вернуть только 1 или 0.
                                            +2
                                            Разумеется, квалифицированному программисту не составит разобраться во всех этих конструкциях. Более того, он в силу своего опыта знает во всех тонкостях как они работают и т.д. Безусловно, не составит труда и новичку сесть разобраться что именно хотел сказать автор кода. Я говорю о другом. О том, что понятность кода для программистов более низкой квалификации приводится в жертву его (кода) компактности, а это, на мой взгляд, неправильно. Грубо говоря, я могу нахреначить в своём коде столько хаков, что любой, прочитавший 5 его строк, проклянёт меня. А могу написать тот же самый по функциональности код таким образом, что любой юниор без труда сможет продолжать его развивать.
                                              0
                                              Зато для всех кроме новичков ваш код будет менее читабелен. Банально потому что длиннее.
                                                +1
                                                Вы всегда именуете переменные одним или двумя символами? Почему нет, ведь иначе код становится длиннее?

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

                                                Ещё раз: если вы уверены, что ваши хаки поймут все, кто будет читать и поддерживать код — на здоровье, лепите сколько хотите. Если такой уверенности нет, то лучше упростить код. Это же элементарно — подумать о тех, кто пойдёт за тобой.
                                                • НЛО прилетело и опубликовало эту надпись здесь
                                                    +1
                                                    Речь как раз о том, чтобы не стать Эллочкой Людоедочкой, которой хватает 10 строк, чтобы выразить любой алгоритм.

                                                    Если честно, я в некотором недоумении. Я не призываю раздувать код, я не призываю отказаться от паттернов, от красивых и элегантных решений. Я призываю писать код так, чтобы он был понятен тому, кто его читает, даже если читающий обладает более низкой квалификацией. И говорю о том, что использование хаков, как правило, затрудняет, а не облегчает чтение кода. При этом я уже на протяжении 5 комментариев спорю с чем угодно, но только не с этим изначальным высказыванием.
                                                    • НЛО прилетело и опубликовало эту надпись здесь
                                                        0
                                                        this.age = age || his_age || stored_age && parsed_age || 10.

                                                        У меня полное ощущение, что вы хорошо понимаете, что я пытаюсь донести. Разве нет?

                                                        • НЛО прилетело и опубликовало эту надпись здесь
                                                            0
                                                            Наверное, я взял слишком простой и очевидный хак, чтобы продемонстрировать свою мысль. Признаю свою ошибку.
                                            +7
                                            Никогда не считал себя квалифицированным кодером на js, но только из этой статьи узнал что что вот это this.age = age || 10 хак. Достаточно очевидная конструкция. Много где видел и сам писал. И читается проще чем if. ИМХО, конечно.
                                              +2
                                              Да как же проще-то? Сравните:
                                              1. Если не возраст, то сделать возраст равным десяти.
                                              2. Сделать возраст равным возрасту или 10.

                                              Какой из двух вариантов заставляет меньше напрягаться встроенный в мозг интерпретатор, неужели второй?
                                                +3
                                                Возможно, я уже не человек, но именно второй вариант мне кажется прозрачным и понятным.
                                                  +2
                                                  Мозг — не компьютер. Оба варианта воспринимаются как «если не возраст, то возраст равен 10», если вы знакомы с этим хаком.

                                                  А если вы достаточно длительное время писали на Perl’е (почему‐то там я использовал эту конструкцию чаще всего), то age = age || 10 будет восприниматься именно так: «если возраст не определён, то он равен 10». Вам нужно обновить свою версию «встроенного в мозг интерпретатора»: он вполне способен по ходу дела повышать уровень абстрагирования. Не думаю, что тут имеет смысл сравнивать энергетические затраты на повышение уровня с затратами на чтение вчетверо большего числа строчек.

                                                  Ктати, если переменных со значением по‐умолчанию больше одной, то || всегда лучше:

                                                  this.age = age || 10;
                                                  this.sex = sex || "Male";
                                                  
                                                  if (!age) {
                                                      age = 10;
                                                  }
                                                  if (!sex) {
                                                      sex = 10;
                                                  }
                                                  this.age = age;
                                                  this.sex = sex;
                                                  
                                                  • НЛО прилетело и опубликовало эту надпись здесь
                                                      0
                                                      Забыли hasOwnProperty.
                                                      • НЛО прилетело и опубликовало эту надпись здесь
                                                        • НЛО прилетело и опубликовало эту надпись здесь
                                                          0
                                                          Гм. Откуда в this возьмётся age?

                                                          Кроме того, у вас здесь три строчки «мусора»: кода, который не нужен для восприятия алгоритма (если такие конструкции раскиданы по всему коду, то к ним быстро привыкаешь), а также кода, который это восприятие затрудняет (если вы такую конструкцию видите в первый раз, то понять, что она делает, гораздо сложнее, чем в варианте с this.age = age || 10;).

                                                          Даже более того, вы используете ||. Супер! Будучи применённой к значениям внутри по‐первости (если о новичках: людях, плохо знакомых с вашим кодом) непонятного объекта defs (definitions? defaults? ещё что‐то?) она, разумеется, становится понятнее, чем в this.age = age || 10;.

                                                          Переменная defs понадобится только, если переменных уж слишком много: в этом случае для установки значений будет использоваться функция вида mergeObjects(this, args, defaults), а все переменные будут в объекте args.
                                                    +2
                                                    Если этот хак общепринятый и программисту он не понятнет, то программист полезет в гугл или форум и станет немножечко продвинутей
                                                    +3
                                                    Приведение переменной к числу при помощи +"123" лучше, чем используя parseFloat/parseInt тем, что parse* для строки "123foobar" вернут 123, тогда как +"123foobar" вернет NaN.
                                                      +3
                                                      Еще в копилку хаков — a >>> 0 работает, как приведение числа к uint32_t:
                                                      12345 >>> 0 // 12345
                                                      -12345 >>> 0 // 4294954951
                                                      12345678901 >>> 0 // 3755744309
                                                      Работает лучше, чем a & 0xFFFFFFFF, из-за явной беззнаковости.
                                                        +7
                                                        Чит для получения всех ключей объекта в массив:

                                                        var arr = [], i = 0, obj = {a:1,b:2,c:3};
                                                        for( arr[i++] in obj );
                                                        // arr = ['a','b','c']
                                                        

                                                          +4
                                                          Я бы использовал Object.keys если es5 доступно. А вообще если, нечто расширит Object.prototype в ie8 вы словите нечто веселое. Вам бы проверку для hasOwnProperty добаить и тогда это будет почти shim для Object.keys.
                                                            +1
                                                            Зависит от того поведения, которое нужно. Временами надо получить и ключи прототипов. hasOwnProperty сюда не вкорячить, тут вся магия в arr[i++] in.
                                                              +1
                                                              Оно и понятно, что в зависимости от того что нужно. Хотя признаться у меня не было случая когда нужны вообще все методы, даже прототипов, а вот ie8 появляется регулярно.
                                                          +1
                                                          честно говоря от фразы
                                                          если вы поместите объект в условный оператор if, то он выполнит либо true-ветку кода (когда объект имеет значение true), либо выполнит false-ветку (соответственно, когда объект имеет значение false).

                                                          у меня мозг взрывается.
                                                          Напишите «переменная», «выражение», ведь неокрепшие разумом дети будут читать.
                                                            –3
                                                            Однако стоит заметить, что с таким способом вам не удастся задать переменной значение 0, так как 0 является ложным выражением.

                                                            image
                                                              +5
                                                              Разумеется, если значение по умолчанию 0 — то никаких проблем не возникает. Проблемы начинаются, когда значение по умолчанию — 10, а нам надо 0 передать.
                                                                0
                                                                Ах вот о чём речь. Не сразу понял.
                                                              +15
                                                              В JavaScript || является примером выполнения короткого замыкания.

                                                              крайне неудачный перевод англ. термина Short-Circuit Evaluation, у нас обычно это называют «короткой схемой вычисления» или «упрощенный порядок вычисления выражений»

                                                              В JavaScript есть понятие замыканий (closure) и фраза «короткое замыкание» взорвет мозг юному паддавану
                                                                –3
                                                                Однако стоит заметить, что с таким способом вам не удастся задать переменной значение 0, так как 0 является ложным выражением.

                                                                Поправьте плз, если я не прав.
                                                                function x(x){alert(x||0)};x(); //0
                                                                –1
                                                                Прибавлю, что «void 0» ещё и набирать быстрее и проще, чем «undefined».
                                                                  +3
                                                                  Ну это смотря где набирать. В моем редакторе автодополнение выкинет undefined уже после «un».
                                                                  • НЛО прилетело и опубликовало эту надпись здесь
                                                                    –2
                                                                    Статья из цикла «Как из JS сделать perl»
                                                                      +2
                                                                      Конвертация строки в число с помощью оператора +
                                                                      И не только строки:

                                                                      +new Date
                                                                      
                                                                      В общем случае унарный плюс форсит вызов valueOf.

                                                                      Скорее всего, вы бы применили методы parseFloat и parseInt.
                                                                      В очередной раз незаслуженно не упоминается функция и конструктор Number. Функции parseInt и parseFloat делают несколько больше, чем конвертация в число. Наиболее близкий аналог это «выковыривание» числа из числоподобной строки. Для конвертации лучше прямолинено использовать конструктор Number.
                                                                      Вот хороший пример:

                                                                      ['1','2','3'].map(parseInt);
                                                                      ['1','2','3'].map(Number);
                                                                      
                                                                        +1
                                                                        С другой стороны, вместо if (x == «test») можно просто написать if (x). Если же x будет пустой переменной, то просто выполнится код из блока else.

                                                                        Если x число и равно 0, то тоже выполнится else. Из-за этого многие пишут код, который потом работает, не так, как ожидается.
                                                                          0
                                                                          Поэтому, товарищи, пишите тесты!
                                                                          0
                                                                          Понимаю, что перевод, но все эти вещи описываются в любом туториале (javascript.ru, mozilla) и книгах типа «Effective JS» и «JavaScript: the good parts». Причём объясняется лучше почему, как, зачем, когда надо, когда не надо.
                                                                            –2
                                                                            И этот язык становится доминирующим в веб-программирвоании! За что, господи, за что?
                                                                              0
                                                                              Есть закономерность: Каждый, кто знакомится с внутренностями JS — начинает высказывать предсказуемыми фразами.
                                                                                0
                                                                                Я уже детально познакомился с внутренностями Javascript, но своего мнения не поменял.
                                                                                  –1
                                                                                  А никто и не говорит иного — хуже языка, нежели JS лично я пока не видел. Даже всеми осуждаемый PHP по сравнению с сабжем — является идеально структурированным и продуманным до мелочей.
                                                                                    0
                                                                                    Вы видимо мало языков знаете. Есть и хуже.
                                                                                      0
                                                                                      В таком случае примеры в студию ;)
                                                                                        0
                                                                                        Этот пост о javascript. Я не хочу плодить оффтоп. Если вы хотите подискутировать на эту тему, то напишите отдельный пост о том, почему на ваш взгляд javascript ужасен и спросите мнения у сообщества. И уже в рамках этого поста мы с удовольствием все обсудим.
                                                                                          0
                                                                                          Из распространенных языков Javascript имеет наибольшее количество проблем:

                                                                                          — проблемы собственно языка и его синтаксиса: wtfjs.com/. Это просто убер-аргумент, тут не пройдет возражение, что в других языках не лучше. Лучше и гораздо.

                                                                                          — низкий порог вхождения и как следствие высокое число неквалифированных разработчиков, создающих библиотеки низкого качества

                                                                                          — несмотря на наличие стандарта ECMA script полнота его поддержки разными броузерами отличается, также имеются вещи работающие только в конкретном броуезере

                                                                                          — сложность разворачивания окружения фронтенд-разработчика абсолютно непропорциональна сложности решаемых в рамках этого окружения задач

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

                                                                                          — отладка сложных приложений — это ад, сложных приложений с большим количеством данных — двойной ад.

                                                                                          — коммьюнити на 90% состоящее из фан-боев, которые в упор отказываются видеть и признавать недостатки
                                                                                            0
                                                                                            Помню вашу статью про «фрактал отсоса». Вижу, что мнение ваше не изменилось. Повторно разводить тут полемику я не вижу смысла. У JS тьма недостатков, но для тех задач, которые он решает, у нас нет альтернативы. Потому и появились coffeescript и typescript, которые тоже далеки от идеала.
                                                                                                0
                                                                                                Вывод из морали: Objective-C появился для того, чтобы хорошенько развеселить очень грустных разработчиков.
                                                                                                0
                                                                                                Достаточно было сказать что в js слабая динамическая типизация и программа старается не падать почти никогда(пытаясь обработать необрабатываемое), все ваши аргументы против js проистекают отсюда.
                                                                                                Удивительно как вы эту одну особенность языка свели к множеству недостатков.
                                                                                                Javascript и правда ужасен, но и ваши аргументы тоже.
                                                                                                  0
                                                                                                  Понимаете, если просто написать так как вы, то человек читающий вас может подумать, что проблема в общем-то плевая, хотя за «в js слабая динамическая типизация и программа старается не падать почти никогда(пытаясь обработать необрабатываемое)» скрывается страшное.
                                                                                      0
                                                                                      Я хорошо знаю js, и мне он нравится — это я подтверждаю закономерность, или нарушаю?
                                                                                        0
                                                                                        А чем именно вам нравится этот язык в сравнении с другими?
                                                                                          +1
                                                                                          Провоцируете? Хорошо.

                                                                                          По сравнению с PHP
                                                                                          В js всё-таки меньше бардака, и объектно-ориентированность прикручена получше. В JS можно понять несколько принципов, и дальше можно в уме прокручивать код не хуже интерпретатора:

                                                                                          — Значения приводятся к нужному типу, если это возможно. У объектов приведения к примитивам для это дёргаются методы valueOf и toString.
                                                                                          — Если хотя бы один операнд — строка, то весь оператор строка работает как конкатенация.
                                                                                          var объявляет переменную в скопе на уровне функции. Если перемнная явно не объявлена, она она через механизм делегирования берётся из верхних скопов.
                                                                                          — Если у объекта нет свойства, она через механизм делегирования берётся из прототипа.
                                                                                          this определяется при вызове, а не при создании.

                                                                                          В PHP же без php.net, открытого в соседней вкладке делать нечего.

                                                                                          По сравнению с Python
                                                                                          Да, считай, два брата-близнеца. Только питон не так активно приводит типы, как js. Половину wtfjs можно портировать на питон без особых изменений (напомню, что BOM/DOM не является частью языка), даже поведение супер-страшного прототипного наследования:

                                                                                          function Foo(){};
                                                                                          Foo.prototype.bar = 42;
                                                                                          
                                                                                          var foo = new Foo();
                                                                                          console.log(foo.bar); // 42
                                                                                          
                                                                                          Foo.prototype.bar = 32;
                                                                                          console.log(foo.bar); // 32
                                                                                          


                                                                                          class Foo:
                                                                                          	bar = 42
                                                                                          
                                                                                          foo = Foo()
                                                                                          print foo.bar # 42
                                                                                          
                                                                                          Foo.bar = 32
                                                                                          print foo.bar # 32
                                                                                          


                                                                                          Сравнивать с VBScript, надеюсь, не заставите? А с Руби у меня, к сожалению, не сложилось.
                                                                                            +1
                                                                                            Значения приводятся к нужному типу, если это возможно. У объектов приведения к примитивам для это дёргаются методы valueOf и toString.

                                                                                            В php тоже приводятся к нужному типу, причём приведение типов более приятное, нежели в JS, у объектов также дёргается метод __toString.

                                                                                            Если хотя бы один операнд — строка, то весь оператор строка работает как конкатенация.

                                                                                            В php специально сделали оператор ".", для конкатенации, а "+" для сложения. При использовании конкатенации — всё будет стараться приводиться к строке, при сложении — к числам — это значительно удобнее и гибче, нежели в JS.

                                                                                            var объявляет переменную в скопе на уровне функции. Если перемнная явно не объявлена, она она через механизм делегирования берётся из верхних скопов.

                                                                                            $some — объявляет переменную в скоупе на уровне функции. Если требуется передать переменную в безымянную функцию — её требуется явно указать в операторе use — это облегчает понимание кода, так же в абсолютно любом месте кода ссылки на класс и на инстанс класса, включая родителей и наследников ($this, self, parent, static) никогда не поменяются — это намного удобнее и практичнее, нежели страдать с постоянно изменяющимися контекстами this и через раз использовать call\apply.

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

                                                                                            Очень сомнительное преимущество. С таким же успехом в php можно создать абстрактный класс и назвать его Prototype и после наследования от оного — будет тоже самое, если у объекта нет свойства — оно возьмётся из абстрактного класса =)

                                                                                            this определяется при вызове, а не при создании.

                                                                                            А вот это как раз наоборот боль — изменяющийся контекст добавляет больше проблем, нежели их решений. Мне проще в нужных местах передать этот контекст в виде аргумента, нежели следить за тем, чтобы он нигде не поменялся от неосторожного телодвижения.
                                                                                              0
                                                                                              Если взять Ваш код на питоне, добавить в конец класса end и заменить print на puts (или p) — получится ruby. Так что отличий в примере почти нет.
                                                                                                0
                                                                                                В js всё-таки меньше бардака, и объектно-ориентированность прикручена получше.


                                                                                                Отсутствие унифицированного подхода к ООП плохо ходя бы потому, что затрудняет создание индексатора для языка, что в свою очередь мешает хорошей его поддержке в IDE. И если я правильно я понимаю, то построить иерархию классов в JS можно только выполнив код, поскольку явные декларативные конструкции языка, позволяющие определить кто чей потомок отсутствуют.Может вы помните PHP по четвертой версии, но там сейчас существует очень хорошая поддержка ООП. И не только я не согласен с тем, что ООП в JS не идеально, например в MooTools есть своя надстройка над ООП.

                                                                                                В JS можно понять несколько принципов, и дальше можно в уме прокручивать код не хуже интерпретатора:


                                                                                                Я с вами не согласен:

                                                                                                    var d = new Date("2012--12-01"); // объект будет создан без предупреждений
                                                                                                
                                                                                                    d.toJSON();       // null
                                                                                                    d.getTime();      // NaN
                                                                                                    d.toString();     //'Invalid Date'
                                                                                                    d.toISOString()   // наконец-то бросит ошибку!
                                                                                                
                                                                                                
                                                                                                    []     == true // false - пустой массив, вроде бы интуитивно понятно
                                                                                                    [1]    == true // true  - единица, все логично - и массив непустой, и единица приводится к true
                                                                                                    [2]    == true // false - массив непустой, значит дело в единице, которая приводится к true... 
                                                                                                    [true] == true // false - либо мы имеем хитрое правило приведения, либо true != true
                                                                                                     
                                                                                                    // может все дело в магической единице ?
                                                                                                    [1 , 1]  == true // false нет...
                                                                                                     
                                                                                                    // а если вложенный массив?
                                                                                                    [ [1], [1] ]    == true // false
                                                                                                    [ [ [ [1] ] ] ] == true // true
                                                                                                     
                                                                                                    // но !
                                                                                                    new Array()    == true // false 
                                                                                                    new Array(1)   == true // false 
                                                                                                    new Array(1,2) == true // false 
                                                                                                


                                                                                                Если вы считаете такое поведение допустимым, то вы лукавите, обвиняя PHP в бардаке.

                                                                                                — Значения приводятся к нужному типу, если это возможно. У объектов приведения к примитивам для это дёргаются методы valueOf и toString.


                                                                                                Это происходит как минимум в php и perl, но отличаются правила приведения.

                                                                                                — Если хотя бы один операнд — строка, то весь оператор строка работает как конкатенация.


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

                                                                                                Позвольте вам привести типичный код на JS, который я часто вижу в проектах:

                                                                                                  $("#id3").val(
                                                                                                      sum(
                                                                                                       $("#id1").val(),
                                                                                                       $("#id2").val()
                                                                                                      )
                                                                                                  );
                                                                                                


                                                                                                А потом эту же функцию образную sum несколькими десятками строк ниже используют как:

                                                                                                  $("#id3").val(
                                                                                                      sum(
                                                                                                       5,
                                                                                                       10
                                                                                                      )
                                                                                                  );
                                                                                                


                                                                                                Т.е. результат для например sum(5,10) и sum(«5», «10») будет разным. И очень мало людей настолько параноидальны, что будут думать, что в кишках каждой функции, в которую они кидают данные.

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

                                                                                                — var объявляет переменную в скопе на уровне функции. Если перемнная явно не объявлена, она она через механизм делегирования берётся из верхних скопов.


                                                                                                Или я вас не понял, или это есть во многих языках.

                                                                                                — Если у объекта нет свойства, она через механизм делегирования берётся из прототипа.


                                                                                                Позвольте, но это же есть в любом языке с традиционным ООП. Или я вас опять неправильно понял?

                                                                                                — this определяется при вызове, а не при создании.


                                                                                                Честно говоря не понимаю этого плюса. Пробрасывать целый скоуп в функцию при помощи apply не совсем правильно, это как делать урезаныне глобальные переменные.

                                                                                                В PHP же без php.net, открытого в соседней вкладке делать нечего.


                                                                                                Во-первых обычно область применения PHP несколько пошире, чем у JS. Или вы наизусть помните как читать файлы через ноду? API к монге тоже помните? Во-вторых php.net — это источник в котормо собрано все. Весь язык, все функции, охватывающие основную область применения, примеры и caveats к этим функциям. Для Js я такого не встречал, хотя тут играет роль минимализм.

                                                                                                По сравнению с Python
                                                                                                Да, считай, два брата-близнеца. Только питон не так активно приводит типы, как js.

                                                                                                Половину wtfjs можно портировать на питон без особых изменений (напомню, что BOM/DOM не является частью языка), даже поведение супер-страшного прототипного наследования:

                                                                                                function Foo(){};
                                                                                                Foo.prototype.bar = 42;
                                                                                                
                                                                                                var foo = new Foo();
                                                                                                console.log(foo.bar); // 42
                                                                                                
                                                                                                Foo.prototype.bar = 32;
                                                                                                console.log(foo.bar); // 32
                                                                                                


                                                                                                class Foo:
                                                                                                    bar = 42
                                                                                                
                                                                                                foo = Foo()
                                                                                                print foo.bar # 42
                                                                                                
                                                                                                Foo.bar = 32
                                                                                                print foo.bar # 32
                                                                                                




                                                                                                Не просто «не так активно приводит типы», а больно бьет по рукам за неявность. И, несмотря на возможность трюкачества с питонячьим ООП, там оно есть в явном виде. А поменять кусок класса в рантайме можно почти в любом динамическом языке, но обычно этим почему-то никто не пользуется. Скорее всего потому, что единственная вещь где это часто нужно — реализация наследования, которое таки реализовано явно в других языках.
                                                                                                  0
                                                                                                  В других языках это либо запрещено, либо есть специальный оператор конкатенации.
                                                                                                  Извините, мне стало смешно. Наверное, C#, Java и Python слишком мало распространены…
                                                                                                    0
                                                                                                    Странно, но в питоне

                                                                                                    "3" + 3
                                                                                                    


                                                                                                    выдает ошибку.

                                                                                                    Java и C# гораздо более строготипизированные языки, чем Javascript, так что там контроль типов аргументов происходит уже по сигнатуре функции. По этой же причине(типизация) вы всегда знаете что с чем вы складываете/конкатенируете, в отличие от javascript.

                                                                                                    Можете пояснить из-за чего вам стало смешно?
                                                                                                      0
                                                                                                      Может вы перепутали с Ruby? Насколько я знаю — это единственный язык (помимо JS), где символ "+" используется как в качестве сложения, так и в качестве конкатенации и при этом он не выдёт ошибку при использовании этого оператора с разными типами.
                                                                                                      0
                                                                                                      Хм… Я и не говорил, что описанные мной принципы — это нечто уникальное для джаваскрипта. Это — минимум, знание которого позволит не удивляться wtfjs.

                                                                                                      Когда при приведении к json метод возвращает null — это не удивительно, в json нет NaN. Когда таймстемп невалидной даты — невалидное число, для меня тоже нет в этом ничего удивительного. А примеры с == true надуманны. Вы видели, чтобы кто-то так писал на практике, сравнивая я true явно, причем двойным равно? Но так или иначе — тут оба операнда приводятся к числу, и никакой магии нет. И это, кстати, ничем не лучше пхпшного isEmpty().

                                                                                                      Да, на php я не писал ничего более-менее серьёзного уже пару лет. И, возможно, там всё стало хорошо, и даже появилась поддержка юникодовых строк. Но ведь и из джаваскрипта понемногу выкашивают родовые травмы — нельзя взять и поменять некомпилируемый язык. Та же хрень и с питоном.

                                                                                                      Пробрасывать целый скоуп в функцию при помощи apply

                                                                                                      Вы не знаете, что делает apply, ведь так?

                                                                                                      И да, я помню, как в ноде прочитать файл (монгой я не пользуюсь), хотя пишу на ней пару десятков строчек в месяц максимум — стандартный модуль возвращает объект стандартного интерфейса Stream. И никаких плясок с ob_start(), из-за того, что какой-то метод может возвращать строки только записав в stdout.
                                                                                                        0
                                                                                                        Когда при приведении к json метод возвращает null — это не удивительно, в json нет NaN.


                                                                                                        Проблема в том, что null — это валидный JSON, и возвращать такое не логично в случае, когда дата невалидна.

                                                                                                        Когда таймстемп невалидной даты — невалидное число, для меня тоже нет в этом ничего удивительного.


                                                                                                        Тут я с вами согласен, дело только за оставшимися функциями.

                                                                                                        Вы не знаете, что делает apply, ведь так?


                                                                                                        Очевидно я имел в виду контекст this, а не скоуп, область видимости, если вы хотите придраться к формулировке. И тем не менее я не вижу хороших способов применения этого.

                                                                                                        Я задал вам вопрос потому, что мне кажется что все современные языки активно тащат фичи друг у друга, так что с точки зрения «приятных» вещей все они плюс-минус одно и то же. Добавление новых фич не ломает совместимость, и я рад что в новом стандарте ECMA script наконец добавили итерацию по значениям коллекций. Но вот исправить все эти по-вашему «надуманные» проблемы Javascript, на которые уже завязана куча кода, практически нереально. И поэтому я удивляюсь, что в этой части вы не замечаете разницы Javascript и других языков, продолжая его любить.
                                                                                                          0
                                                                                                          Ну, нравится мне этот язык. На нём можно писать для продакшна — жутко скучно со всеми включенными ворнингами jshint (кроме eqnull), используя только самые известные и несложные паттерны. Ну вроде как, в php можно использовать register_globals и $_REQUEST, но не стоит. Так и в js можно использовать with и ==, но не стоит.

                                                                                                          Но вместе с этим, есть и достаточно ниндзютсу не для продакшна, которое делает его нескучным. Это, наверное, что-то вроде ручной коробки передач — на любителя. Я вовсе не призываю его любить, лишь удивлён таким количеством язвы в сторону языка, в котором, имхо, всего один смертельный косяк — отсутствие int64.
                                                                                                            0
                                                                                                            Я работаю фронт-энд разработчиком уже более 3х лет, хочется уже наконец высказать всё, что накипело об этом удивительном творении безумного разума.

                                                                                                            Наверняка у других участников и коллег тоже подобное психоэмоциональное состояние перманентного стресса после JS, под конец рабочего дня ;)
                                                                                                              0
                                                                                                              Я уже больше 6 лет на фронте, было всякое, но сейчас: прикрутил нормальный пакетный менеджер, разруливающий зависимости, подучил матчасть по языку. И знаете, мне JS тоже нравится. При всех его «особенностях». Поэтому не говорите за всех.
                                                                                                        0
                                                                                                        Ах да. Про функцию sum. Это уже зависит от вас. Если внутри аргументы будут приводиться к Number, то ничего плохого не произойдёт, и не надо приводить ничего вручную при каждом вызове — это уже вопрос апи, а не языка.

                                                                                                        Угадайте, что вернет `Math.max(«2», «3») ` из стандартной либы. Как ни странно, число 3.
                                                                                                          0
                                                                                                          Если внутри аргументы будут приводиться к Number


                                                                                                          В этом и проблема — нельзя быть уверенным в чужом коде в такой, казалось бы, мелочи.
                                                                                                  0
                                                                                                  А ведь помнится ходили разговоры про tcl в браузере вместо/вместе c js…

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

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