10 странностей и секретов JavaScript

Автор оригинала: Andy Croxall
  • Перевод
JavaScript. Одновременно пугающий и притягательный. Я уверен, если бы Пабло Пикассо был программистом, то он создал именно этот язык. Null здесь является объектом, пустым массивом, он равен false, а функции летают рядом с ним как теннисные мячики.



Целевая аудитория статьи — продвинутые разработчики, которые хотят узнать больше о JavaScript. Это коллекция странностей и хорошо хранимых секретов. Секреты некоторых глав могут пригодиться вам при разработке собственных программных продуктов, другие просто являются предметами для смеха и осуждения. В любом случае, мы начинаем!

Типы данных и объявления


1. Null — это объект

Давайте начнем с известной всем странности. Null — объект, причем самый высший из них. Null? Объект? «Этого не может быть, так как null — это полное отсутствие какого-либо значения», — скажете вы. И вы, несомненно, правы. Однако, как бы то ни было, у меня есть доказательство. Вот оно:

alert (typeof null); //выводит 'object'


Несмотря на это, null не является экземпляром object. (Если вы не знали, значения в JavaScript являются экземплярами базового объекта. Каждое число является экземпляром объекта Number, каждый объект является экземпляром объекта object и так далее.) Это возвращает нам разум, ведь null — отсутствие значения, значит он не может быть экземпляром чего-либо. Так следующая строка выводит false:

alert (null instanceof Object); //false


2. NaN — это число

Вы удивлены, что null — это объект? Тогда подумайте вот о чем — NaN («Not a Number» — не число) является числом! Чем дальше, тем страшнее! NaN даже не равен самому себе. (Ваша голова еще не болит?)

alert (typeof NaN); //выводит 'Number'
alert (NaN === NaN); //выводит false


По факту, NaN не равен вообще ничему. Единственный способ сравнить что-нибудь с NaN — функция isNaN().

3. Массив без ключей == ложь (или о правдочке и кривдочке)

Вот и другая странность JavaScript:

alert (new Array() == false); //выводит true


Чтобы понять, что здесь происходит, вы должны принять концепцию правдочки и кривдочки (truthy and falsy). Есть «облегченные» виды true и false, которые могут вас подвести, если вы углубитесь в логику и философию.

Я прочитал много определений, что такое правдочка и кривдочка, и простейшее, по моему мнению, звучит так: в JavaScript каждый объект имеет встроенное булевское значение, которое вызывается, когда объект должен вести себя как булевский; например, когда вы сравниваете его с false.

Так как яблоки нельзя сравнивать с жемчугом, когда JavaScript вызывает сравнение объектов разных типов, он выполняет их приведение к общему типу. False, zero, null, undefined, пустые строки и NaN — все приводится к false — не постоянно, а только в контексте данного выражения. Вот пример:

var someVar = 0;
alert (someVar == false); //выводит true


Здесь мы запрашиваем сравнение числа 0 и значения false. Так как данные не являются однотипными, JavaScript неявно преобразует их значения к значениям встроенных булевских флажков, который (в случае с нулем) равен кривдочке.

Вы могли заметить, что я не включал пустые массивы в список объектов, приводящихся к false. Они ведут себя очень интересно — они вычисляются как правдочка, но, при сравнении с булевским значением, ведут себя как кривдочка. Вы еще живы? Хорошо, потому что я подготовил для вас еще один пример:

var someVar = []; //пустой массив
alert (someVar == false); //выводит true
if (someVar) alert ('hello'); //выводит 'hello', значит someVar является true


Чтобы избежать приведения, вы можете использовать оператор сравнения типа и значения === (вместо ==, который сравнивает только значения.)

var someVar = 0;
alert (someVar == 0); // выводит true, 0 - кривдочка
alert (someVar === false); // выводит false, 0 - число, а не булевское значение


Фух. Как вы уже поняли, приведение типов в JavaScript — довольно сложная тема, так что вам стоит посвятить еще пару вечеров на изучение данной темы.

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

Регулярные выражения


4. Метод replace() может принимать callback функцию

Это один из самых секретных секретов JavaScript, он пришел к нам в версии 1.3. Большинство использований replace выглядит как-то так:

alert('10 13 21 48 52'.replace(/d+/g, '*')); //заменить все числа на *


Это простая замена, число — звездочка. Но что, если мы хотим получить возможность самому выбрать место для замены? Что если мы хотим заменить только те числа, которые меньше 30? Это может быть достигнуто с помощью того же метода replace. Мы просто должны вызвать callback функцию при каждом совпадении:

alert('10 13 21 48 52'.replace(/d+/g, function(match) {
	return parseInt(match) < 30 ? '*' : match;
}));


Для каждого совпадения JavaScript будет вызывать нашу функцию, передавая наше совпадение в качестве параметра. Потом, мы вернем звездочку (если число меньше 30) или параметр без изменений.

5. Регулярные выражения — больше чем найти и заменить

Многие продвинутые JavaScript разработчики используют только match и replace при работе с регулярными выражениями. Но JavaScript дает нам больше возможностей, чем просто 2 метода.

Наибольший интерес представляет test(), которые работает как match, но не возвращает совпадение, он просто проверяет вхождение паттерна в строку.

alert (/\w{3,}/.test('Hello')); //выводит 'true'


Код сверху ищет в строке 3 и более буквенно-числовых символа и, так как строка Hello соответствует этим требованиям, мы получаем true. Нам не нужно само совпадение, только результат.

Также, объект RegExp, с помощью которого вы можете создавать динамические регулярные выражения, противоположен статичному. Это дает преимущество в виде короткой записи (мы просто окружили паттерн слэшами). Но, в данном случае, вы не можете указывать на переменные, так что создание динамических паттернов невозможно. С RegExp() вы можете это сделать

