Статьи

Топ 10 вещей, которые JavaScript получил неправильно

JavaScript, если только по умолчанию, является одним из самых популярных доступных языков программирования. С годами это было помечено как кошмар для работы, и, в некоторой степени, это правда! Однако чаще всего люди хотят сказать, что DOM API — это кошмар. Тем не менее, есть несколько ошибок в языке.

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

Начнем с забавного джеба по выбору имени. Хотя изначально он назывался Mocha, а затем LiveScript, позднее он был изменен на JavaScript. Согласно истории, его сходство с именем Java было результатом сотрудничества между Netscape и Sun в обмен на Netscape, объединяющую среду выполнения Java в их популярном браузере. Также было отмечено, что название пришло, почти как шутка, из-за соперничества между LiveScript и Java для сценариев на стороне клиента.

Тем не менее, на форумах в Интернете тысячи комментариев «JavaScript не имеет ничего общего с Java»!

Учти это…

1
console.log(typeof null);

Это имеет нулевой смысл. Если нулевым является отсутствие значения, то как его тип может быть «объектом»? Простой ответ заключается в том, что это ошибка, относящаяся к первой версии JavaScript — ошибка, которая даже была неправильно перенесена в JScript от Microsoft.

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

1
console.log(NaN === NaN);

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

Обновление: после прочтения некоторых блестящих комментариев, особенно тех, которые касаются NaN, похожего на бесконечность, становится совершенно ясно, что NaN не будет равным самому себе. Но это все еще может сбивать с толку. Обратитесь к комментариям для более глубокого обсуждения этого!

Зависимость от глобальных переменных широко считается самой худшей частью JavaScript. Для простых проектов, как быстрые советы на этом сайте, это не имеет никакого значения. Однако реальное бремя глобальных переменных вступает в игру, когда вы начинаете ссылаться на несколько сценариев, не зная, как они созданы или названы. Если они имеют одинаковое имя с одной из ваших переменных, ваша программа выдаст какую-то ошибку.

«Проблема с JavaScript заключается не только в том, что он позволяет им (глобальные переменные), но и требует их». — Крокфорд

Хорошо — это не ошибка JavaScript. Я немного обманул. Это из-за поставщиков браузеров. Сказав это, обнаружение строки пользовательского агента очень распространено в JavaScript; поэтому важно знать, с чем вы имеете дело. Вероятно, он не входит в этот список, но кого это волнует! Это хорошо знать.

Это не столько ошибка, сколько неизбежное решение. Например, откройте Safari, получите доступ к веб-инспектору и зарегистрируйте строку пользовательского агента в консоли.

1
2
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. Ответ уходит в прошлое на десятилетие, и, опять же, это не ошибка, а скорее неизбежное обстоятельство.

Для тех, кто не знаком, строка user-agent просто предназначена для идентификации браузера и его версии. Например, первый браузер, Mosaic, имел строку user-agent, которая выглядела так:

1
Mosaic/0.9 // browser name / version number

Это имеет смысл. И когда Netscape вышел на сцену, они сохранили использование Mosaic, а также добавили раздел типа шифрования.

1
Mozilla/2.02 [en] (Win95; I) // browser name / version / encryption

Все идет нормально. Проблемы вступили в игру, когда — подожди — Internet Explorer 3 был выпущен. Имейте в виду, что при запуске Netscape был самым популярным из доступных браузеров. Фактически, многие серверы и программы уже внедрили обнаружение агента пользователя, чтобы идентифицировать Netscape. Хотя сегодня это очень обсуждаемая тема, тогда это не было большой проблемой. Если бы IE использовал свою собственную строку user-agent, это выглядело бы примерно так:

1
MSIE/3.0 (Win95; U)

Это оставило бы их в невыгодном положении , потому что Netscape уже был идентифицирован многими серверами. Таким образом, разработчики решили неправильно идентифицировать браузер как Mozilla, а затем добавить дополнительный набор информации, помечая его как Internet Explorer.

1
Mozilla/2.0 (compatible; MSIE 3.0; Windows 95)

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

Я настоятельно рекомендую вам прочитать « Историю строки User-Agent » Николаса Закаса, если вы хотите глубже вникнуть.

Рассмотрим следующий код:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
// Create a function that will call a function with the name equal to parameter fn.
function foo(fn) {
    if (typeof fn === «function») {
        fn();
    }
}
 
// Create an object with a property and a method.
var bar = {
    barbar : «Hello, World!»,
    method : function() {
        alert(this.barbar);
    }
};
 
