JavaScript - один из самых популярных языков программирования. Несколько лет назад про него говорили, что он ночной кошмар программиста, который работает с ним. В некоторых случаях это утверждение было правдой. Однако большей частью эти высказывания относились к DOM API. Тем не менее существует несколько явных ошибок языка.
Данная статья имеет целью предупредить читателя о некоторых недостатках JavaScript.
1. Название. JavaScript - это не Java.
Начнем с шутливого пинка по названию. Изначально оно звучало как Mocha, затем LiveScript, и, наконец, изменилось на JavaScript. В соответствии с историческими фактами, схожесть с названием Java стала результатом соглашения между Netscape и Sun, в обмен на согласие Netscape встроить поддержку Java в их тогда популярный браузер. Стоит также отметить, что название пришло почти как шутка благодаря соперничеству между LiveScript и Java в сфере скриптов на стороне клиента.
Тем не менее, в форумах присутствуют тысячи сообщений на тему “JavaScript не имеет ничего общего с Java”!
2. Null - это объект?
Попробуйте ввести в консоль JavaScript:
console.log(typeof null); // Возвращает: object
Утверждение не имеет смысла. Если null означает отсутствие значения, то как оно может иметь тип object? Простой ответ на поставленный вопрос: это обычная ошибка, которую сделали во время разработки первого релиза JavaScript. Она была перенесена из JScript.
3. NaN !== NaN
Мы можем предположить, что NaN ссылается на значение, которое не является разрешенным числом. Проблема заключается в том, что NaN ничему не равно, в том числе и самому себе.
console.log(NaN === NaN); // Врозвращает false
Если Вы хотите проверить, имеет ли переменная значение Nan, то нужно использовать функцию isNaN().
4. Глобальные переменные.
Зависимость от глобальных переменных хорошо известна как одна из худших особенностей JavaScript. Для простых проектов, таких как всплывающие подсказки, их действие не заметно. Однако, основная нагрузка на глобальные переменные ложиться тогда, когда Вы начинаете ссылаться на многочисленные скрипты, без знанния внутренней структуры. И вот здесь программа начинает выдавать ошибки одну за другой по причине совпадения имен.
5. Строка userAgent содержит Mozilla. Интересно почему?
Данный факт не являектся ошибкой JavaScript. Все происходит из-за производителей браузеров. Использование строки userAgent является распространенной практикой в JavaScript, так как очень важно знать, с чем приходится иметь дело. Например, если в браузере Safari ввести в консоли JavaScript команду:
console.log(navigator.userAgent);
// Вывод на экран: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; en-us) AppleWebKit/531.21.8 (KHTML, like Gecko) Version/4.0.4 Safari/531.21.10
Заметьте: в начале строки стоит: Mozilla/5.0. Почему же браузер Safari идентифицируется как основанный на Mozilla? Хотя в дальнейшем происходит коррекция идентификации, нет объяснений, почему продолжается введение в заблуждение программистов. В действительности, Вы можете обнаружить, что большинство браузеров идентифицируют себя как Mozilla. Ответ на вопрос, почему так происходит, можно дать взлянув на десять лет назад. И основной причиной является не ошибка, а ряд непреодолимых обстоятельств.
Для тех, кто не знает, строка userAgent просто идентифицирует браузер и его версию. Например, первый браузер Mosaic имеет такую строку userAgent:
Mosaic/0.9 // имя браузера / номер версии
Смысл прослеживается очень четко. И когда Netscape вышло на рынок, они использовали формат Mosaic и добавили секцию кодировки.
Mozilla/2.02 [en] (Win95; I) // имя браузера / версия / кодировка
Продолжение отличное. Проблемы начались, когда на рынке появился новый игрок - догадайтесь какой - Internet Explorer 3. Когда он стартовал, Netscape был самым популярным доступным браузером. В действительности, много серверов и программ использовали строку userAgent для идентификации Netscape. Хотя сейчас это вызывает горячие споры, но тогда, если бы IE использовало свою собственную строку userAgent, например такую:
MSIE/3.0 (Win95; U)
Такой ход поставил бы IE 3 в очень ущербное положение, потому что Netscape уже идентифицировался большинством серверов. Поэтому разработчики решили идентифицировать продукт Microsoft как браузер Mozilla, а потом добавить метки для Internet Explorer.
Mozilla/2.0 (compatible; MSIE 3.0; Windows 95)
В наши дни детекция с помощью строки userAgent стоит последней в списке задач. Поэтому отношение к ней соответствующее. Вы можете обнаружить, что большинство браузеров следует традиции IE идентифицировать себя как Mozilla, и причиной тому является цепная реакция разработчиков.
6. Несоответствие области видимости
Расмотрим следующий код:
// Создаем функцию, которая вызывает другую функцию с именем еквивалентным параметру fn.
function foo(fn) {
if (typeof fn === "function") {
fn();
}
}
// Создает объект со свойством и методом.
var bar = {
barbar : "Всем привет!",
method : function() {
alert(this.barbar);
}
};
bar.method(); // Выводит "Всем привет!"
foo(bar.method); // Если мы вызываем функцию foo и передаем метод "bar.method", будет выдано сообщение об ошибке "undefined."
foo(function() { bar.method(); }); // Выводит "Всем привет!"
Причина того, что foo(bar.method) не выводит такой же результат, заключается в том, что функция метода вызывается как метод объекта окна, а не bar. Для того, чтобы исправить положение мы должны вызвать bar.method() через вызов анонимной функции.
7. Использование побитовых операторов.
JavaScript имеет много похожего на Java. В том числе и побитовые операторы:
& - И
| - ИЛИ
^ - исключающее ИЛИ
~ - НЕ
> - знаковый сдвиг вправо
??? - беззнаковый сдвиг вправо
<< - сдвиг влево
Рассмотрим первый оператор &. Гораздо более эффективно будет использовать оператор &&, так как он будет выполняться быстрее. Так происходит потому, что JavaScript - это не Java, и у него нет типа integer. Поэтому процессы с относительной длиной требуют операции преобразования для операндов, выполняют операцию, а затем происходит обратное преобразование к исходному типу.
Поэтому вместо использования & как "И", и | как "ИЛИ" следует использовать && и ||.
8. Слишком много логических значений false
Может быть это не является ошибкой JavaScript, но очень затрудняет процесс изучения языка. Такие значения как null, false, и undefined почти всегда обозначают одно и тоже, но между ними существует разница, которая вызывает затруднение в понимании.
!!(0); // false
!!(false); // false
!!(''); // false
!!(null); // false
!!(undefined); // false
!!(NaN); // false
Обратите внимание, что все остальные значения обозначают true.
9. Он делает ошибки в арифметических операциях.
JavaScript имеет несколько особенностей при работе десятичными числами. Для примера, откройте консоль, и введите console.log(.2 + .4). Ожидается, что будет выведено ".6", не так ли? Так, но не совсем!
console.log(.2 + .4); // 0.6000000000000001
Как такое происходит? Причина в том, что JavaScript использует стандарт IEEE для арифметики двоичных чисел с плавающей десятичной точкой. Механизм достаточно сложный для объяснения в данной статье, но когда происходит работа с десятичными дробями, результат может немного отличаться от того, что Вы ожидаете. Помните, что целочисленная арифметика точна, и таким образом можно избежать влияния данного пункта на результат работы программы.
10. Выбор стиля кодирования не зависит от Вас!
Когда дело доходит до кодирования, то начинается использование своего собственного стиля. Многие программисты предпочитают ставить скобочки на одной строке с оператором, другие предпочитают переносить на следующую строку:
// Скобочки на одной строке справа
return {
foo : bar
};
// Скобочки на новой строке
return
{
foo : bar
};
В зависимости от того, какую книжку про программирование мы прочитали первой, или от того, что сказал учитель, Вы предпочитаете какой-либо из методов приведенных выше, или оба сразу. Однако в JavaScript это не будет зависеть от Вас!
Рассмотрим приведенные выше выражения. Верите Вы или нет, но они НЕ идентичны. Добавьте следующие строки на какую-нибудь страницу HTML.
var foo = function() {
return {
a : 'b'
};
}();
alert(foo.a); // b
Код просто создает переменную foo, которой присваивается возвращаемый объект. Когда мы вызываем alert(foo.a), то ожидаем увидеть окно сообщения со значением b. Теперь сдвинем открывающуюся скобку от выражения return на одну строку вниз:
return
{
a : 'b'
};
Если Вы запустите страницу в браузере снова, то получитие в окне сообщений инструмента разработчика сообщение об ошибке, такое как "foo is undefined".” И в чем дело!?
Почему JavaScript так реагирует? Потому, что существует нечто, называемое "вставкой точки с запятой". В действительности JavaScript пытается корректировать наш плохой код. Здесь он считает, что Вы пропустили закрывающую точку с запятой, поэтому переходит вперед и добавляет ее для Вас. Хотя в оригинале данное свойство задумывалось как помощь в кодировании, особенно начинающим программистам, в действительности это очень плохая штука, как было продемонстрировано выше.
В нашем примере нет возможности определить, почему foo.a возвращает "undefined". Теперь, когда мы знаем про вставку точки с запятой, причина становится понятной - JavaScript добавил точку с запятой после выражения return.
return; // JS ошибочно добавляет здесь точку с запятой.
{
a : 'b'; // Здесь также будет добавлена точка с запятой, так как JS не распознает это как объект.
};
Надеюсь, что урок был полезен для Вас!