function findWord(word, string) {
	var instancesOfWord = string.match(new RegExp('\b'+word+'\b', 'ig'));
	alert(instancesOfWord);
}
findWord('car', 'Carl went to buy a car but had forgotten his credit card.');


Здесь мы создаем динамический паттерн, который основан на значении аргумента word. Функция возвращает число нахождения слова word в строке целиком (не частью другого слова). Так, наш пример вернет car однажды, игнорируя это слово, входящее в Carl и card. Мы делаем это с помощью флага b.

Так как RegExp определен как строка, а не с помощью короткого синтаксиса, мы можем использовать переменные при построении паттерна. Это означает, что мы должны дважды экранировать любые специальные символы, так как мы сделали с символом границы слова.

Функции и контексты



6. Вы можете подделать контекст

Контекст, в котором что-то выполняется, определяет, какие переменные доступны. Свободный JavaScript (тот, который запускается не из функции) оперирует с глобальным контекстом объекта window, к которому всё имеет доступ, а локальные переменные, которые объявлены внутри функции доступны только внутри этой функции, не снаружи.

var animal = 'dog';
function getAnimal(adjective) { alert(adjective+' '+this.animal); }
getAnimal('lovely'); //выводит 'lovely dog';


В этом примере наша переменная и функция объявлены в глобальном контексте. Так как он всегда указывает на текущий контекст, в этом пример он указывает на window. Поэтому, когда функция ищет window.animal, она находит его. Пока все нормально. Но, мы можем заставить нашу функцию думать, что она запускается в другом контексте, не смотря на родной контекст. Мы можем провернуть это с помощью метода call(), а не с помощью функции.

var animal = 'dog';
function getAnimal(adjective) { alert(adjective+' '+this.animal); };
var myObj = {animal: 'camel'};
getAnimal.call(myObj, 'lovely'); //выводит 'lovely camel'


Здесь наша функция вызывается не в контексте window, а в контексте myObj как аргумент метода call(). Call() делает вид, что функция является методом myObj. Заметьте, что любые аргументы, которые мы передаем в call() после первого будут переданы в функцию.

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

Кстати, apply() выполняет ту же работу, что и call(), кроме того, что аргументы передаются как массив. Вот пример:

getAnimal.apply(myObj, ['lovely']); //аргументы передаются массивом


7. Функции могут вызывать сами себя

Ничего не запрещает этого:

(function() { alert('hello'); })(); //выводит 'hello'


Синтаксис очень прост: мы объявляем функцию и сразу же вызываем ее как и другие функции, с помощью скобок. Вы можете спросить: «Зачем нам использовать это?» Это может выглядеть как несоответствие терминов: функция обычно содержит код, который мы хотим выполнить позже, не сейчас, иначе, мы не помещаем его в функцию.

Одно из преимуществ само-исполняющихся инструкций (СИИ) — использование текущих значений переменных внутри отложенного кода. Вот проблема:

var someVar = 'hello';
setTimeout(function() { alert(someVar); }, 1000);
var someVar = 'goodbye';


Новички на форумах спрашивают, почему alert выводит goodbye, а не hello. Ответ в том, что значение переменной someVar не вычисляется до запуска кода. А к тому времени, переменная уже перезаписана значением goodbye.

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

var someVar = 'hello';
setTimeout((function(someVar) {
	return function()  { alert(someVar); }
})(someVar), 1000);
var someVar = 'goodbye';


Теперь, мы видим hello, как мы и хотели, потому что функция показывает изолированную версию переменной.

Браузер


8. Firefox читает и возвращает цвета в RGB, не в HEX

Я никогда не мог понять, почему Mozilla делает это. Конечно, я должен показать пример:

Hello, world!

<script>
var ie = navigator.appVersion.indexOf('MSIE') != -1;
var p = document.getElementById('somePara');
alert(ie ? p.currentStyle.color : getComputedStyle(p, null).color);
</script>


Когда большинство браузеров возвращает ff9900, Firefox вернет rgb(255, 153, 0) эквивалент. Есть функции, которые конвертируют RGB в HEX.

Как вы могли заметить, у IE есть иные методы определения вычисленных стилей.

Дополнительно


9. 0.1 + 0.2 !== 0.3

Эта странность встречается не только в JavaScript, это проблема всей компьютерной науки. Вывод будет такой — 0.30000000000000004.

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

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

Остальное выходит за рамки этой статьи (у нас тут не математический кружок)

Данная тема широко рассматривается на форумах о компьютерной науке и разработке. Вы можете предложить свои пути решения этой проблемы.

10. Неопределенное может быть определено

Хорошо, давайте закончим глупостью. Это странно звучит, но undefined — незарезервированное слово в JavaScript, хотя и имеет особое значение — показать, что переменная не определена.

var someVar;
alert(someVar == undefined); //выводит true


Пока все в порядке. Но:

undefined = "I'm not undefined!";
var someVar;
alert(someVar == undefined); //выводит false!