bar.method();
foo(bar.method);
foo(function() { bar.method(); });

Причина, по которой foo (bar.method) не отображает тот же результат, заключается в том, что функция метода будет вызываться как метод объекта окна, а не как bar. Чтобы это исправить, мы должны вызвать bar.method () из переданной анонимной функции.

Большое спасибо Джереми Макпику за сообщение об этой ошибке.

JavaScript имеет много общего с Java, одним из которых является набор побитовых операторов.

  • &и
  • | или
  • ^xor
  • ~нет
  • >>подписанный сдвиг вправо
  • ??? беззнаковое смещение вправо
  • <<сдвиг влево

Рассмотрим первый пункт &; было бы намного эффективнее использовать оператор &&, так как это быстрее. Это связано с тем, что JavaScript отличается от Java и не имеет целых чисел. Таким образом, для преобразования операнда, обработки чего-либо, а затем преобразования его обратно требуется относительно длительный процесс.

Вот почему вы можете избежать использования & для «и», и | для «или» — даже если вы должны использовать && и ||.

Возможно, это не ошибка в JavaScript, но, безусловно, делает процесс обучения, особенно для начинающих, сложным. Значения типа null, false и undefined почти означают одно и то же, но есть различия, которые могут быть запутанными для понимания.

Чтобы проверить, откройте консоль в Firefox и найдите логическое значение следующих элементов.

1
2
3
4
5
6
!!(0);
!!(false);
!!(»);
!!(null);
!!(undefined);
!!(NaN);

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

Больше, чем ошибка, это множество ложных значений просто сбивает с толку!

Хорошо, хорошо — я на 99% дразню с заголовком выше. Но у JavaScript действительно есть несколько незначительных проблем при работе с десятичными знаками, например, такие как денежные транзакции. Например, откройте консоль и войдите «.2 + .4». Мы ожидаем, что он покажет «.6», правильно? Ну, это так, и это не так!

математический
1
console.log(.2 + .4);

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

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

01
02
03
04
05
06
07
08
09
10
// braces on the right
return {
  foo : bar
};
 
// braces on their own line
return
{
  foo : bar
};

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

Я узнал этот конкретный пример из лекции, которую Дуг Крокфорд дал около года назад. Рассмотрим обратное утверждение сверху. Верьте или нет, они не равны. Не веришь мне? Попробуйте это. Добавьте следующее к некоторой HTML-странице.

1
2
3
4
5
6
7
8
9
var foo = function() {
     
    return {
        a : ‘b’
    };
     
}();
 
alert(foo.a);

Приведенный выше код просто создает переменную с именем foo, которая равна возвращаемому объекту. Когда мы предупреждаем (foo.a), мы, как и ожидалось, видим окно оповещения со значением «b». Теперь просто возьмите эту открывающую фигурную скобку из оператора return и вставьте ее в свою собственную строку, вот так.

1
2
3
4
return
{
    a : ‘b’
};

Если вы снова запустите его в своем браузере, вы получите ошибку Firebug, сообщающую, что «foo не определено». Что за черт!? 🙂

Так почему же JavaScript делает это? Это из-за того, что называется «точка с запятой». По сути, JavaScript попытается исправить наше плохое кодирование. Если, например, он думает, что вы остановили точку с запятой, он пойдет дальше и добавит ее для вас. Хотя изначально это было задумано для удобства, особенно для новых JavaScripters, на самом деле это очень плохо, когда у вас нет контроля над собственным кодом, как показано выше.

В нашем примере нет способа определить, почему foo.a возвращает «undefined». Теперь, когда мы знаем о вставке точек с запятой, причина, по которой он не определен, заключается в том, что JavaScript добавит точку с запятой в конец оператора return.

1
2
3
4
return;
{
    a : ‘b’;
};

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

Как я упоминал в начале этой статьи, я люблю JavaScript и использую его ежедневно. Но это не значит, что в языке нет действительно ужасных ошибок. Я хотел бы услышать ваши мысли в комментариях! Спасибо за прочтение. Ретвиты и Diggs всегда приветствуются! Огромное спасибо Джереми МакПику , Дагу Крокфорду , Николасу Закасу и Джону Резигу : я ссылался на ваши учебные пособия и книги при подготовке этой статьи.

  • Подпишитесь на нас в Твиттере или подпишитесь на ленту Nettuts + RSS для получения лучших учебных материалов по веб-разработке.