Вы можете обратиться к списку всех зарезервированных слов JavaScript.
Поделиться публикацией
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

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

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

    +19
    По пункту 2 (NaN) хочется отметить, что ничего уникального в описанном Вами нет. NaN ведет себя схожим образом во многих языках. То есть это не особенность JavaScript.
      +27
      Более того, такое поведение — стандарт.
        +16
        Более того, такое поведение логично. Иначе выйдет, например, что log(-2) = sqrt(-1), например. Крайне маловероятно, что два NaN получены одинаковым способом, значит, с большей вероятностью они должны быть не равны.
          0
          А как реализовано isNaN()? Или это вызов более низкоуровневой функции?
            +1
            Если не изменяет память, проверка на неравенство самому себе, вроде как, эквивалентна проверке на NaN (вообще, а не в контексте JS). Так что, может быть, так и реализовано.
              +1
              return !(arg==arg);

              Лаконично. Но я думал более глубокая проверка происходит.
                +1
                Логичнее было бы:
                return arg!=arg;
                
                  +1
                  Да, вы правы.
                    +1
                    Нет, вы оба неправы.
                    return arg !== arg;
                    

                      0
                      Кто на чем пишет смотря. Обсуждалась сама проблема NaN, чему подвержен не только JS.
                        0
                        нестрогое сравнение есть не только в JS
                          0
                          В С++ есть?
                            0
                            Хватит холивара. Мы обсуждали лишь правильность вывода.

                            Пришли к решению:
                            Либо (если язык поддерживает строгое сравнение):
                            return arg !== arg;
                            

                            Либо:
                            return arg != arg;
                            
              –2
              Когда-нибудь я научусь обновлять комментарии..

              var nan1 = parseFloat('notNaN');
              var nan2 = NaN;
              
              function isnan (nan) {
                 return nan.toString() == 'NaN' ? true : false;
              }
              
              isnan(nan1) == isnan(nan2); //true
              
                +2
                Простите, это ваше творчество или кусок из какой-либо реализации JS?
                  0
                  Это мое творчество. Но работает (по-моему) аналогично этому. Но последнее предпочтительнее (из-за кол-ва кода).
                    +6
                    Ваш пример похож на:
                    public static boolean isNegative(int x) {
                        return Integer.toString(x).matches("-[0-9]+");
                    }
                    

                    из этого поста.
                      +3
                      За исключением того момента, что isNegative хотя бы правильно работает.
                  +3
                  var nan3 = "NaN";
                  var nan4 = {toString: function() {return "NaN"}};
                  isnan(nan3); // true
                  isnan(nan4); // true
                  
                    0
                    Да, я понял свою ошибку. Спасибо.
                    К сожалению, удалить комментарий хабр не позволяет…
                    0
                    isnan(null); // error
                    isnan({toString: function() {return 'NaN'}}); // true

                    Ну и так далее…
                  0
                  По-моему, утверждать, что две бессмыслицы не равны друг другу, столь же нелогично, как что они равны друг другу. Я бы, на месте ECMAScript, выбросил исключение.
                    0
                    Это уже более философский вопрос. Тогда уж логично бросать исключение при попытке вычислить вещи типа log(-2) или sqrt(-1). Ну и чтобы быть до конца последовательным, то и для деления на 0 сделать исключение. Вот только были ли исключения в первой версии JavaScript — Айк его знает. А если не было, то менять логику таких базовых операций потом уже было поздно.
                      0
                      А с философской точки зрения, NaN===NaN должно вычисляться в undefined, имхо.
                        +1
                        Вы что, прикалываетесь?)
                        Оператор === ВСЕГДА возвращает Boolean и должен так возвращать.
                          0
                          Я говорю — с философской точки зрения; ведь сравнивать NaN-ы бессмысленно.
                +1
                Хочу заметить, что это написал не я. Это перевод
                  +1
                  И перевод хороший, к слову.
                    +2
                    Перевод хороший, а вот сама статья не очень. Надергали непонятно каких, никак не связанных между собой штук, половина которых собственно к JS отношения не имеет.
                +29
                Всё это не баги, а фичи.
                  +8
                  В случае с typeof, который возвращает странные ответы вроде object в случае с null, признанный баг, в остальном да, фичи.
                    0
                    С каких пор
                    typeof(null) == "object"
                    стало багом?
                    Смотрим реализацию JS движка (на примере Rhino) — Null / null это такой-же объект — и поэтому вывод typeof верен.
                      +1
                      С тех пор как Брендан Айк сам признал это багом?
                        0
                        Ну так это актуально только для Harmony который еще пока в статусе драфта.
                        Сейчас речь идет именно текущей реализации ECMAScript БЕЗ расширения Harmony — поскольку там свои особенности, тот-же let и не только.
                          0
                          Это как-то влияет на то, что поведение typeof — общепризнанный баг? Другое дело, что его уже не починить из-за обратной совместимости.
                            –1
                            Если баг работает так что его поведение считают by default (и не чинят) — то это уже не баг :)
                              0
                              Нестандартная блочная модель IE тоже была хорошо описана и многие делали by default (сайт работает только в IE6), но она не переставала от этого быть багом.
                                0
                                Модель рендеринга != языку, кстати typeof(null) == «object» во всех браузерах сейчас, поэтому это есть везде by default.
                                  0
                                  Это лишь вопрос пристрастий.
                                    0
                                    А вот так не все:

                                    Object.prototype.toString.call(null);
                                    
                                      0
                                      А теперь впишите это в отличный от V8 движок и сильно удивитесь.
                                      Например Rhino выдает "[object Global]"
                                        0
                                        Я это и имел ввиду.
                                        Из всех известных мне реализций, корректно [object Null] возвращают лишь Presto, SpiderMonkey и WebKit
                    +3
                    var someVar = 0;
                    alert (someVar == 0); // выводит true, 0 - кривдочка
                    alert (someVar === 0); // выводит false, 0 - число, а не булевское значение
                    

                    Последняя строка вернет true, а не false, т.к. сравнение идут по значению, а значения совпадают.
                      +1
                      Здесь опечатка. В оригинале, при сравнении, используется false
                      +8
                      Brendan Eich уже рассказывал, почему это работает именно так, а не иначе. Javascript бесподобен.
                        +1
                        Мне хочется усомниться в материале. Например:
                        var someVar = 0;
                        alert (someVar == false); //выводит false

                        Это работает не так. Не верите — проверьте.
                          0
                          mac:~ den$ node
                          > var someVar = 0;
                          undefined
                          > someVar == false
                          true
                          


                          ЧЯДНТ?

                          ЗЫ: Статьи такого рода доставляют…
                            0
                            При операторе сравнения переменная someVar преобразуется в булевое значение. Нужно применять оператор идентичности (===) в таком случае.
                              0
                              Угу, а то, что в спецификации написано, что в большинстве случает операнды приводятся к Number и потом сравниваются, то этого ничего. Думаете в правильную сторону, но по факту происходит другое при сравнении этих значений.
                              0
                              Вы все так делаете. Но в комменте сказано что это выражение «выводит false», а выводит оно true
                                0
                                Извините, был не внимателен.
                            +2
                            Про 4 не знал, спасибо.
                              0
                              Ага, это единственное новое, что я узнал из статьи. Ради этого можно и плюсануть :-)
                              +8
                              Каждое число является экземпляром объекта Number, каждый объект является экземпляром объекта object и так далее.)
                              Вот насколько я помню, ничего подобного. Когда мы пытаемся вызвать метод или свойство у примитивного объекта, создаётся экземпляр (значение как бы оборачивается в конструктор), который возвращается и именно у него всё это вызывается.

                              Побочным эффектом такого поведения является вот это:
                              var test=1;
                              test.prop='test';
                              alert(test.prop); // undefined
                              
                                0
                                Ага, вроде autoboxing в Java, когда неявно преобразуются всякие int в Integer.
                                –9
                                9. 0.1 + 0.2 !== 0.3

                                Эта странность встречается не только в JavaScript, это проблема всей компьютерной науки. Вывод будет такой — 0.30000000000000004.

                                Хотелось бы увидеть результат на процессорах и системах различных архитектур (x86-x64)
                                  +5
                                  Эта «странность» говорит только о том, что большинство десятичных дробей нельзя в точности выразить двоичной дробью. С двоичными дробями процессору работать проще. IEEE754 также предусматривает десятичные дроби, но в JavaScript этот тип не реализован.

                                  Есть библиотека Big.js, которая позволяет работать с десятичными дробями:

                                  Big('0.1').plus('0.2').cmp('0.3') // => 0
                                    +1
                                    А стандарт почитать?
                                    4.3.19
                                    Number value
                                    primitive value corresponding to a double-precision 64-bit binary format IEEE 754 value
                                    На любом процессоре должно работать именно так, иначе это будет не стандартный JavaScript.
                                    • НЛО прилетело и опубликовало эту надпись здесь
                                      +8
                                      Статья неплохая, но всё, что вы тут написали, давно известно почти любому яваскрипт-программисту.
                                        +1
                                        Хабр читают только яваскрипт-программисты?
                                          –2
                                          Подозреваю, что не JS программисту всё это неинтересно, а JS программист всё это и так уже знает.
                                            +2
                                            Я вот совсем не JS-программист, но для веб-программирования JS необходим.
                                        +14
                                        3. Массив без ключей == ложь (или о правдочке и кривдочке)


                                        Ну и объяснение. На самом деле при сравнении массива с примитивом он приводится к примитиву через toString. Сначала каждый элемент массива приводится к строке, а потом строки соединяются запятыми. В случае пустого массива получается пустая строка. Так как мы не можем сравнить пустую строку с false, логичным образом false приводится к чему? Само собой к числу. К нулю. И так как мы не можем сравнить пустую строку с нулем, она приводится к числу. К нулю. А потом мы уже можем сравнить ноль с нулем. А вот в случае непустого массива мы получим [1,2,'test'] == '1,2,test'
                                          0
                                          Вот вот, просто автор почему то не упоминает что оператор == делает преобразования типов. Пустой массив преобразуется в пустую строку, а пустая строка это false.

                                          С тем же успехом можно удивляться такому коду:
                                          var a = [];
                                          a == ''; // true
                                          a == 0; // true
                                          


                                          с кодом типа if (a) {… } тоже все понятно, хоть a и пустой массив, но все же он существует и условие является истеным.

                                            0
                                            В статье упоминается преобразование типов. Вы невнимательно её читали.
                                            Так как яблоки нельзя сравнивать с жемчугом, когда JavaScript вызывает сравнение объектов разных типов, он выполняет их приведение к общему типу. False, zero, null, undefined, пустые строки и NaN — все приводится к false — не постоянно, а только в контексте данного выражения.
                                              0
                                              а если учесть что toString можно перегрузить — то становится еще веселее.
                                              +2
                                              Да объяснение никакое. Например, такие массивы [0], [«0»], [[[«000000»]]] == false.
                                                0
                                                Ещё можно так приколоться:
                                                alert([] == false); // true
                                                Array.prototype.toString = function() {return "array"};
                                                alert([] == false); // false
                                                

                                                Опять же объяснения в статье недостаточно, чтобы это поведение описать.
                                              +13
                                              Если появляются такие статьи значит не все еще видели ролик WAT
                                              www.youtube.com/watch?v=kXEgk1Hdze0
                                              • НЛО прилетело и опубликовало эту надпись здесь
                                                  0
                                                  В последнем примере с Array(16) ничего необычного, лишь его непонимание.
                                                  Он говорит: «16 запятых». Но там лишь 15 (создаётся массив с 16 элементами (они не объявлены, приводятся к пустой строке) и они объединяются через запятую).
                                                  Array(16).join(«wat») аналогично, пустые строки, объединённые «wat» итого 15 раз «wat»
                                                  «wat» + 1 превращается в «wat1», это понятно.
                                                  «wat» — 1 тоже понятно, что будет NaN, итого 15 раз NaN

                                                  Всё, что до этого, да, прикольно.
                                                  • НЛО прилетело и опубликовало эту надпись здесь
                                                      –1
                                                      Ок, допускаю, что ему не хватало материала на таком же уровне и он добавил это. В примере с Array(16) было явно неверная интерпритация и подача результата публике.
                                                  –1
                                                  js> var someVar = 0;
                                                  js> print(someVar === 0);                                                                                                                 
                                                  true
                                                  
                                                  

                                                    +2
                                                    Мы не читатели, а писатели :) habrahabr.ru/post/171359/#comment_5944667
                                                      0
                                                      В наше время можно ожидать все что угодно — так-что лишний раз проверить не помешает :) Не посмотрел внимательно сразу сбило столку пошел проверять потом забыл, каюсь.

                                                      Jshint/Jslint рулит.
                                                    +8
                                                    Прочитал все, никаких странностей не заметил. Все работает так как описано и как должно работать. У нормального js разработчика тут не должно быть вопросов.
                                                      +30
                                                      Ого. Кажется, такой статьи не было целый месяц. Это существенный прогресс.
                                                        0
                                                        Статья откровенно пустая. Из серии: «10 способов бла-бла-бла». Продвинутые разработчики, для которых эта статья как бы предназначена, уже давно прочитали Фланагана, а там это все есть.
                                                      +8
                                                      в JavaScript каждый объект имеет встроенное булевское значение, которое вызывается, когда объект должен вести себя как булевский
                                                      Честно говоря вообще не понял, что имел в виду Andy, какой-то бред, грубо говоря.
                                                      Насчет оператора сравнения лучше почитать спецификацию ECMA-262 (ред. 3) (стр.55, п.11.9.3, или то же самое в переводе), а не использовать размытые аналогии. По ссылке описан простой и понятный алгоритм работы оператора и расписано приведение типов в каждом случае.

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

                                                      И вообще, таких статей как эта, бесконечно «срывающих покровы» с JavaScript, на хабре довольно много.
                                                      Все работает так как описано и как должно работать.
                                                        0
                                                        Лучше про пятую редакцию читать… es5.github.com/
                                                        +1
                                                        Лично мне в js больше всего удивило:
                                                        parseInt("08") == 0
                                                        

                                                        Что в хроме уже стало «8» =)
                                                          +1
                                                          Где-то я краем глаза читал, что восьмеричную систему в parseInt хотят закопать. Видимо, Хром быстрее всех принялся.
                                                            +1
                                                            ECMAScript вообще не поддерживает литерал восьмеричных чисел, он лишь присутствует в некоторых реализациях. В ES5 strict mode такие литералы явно запрещены.
                                                              0
                                                              Вы забываете указывать систему счисления http://jsfiddle.net/T7FUY/
                                                              0
                                                              в JavaScript каждый объект имеет встроенное булевское значение, которое вызывается, когда объект должен вести себя как булевский; например, когда вы сравниваете его с false.

                                                              Не совсем так.

                                                              Есть в JS т.н. примитивные типы и ссылочные. Для примитивных типов определены операции сложения, вычитания, сравнения и т.д. Для ссылочных — операции типа вызова метода. Точка.

                                                              Тем не менее мы можем в JS делать всякие такие штуки типа:
                                                              var a = 0;
                                                              a.toPrecision(); // вызов метода у примитива
                                                              a.nswer = 42 // движок и не подавится, хотя тут мы вызываем аксессор у примитива
                                                              
                                                              var b = {};
                                                              b == a; // операция сравнения у объекта!


                                                              Что это за вуду такое? А никакой магии тут нет. Есть только автоматический boxing и unboxing (если вы копались в Java и/или C# — наверняка вы слышали эти термины).

                                                              Как только мы обращаемся к примитивному значению, как к оъекту, тут же происходит автоматический боксинг. В примере выше — вызывыется конструктор new Number(a), он возвращает объект, тому объекту в поле под именем «nswer» записывается число 42. К сожалению или счастью, сразу после этого просходит анбоксинг и переменной a возвращается указатель на число 0.

                                                              Несколько интереснее вторая часть кода.
                                                              Любой объект в JS наследует по цепочке прототипов от Object два метода — .valueOf и .toString. Как только объект подвергается операции, применимой только к примитивам (напр., сложение, сравнение и т.д.), то у него просто вызывается метод .valueOf. Если этот метод не является собственным методом данноо объекта, а унаследован по цепочке прототипов, то вызывается метод .toString. Эта проверка продолжает идти вверх по цепочке прототипов, пока ей не встретится собственный метод объекта.

                                                              Fun fact: этим можно пользоваться для своих целей. Например, доопределить метод у Backbone.Collection (var Collection = Backbone.Collection.extend({ valueOf: function () { return !!this.size() } }))

                                                              После этого вступают в силу классические законы сравнения в языках со слабой типизацией. Сиречь: пустая строка и 0 — это false, все остальное — true. (есть еще малость джаваскриптовой специфики, типа "false" == false и "0" == false, но, возможно, тут надо просто внимательно смотреть, что именно возвращает метод .valueOf у строк).

                                                              P.S.
                                                              Чтобы понять, что здесь происходит, вы должны принять концепцию правдочки и кривдочки (truthy and falsy).
                                                              лол. А вообще забавный перевод.
                                                                +1
                                                                Либо «передаваемые по ссылке» и «передаваемые по значению», либо «примитивные типы» и «объекты». «Ссылочные» и «примитивные» — объекты из разных рядов. А анбоксинга в JS просто нет. Создаваемый объект никуда не присваивается, нет этого в коде у вас. Давайте перепишем, чтобы стало понятнее.
                                                                var a = 0;
                                                                Number(a).toPrecision();
                                                                Number(a).nswer = 42
                                                                

                                                                Как видите, создаётся анонимный объект, который нигде не используется и пропадает.
                                                                +1
                                                                Из последнего что меня удивило. Вот такой фокус:
                                                                var a = [3,2,1];
                                                                function F(){};
                                                                F.prototype = a;
                                                                var b = new F();
                                                                

                                                                Получаем интересный такой Array-like объект:
                                                                alert(JSON.stringify(b));
                                                                b[0]++;
                                                                alert(JSON.stringify(b));
                                                                b.push(5);
                                                                alert(JSON.stringify(b));
                                                                b.sort();
                                                                alert(JSON.stringify(b));
                                                                


                                                                Разные движки ведут себя по разному. Хромовский, Огнелисовский IE>=9 честно добавляют свойство length к объекту после вызова push. А старые IE нет. Оперу не проверял.
                                                                  0
                                                                  Вот Вы мне сейчас случайно мозг разрушили…

                                                                  Если я правильно понял, то… получается, что b ведёт себя, как preallocated массив размерности прототипа (в данном случае — a), который при обращении к любому из своих элементов определяет значение этого элемента из прототипа?
                                                                    0
                                                                    Не при обращении, а при изменении. В общем то все в четком соответствии спецификации, но прикольно :-).
                                                                      0
                                                                      Да, пардон, точно… При обращении — возвращает значение из прототипа, но не определяет.
                                                                  –2
                                                                  Вот поэтому Javascript мне не понравился с первого взгляда, когда я впервые с ним познакомился много лет назад. Считаю, что за работу с JS должны доплачивать.
                                                                    +5
                                                                    Насчёт доплачивать я совсем не против, хотя JavaScript очень люблю :-)
                                                                      0
                                                                      Во прям по этому, и с первого взгляда вы все узрели? Считаю, что за работу с JS вам надо переплачивать!
                                                                        0
                                                                        Однозначно! Переплачивать желательно на порядок или два :)
                                                                        +3
                                                                        Как я вас понимаю. Ну не лежит душа к этому языку.
                                                                        Когда JS стал «модным», появилась node.js, я почувствовал себя раздавленным. Боюсь javascript его как огня и тихо ненавижу.
                                                                        Именно «тихо», ведь незнание JS стало моветоном. И даже сейчас я здорово отгребу за эти три строчки.
                                                                          –1
                                                                          Я лично JS считаю прикольным языком, но не удивлюсь, если через несколько лет JS будут воспринимать так же, как сейчас многие воспринимают PHP — как такой «язык для говносайтов». Вот, скажем, Google сделал для JS свой V8 на котором позже сделали node.js. И вот в то же самое время, когда многие вдохновляются node.js, Google, в котором не худшие инженеры собрались, ваяет свой язык для веб-клиентов. А до этого сваял GWT, чтобы можно было делать веб-клиенты на Java. Утешайте себя тем, что в Google тоже есть люди, побаивающиеся Javascript :)
                                                                            0
                                                                            > Именно «тихо», ведь незнание JS стало моветоном

                                                                            Я по-прежнему вижу много JS кода, в котором не то что знания языка не видно, но даже переменные именуются через_нижнее_подчёркивание аНеКакПринятоКэмэлКейсом. А иногда и так, и так.

                                                                            А можно узнать причины вашей ненависти? И к каким языкам душа лежит?
                                                                              0
                                                                              Пишу на PHP, Python, пробовал на вкус Ruby, Java. Ничего не вызывает такой тоски, как JS.
                                                                              Не люблю этот язык, потому что он «другой». Его асинхронность, наследование и легкий налет функциональщины выбивают меня из колеи.
                                                                              Я так надеялся на dart, а он не взлетел.
                                                                          +1
                                                                          Про то что все в JS является объектами это вы погорячились.
                                                                            +12
                                                                            Ваши «правдочки», «кривдочки» смотрятся убого в этой статье.
                                                                              +1
                                                                              Будь переводчиком я, я бы перевел «truthy» и «falsy» как «истинновато» и «ложновато». :)
                                                                              0
                                                                              typeof берет значение из жестко заданной таблице, где сказано что для null возвращать object. Всё — никакой магии.
                                                                                0
                                                                                Вокруг чего ажиотаж при использовании операторов сравнения == и ===? Всё работает вполне предсказуемо.
                                                                                В PHP var_dump(array()==false); //тоже выдаст true. Потому как по значению пустой массив равен false, так же как и 0 и ""(пустая строка). В то время как === сравнивает по типу.
                                                                                  0
                                                                                  Плохо когда тямы не хватает и при этом лезут своими немытыми рукам в чистый и светлый JS

                                                                                  А если серьезно, то язык имеет свою логику, пусть не безупречную. И он вполне подходит для тех целей что язык используется.
                                                                                  Непонимание же логики JS не красит программиста, и если уж он с ним решил разобраться, то чтение стандарта — обязательно и там такие вещи местами расписаны.
                                                                                    0
                                                                                    Это означает, что мы передаем и изолируем текущее значение переменной, защищая его от того, что происходит с настоящей переменной someVar
                                                                                    var someVar = 'hello';
                                                                                    setTimeout((function(someVar) {
                                                                                        return function()  { alert(someVar); }
                                                                                    })(someVar), 1000);
                                                                                    var someVar = 'goodbye';
                                                                                    


                                                                                    тут я бы добавил, что с объектами такая изолированность не сработает:

                                                                                    var someVar = {a: 'hello'};
                                                                                    setTimeout((function(someVar) {
                                                                                        return function()  { alert(someVar.a); }
                                                                                    })(someVar), 1000);
                                                                                    someVar.a = 'goodbye'; 
                                                                                    

                                                                                    выведет goodbye
                                                                                      +1
                                                                                      Что у вас что у автора — ошибка в 4м примере. Проверять бы перед тем как публиковать :)

                                                                                      alert('10 13 21 48 52'.replace(/\d+/g, function(match) {
                                                                                          return parseInt(match) < 30 ? '*' : match;
                                                                                      }));
                                                                                      
                                                                                      • НЛО прилетело и опубликовало эту надпись здесь
                                                                                          0
                                                                                          А у меня выводит lovely undefined, вместо lovely dog:

                                                                                          #!/usr/bin/node
                                                                                          
                                                                                          var alert = console.log;
                                                                                          
                                                                                          var animal = 'dog';
                                                                                          function getAnimal(adjective) { alert(adjective + ' ' + this.animal); }
                                                                                          getAnimal('lovely');
                                                                                          
                                                                                          
                                                                                            0
                                                                                            alert и console.log работают по разному (см. имплементацию например в V8)
                                                                                            используя console.log Вы автоматически меняете контекст.
                                                                                            node.js
                                                                                            //-console.log
                                                                                            //--process.stdout.write
                                                                                            function (data, arg1, arg2) {
                                                                                              var encoding, cb;
                                                                                            
                                                                                              // parse arguments
                                                                                              if (arg1) {
                                                                                                if (typeof arg1 === 'string') {
                                                                                                  encoding = arg1;
                                                                                                  cb = arg2;
                                                                                                } else if (typeof arg1 === 'function') {
                                                                                                  cb = arg1;
                                                                                                } else {
                                                                                                  throw new Error('bad arg');
                                                                                                }
                                                                                              }
                                                                                            
                                                                                              if (typeof data === 'string') {
                                                                                                encoding = (encoding || 'utf8').toLowerCase();
                                                                                                switch (encoding) {
                                                                                                  case 'utf8':
                                                                                                  case 'utf-8':
                                                                                                  case 'ascii':
                                                                                                  case 'ucs2':
                                                                                                  case 'ucs-2':
                                                                                                  case 'utf16le':
                                                                                                  case 'utf-16le':
                                                                                                    // This encoding can be handled in the binding layer.
                                                                                                    break;
                                                                                            
                                                                                                  default:
                                                                                                    data = new Buffer(data, encoding);
                                                                                                }
                                                                                              } else if (!Buffer.isBuffer(data)) {
                                                                                                throw new TypeError('First argument must be a buffer or a string.');
                                                                                              }
                                                                                            
                                                                                              // If we are still connecting, then buffer this for later.
                                                                                              if (this._connecting) {
                                                                                                this._connectQueueSize += data.length;
                                                                                                if (this._connectQueue) {
                                                                                                  this._connectQueue.push([data, encoding, cb]);
                                                                                                } else {
                                                                                                  this._connectQueue = [[data, encoding, cb]];
                                                                                                }
                                                                                                return false;
                                                                                              }
                                                                                            
                                                                                              return this._write(data, encoding, cb);
                                                                                            }
                                                                                            
                                                                                              0
                                                                                              alert же работает как часть DOM в суперглобальном контексте
                                                                                                0
                                                                                                #!/usr/bin/node
                                                                                                
                                                                                                var alert = console.log;
                                                                                                
                                                                                                var animal = 'dog';
                                                                                                function getAnimal(adjective) {
                                                                                                	var txt = adjective + ' ' + this.animal;
                                                                                                	alert(txt);
                                                                                                }
                                                                                                
                                                                                                getAnimal('lovely');
                                                                                                

                                                                                                lovely undefined

                                                                                                  0
                                                                                                  ЧЯДнТ? www.dropmocks.com/iBpi5w
                                                                                                  P.S. node 0.8.16, но на 0.9.* результат такой-же
                                                                                                    0
                                                                                                    Ты делаешь всё интерактивно, а я запускал из скрипта ))
                                                                                                    Node у меня v0.8.21
                                                                                                    Попробовал интерактивно — результат с скриншотом сошелся.
                                                                                                      0
                                                                                                      ОК, не вопрос.
                                                                                                      // test.js
                                                                                                      
                                                                                                      var alert = console.log;
                                                                                                      
                                                                                                      var animal = 'dog';
                                                                                                      function getAnimal(adjective) {
                                                                                                          var txt = adjective + ' ' + this.animal;
                                                                                                          alert(txt);
                                                                                                      }
                                                                                                      
                                                                                                      getAnimal('lovely');
                                                                                                      


                                                                                                      // index.js
                                                                                                      // выполнить весь код в ЕДИНОМ контексте
                                                                                                      eval(require("fs").readFileSync("./test.js").toString())
                                                                                                      


                                                                                                      // launch
                                                                                                      node ./index.js
                                                                                                      


                                                                                                      Результат — lovely dog

                                                                                                      Причину отличного результат можно посмотреть тут /node-0.8.20/src/node.js@551 это связано с изолированностью модулей ноды при загрузке.

                                                                                                      P.S. это не баг — а фитча
                                                                                                        0
                                                                                                        Потестил — эффект носит случайный характер.
                                                                                                        т.е. невозможно захватить глобальный контекст, а когда запускаем из консоли — то работает VM context модуля REPL.
                                                                                              0
                                                                                              При всем желании увидеть тут странность, и понимании что автор имел ввиду под словом странность, обнаружил её только в нескольких пунктах.

                                                                                              3. Массив без ключей == ложь (или о правдочке и кривдочке)
                                                                                              В чем тут странность? Разьве что в желании сравнить массив и логическое значение. И кто после этого странный? :)

                                                                                              4. Метод replace() может принимать callback функцию
                                                                                              5. Регулярные выражения — больше чем найти и заменить
                                                                                              6. Вы можете подделать контекст
                                                                                              7. Функции могут вызывать сами себя
                                                                                              Вот уж до чего техника дошла… А вообще вы тут просто описали поведение языка.

                                                                                              8. Firefox читает и возвращает цвета в RGB, не в HEX
                                                                                              А это вообще не javascript.

                                                                                              10. Неопределенное может быть определено.
                                                                                              Причинно следственная связь в другую сторону, ога.

                                                                                              Из всех пунктов у меня набралось странных только 3: 1,2 и 9 — вот тут действительно можно удивиться. Интересно было бы услышать ожидаемый результат раз автор считает это необычным.
                                                                                                0
                                                                                                При чём 2 и 9 — это вообще не особенность JS
                                                                                                  +1
                                                                                                  К слову, есть языки вроде smalltalk'а, где деление одного Integer'а на другой даёт отличный от Float'а тип данных — Fraction, т.е. дробь и хнанит в себе пару Integer'ов, что позволяет с лёгкостью сравнивать 1/10 + 2/10 == 3/10, однако же 0.1 это уже не Fraction, а Float.
                                                                                                –1
                                                                                                К статье добавлю еще пару приколов Javascript:

                                                                                                {} + [] = 0
                                                                                                [] + {} = [object Object]
                                                                                                [] + [] = пустая строка
                                                                                                {} + {} = NaN
                                                                                                
                                                                                                  0
                                                                                                  не везде так и зависит от движка.
                                                                                                    0
                                                                                                    Где не так?
                                                                                                      0
                                                                                                      Например в том-же node.js — если Вы исполняете в изолированном контексте — то будет как ожилалось, но а если нет — то нет.
                                                                                                        0
                                                                                                        $ node
                                                                                                        > {} + []
                                                                                                        '[object Object]'
                                                                                                        > [] + {}
                                                                                                        '[object Object]'
                                                                                                        > [] + []
                                                                                                        ''
                                                                                                        > {} + {}
                                                                                                        '[object Object][object Object]' 
                                                                                                        

                                                                                                        $ node --version
                                                                                                        v0.8.14
                                                                                                    +2
                                                                                                    1. Типичная ошибка при конструировании regexp через строку
                                                                                                    new RegExp('\b'+word+'\b', 'ig'); // не правильно, получим \x08word\x08
                                                                                                    new RegExp('\\b'+word+'\\b', 'ig'); // правильно так, получим \bword\b
                                                                                                    

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

                                                                                                    2. Для данного вида конструкций существует другой термин — Immediately-Invoked Function Expression (IIFE) , не понятно зачем нужно было изобретать собственный. К тому же множество вариаций записи и их назначение совсем в другом, нежели предлагает автор — он всего навсего создает замыкание. Но конструкции такого типа в основном используются для создания области видимости для некоторого объекта (объектов), часто для модулей, чтобы скрыть часть внутренней логики.
                                                                                                    3. Не понятно зачем в статье про javascript 8 пункт — это особенности DOM, но никак не javascript.
                                                                                                    4. 9-й пункт справедлив практически для любых языков, это особенности архитектуры процессоров, в частности хранения чисел с плавающей точкой.

                                                                                                    Такое ощущение, что на 10 пунктов автору не хватило — и он начал высасывать из пальца. Очень сомнительна его квалификация.
                                                                                                    По существу, ничего секретного в описанном нет. Это базовые вещи языка. Без их знания (за вычетом пары тройки пунктов) нельзя считаться продвинутым в js, да и вообще программировать на нем.
                                                                                                      –2
                                                                                                      Всем привет :)
                                                                                                      var x = 'fuck';
                                                                                                      setTimeout( function( a, b ) {
                                                                                                        console.log( a, b );
                                                                                                      }, 100, x, 'yeah' );
                                                                                                      x = 'stuck';
                                                                                                      
                                                                                                        –1
                                                                                                        и что?
                                                                                                          0
                                                                                                          И то, что setTimeout всегда оборачивают в функцию или передают первым аргументом функцию, возвращающую другую функцию для того, чтоб значение не менялось.
                                                                                                            0
                                                                                                            Вот только это не секрет и не странность, а особенность языка в полном соответствии стандартам.
                                                                                                            JS — он ВЕСЬ такой… За что его и люблю. :)
                                                                                                              0
                                                                                                              Угу, просто хотел отметить, что setTimeout в нормальных браузерах еще круче :)
                                                                                                                –1
                                                                                                                Она работает точно так же, как bind, не вижу ничего удивительного. Им, кстати, можно заменить IIFE иногда:

                                                                                                                for (var i = 0; i < nodes.length; i++) {
                                                                                                                  (function (i) {
                                                                                                                    node[i].onclick = function (e) {
                                                                                                                        alert(i);
                                                                                                                    }
                                                                                                                  })(i);
                                                                                                                }
                                                                                                                
                                                                                                                // =>
                                                                                                                
                                                                                                                for (var i = 0; i < nodes.length; i++) {
                                                                                                                  node[i].onclick = function (i, e) {
                                                                                                                      alert(i);
                                                                                                                  }.bind(i);
                                                                                                                }
                                                                                                                


                                                                                                                Мне непонятно, что вас удивило. Вполне ожидаемое поведение.
                                                                                                                  0
                                                                                                                  Очепятка в bind'е. Пропущен контекст.
                                                                                                                  for (var i = 0; i < nodes.length; i++) {
                                                                                                                    node[i].onclick = function (i, e) {
                                                                                                                        alert(i);
                                                                                                                    }.bind( node[i], i);
                                                                                                                  }
                                                                                                                  
                                                                                                                    0
                                                                                                                    Вы правы. Но идею я передал)
                                                                                                        0
                                                                                                        У меня одного код
                                                                                                         x = 2-0.14;
                                                                                                                alert(x);
                                                                                                        

                                                                                                        выдает 1.85(9) вместо ожидаемого 1.86?
                                                                                                          0
                                                                                                          Нет это у всех и не только в джаваскрипте, а по сути по всех ЯП, думаю объяснение должно быть где-то тут.

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

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