Хм. @BenLaden read-only
Пользователь
14 июня 2011 в 17:00

Разработка → Топ-11 самых частых ошибок в JavaScript из песочницы

JavaScript — относительно простой язык в изучении. Однако, ошибок в нем допускается более чем достаточно. Вы уверены, что не допускаете их? Сегодня мы рассмотрим 11 самых распространенных ошибок.

Ошибка 1 — Использование глобальных переменных


Если вы только знакомитесь с JavaScript, вы, вероятно, думаете, что это отлично, когда все переменные — глобальные. На самом деле, вы можете не знать всех тонкостей этого инструмента. Глобальные переменные — переменные, которые доступны из любого участка кода, даже если они загружены в разные .js-файлы. Звучит заманчиво, не правда ли? Любая переменная всегда доступна для изменения.

На самом деле, нет.

Это плохая идея, поскольку вы можете перезаписать значения непреднамеренно. Допустим, у вас есть интернет-магазин, и вы используете JavaScript для подсчета суммы цен товаров, добавленных в корзину. Вот пример кода:
var total = 0,    // конечные счет
    tax   = 0.05; // 5%

Теперь, допустим, вы используете код для отображения твитов на странице, или сделали мини-галерею ваших продуктов. И там может содержаться код вроде этого:
var total = 15; // кол-во твитов из twitter

Или,
var tax = function () { /* ... */ }; // Стартер для анимации

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


Так в чем же решение? Одним словом, инкапсуляция; но есть множество других способов избежать этого. Во-первых, вы можете записать весь код в виде само вызова, анонимные функции:
(function () {  
    var total = 0, tax = 0.05;  
  
    // какой-то код  
}());  

И никакой код извне не доберется до кода внутри функции. Это отлично работает для «личного» кода, но это не дает той функциональности, которая нам нужна. Например, если мы хотим корзину-счетчик, которую другие могли бы использовать как шаблон, было бы неплохо сделать вот так:
var cartTotaler = (function () {  
    var total = 0; tax = 0.05;  
  
    // еще код  
  
    return {  
      addItem : function (item) { },  
      removeItem : function (item) { },  
      calculateTitle : function () { }  
    };  
}());  

Еще чуть-чуть о глобальных переменных: заметим, что если вы не используете ключевое слово var при создании переменной, JavaScript-движок определяет её как глобальную по умолчанию. Например:
 (function () {  
  tax = 0.05;  
}());  
  
var totalPrice = 100 + (100 * tax); // 105 

Переменная tax доступна за пределами нашей функции, поскольку не было использовано слово var при её объявлении. Следите за этим.

Ошибка 2 — Забыли о точке с запятой


Любой оператор в JavaScript должен заканчиваться точкой с запятой. Это очень просто. Если вы забудете, то компилятор сделает дело за вас. Значит, можно их не ставить?

Ну, в некоторых местах это жизненно необходимо. Например, если в теле цикла у вас выполняется несколько операторов подряд. Иначе компилятор выдаст ошибку. А как насчет конца строки?

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

Например, такая простая функция:
function returnPerson (name) {  
    return  
    {  
        name : name  
    };  
}  

Кажется, что он должен вернуть маленький миленький объект… но компилятор решит, что вы хотели поставить после return точку с запятой, поэтому ничего не вернется, а объект будет проигнорирован. Вы должны сделать следующее:
return {  
    name : name  
}; 

Так что, мой вам совет — ставьте точки с запятыми; честно, это входит в привычку довольно быстро. Однако, как веб-разработчик, вы, вероятно, используете другие языки (например, PHP), где точки с запятыми ставить не нужно. Тогда, зачем?

Замечание:Если вы не знаете о каждой ситуации, где разрешается опускать, лучше не рисковать.

Ошибка 3 — Использование ==


Если вы сейчас спросите первого попавшегося JavaScript-разработчика:«Какая самая распространенная ошибка в JavaScript?», скорее всего, он/она ответит:«Использование == вместо ===». Что это значит?

Попробуйте это:
if (1 == 1) {  
    console.log("Правда!");  
}  

Вы ожидали, что код заработает, так? Хорошо, теперь попробуйте следующее:
if (1 == '1') {  
    console.log("Правда!");  
}  

Да, у тебя в консоли выскочило «Правда!»… и да, это плохо. Происходит здесь следующее, == — оператор приравнивания. Он делает две переменные максимально схожими. В нашем случае происходит преобразование строки «1» в число 1, и наш if-оператор выдает true.

Решение, как вы поняли, заключается в использовании ===; Все это годно и в случае с операторами !== и !=.

А теперь, для веселья, несколько самых лучших ошибок, получившихся из-за двойного равенства:
''         == '0' // false  
'0'        == ''  // true  
false      == '0' // true  
' \t\r\n ' == 0   // true 

Ошибка 4 — использование объектов оборачивающих типы


JavaScript любезно(!) дает нам несколько оберток типов для легкого(!) создания типов.
new Number(10);  
new String("Привет!");  
new Boolean(true);  
new Object();  
new Array("один", "два", "три");  

Во-первых, это супер неудобно. Все это можно делать и при значительно меньшем числе нажатий клавиш.
10;  
"Привет";  
true;  
{};  
["один", "два", "три"];  

Но, подождите, это еще не все: эти две вещи не совсем одинаковы. Вот что говорит Дуглас Крокфорд* по этому вопросу:

Например, new Boolean (false) производит объект, который имеет valueOf метод, который возвращает завернутое значение.
— JavaScript: The Good Parts, стр. 114


Это значит, что если вы запустите typeof new Number(10) или typeof new String(«Привет!»), то получите 'object' — не такой какой хотели. Плюс, использование оболочки может вызвать неожиданную реакцию.

Так почему же JavaScript дает нам эти объекты? Для внутреннего пользования. примитивные значения не имеют методов (так как они не являются объектами); так, при вызове метода на примитивный объект (Например, «Привет люди!».replace(«люди», «хабралюди»)), JavaScript создает оболочку для строки, делает дело, а затем отбрасывает объект.

Оставьте обертку для JavaScript и используйте примитивные значения.

Ошибка 5 — Нет проверки атрибутов при использовании For-In


Все мы знакомы с перебором массивов; однако, возможно, вам понадобилось перебрать свойства объекта. (Отступление. На самом деле, элементы массива — пронумерованные свойства одного объекта.) Если вы не делали этого раньше, то использовали For-In цикл:
  var prop, obj = { name: "Вася", job: "Тракторист", age: 55 };  
  
for (var prop in obj) {  
  console.log(prop + ": " + obj[prop]);  
}

Если вы запустите этот код, вы должны увидеть следующий вывод:
name: Вася 
job: Тракторист
age: 55

Однако, браузер будет включать в себя свойства и методы дальше по цепочке. Скорее всего, вы не захотите, что бы свойства эти перечислялись. Вы должны использовать hasOwnProperties, что бы отфильтровать свойства, которые не являются объектами:
Function Dog (name) {  
    this.name = name;  
}  
Dog.prototype.legs = 4;  
Dog.prototype.speak = function () {  
    return "Гав!";  
};  
  
var d = new Dog("Тузик");  
  
for (var prop in d) {  
    console.log( prop + ": " + d[prop] );  
}  
  
console.log("=====");  
  
for (var prop in d) {  
  if (d.hasOwnProperty(prop)) {  
    console.log( prop + ": " + d[prop] );  
  }  
}  
  
// Output  
  
// name: Тузик  
// legs: 4  
// speak: function () {  
        return "Гав!";  
// }  
// =====  
// name: Тузик  

Иногда вам нужны свойства, но вы хотите отфильтровать некоторые методы. Это можно сделать с помощью typeof:
for (var prop in d) {  
  if (typeof d[prop] !== 'function') {  
    console.log( prop + ": " + d[prop] );  
  }  
}

В любом случае, всегда for-in выражения ясными, что бы избежать нежелательных результатов.

Ошибка 6 — Использование with или eval


К счастью, большинство источников сегодня не учат with или eval. Но если вы используете старые или не очень авторитетные источники (иногда хороший материал, действительно, трудно найти в интернете), вы могли бы найти там with или eval и использовать их. Ужасное начало, разработчик.

Итак, начнем с with. Две основные причины, по которым нельзя использовать его:

  1. Он замедляет ваш код
  2. Не всегда понятно, что вы делаете


Пункт первый стоит на своем. Так что давайте взглянем на секунду. Вот как with работает: вы отправляете какой-либо объект в with-выражение; затем, внутри блока with, вы можете получить доступ к свойствам объекта как к переменным:
var person = { name: "Петя", age : 10 };  
  
with (person) {  
  console.log(name); //Петя
  console.log(age);  // 10
}  

Но что если у нас есть переменная с таким же именем как свойство объекта with? В принципе, если есть и то и другое, переменная будет использоваться. Но вы не сможете добавить свойства объекту, находящемуся внутри with. Если нет свойства или переменная существует, то это сможет сделать переменная извне выражения with:
var person = { name: "Петя", age : 10 },  
    name = "Жора";  
  
with (person) {  
  console.log(name); // Жора
  job = "Дизайнер";  
}   
  
console.log(person.job); // неопределенно;  
console.log(job); // Дизайнер

Перейдем к eval. В двух словах, вы можете передать строку кода функции, и она будет её выполнять.
eval( "Console.log('Привет!');" );

Звучит безобидно, даже мощно, так? На самом деле, это и есть главная проблема — слишком большая мощь. Очевидно, нет причин писать строки так, потому что 1) почему бы не писать по-простому? И 2) eval замедляет работу также как и with. Таким образом, основное применение eval — выполнять код, который вам не нужен в данный момент. Вы могли бы получить это от сервера или напрямую от пользователя. Вы действительно хотите предоставить пользователям сайта полный контроль вашего кода? Надеюсь, что нет. Кроме того, он открывает свой ​​сайт для хакеров: использование EVAL — знак, который говорит:«Я в отъезде, и ключ под ковриком.» Если вы любите себя или ваших пользователей: не используйте его.

Ошибка 7 — Забываем об использовании систем счисления при использовании parseInt


JavaScript дает нам маленькую функцию, которая помогает преобразовать строку, содержащую число в число:
parseInt("200"); // 200  
parseInt("043"); // 35  

И что же там произошло? Разве во втором примере не должно быть 43? На самом деле, parseInt работает не только с десятичными системами. Когда он видит строку, начинающуюся с 0, то он считает её восьмеричным числом. Поэтому нельзя забывать о системах счисления; они говорят функции о базовом числе.
parseInt("020", 10); // 20
parseInt("100", 2);  // 4


Ошибка 8 — Не используются скобки при употреблении операторов if и while


Одна из заслуг JavaScript — гибкость. Но иногда она может обернуться против вас. Такое происходит в случае с фигурными скобками в составных операторах if и while. Скобки являются необязательными, если в операторе только одна строка кода:
if (true)  
  console.log("внутри оператора if");  

Это очень удобно, ведь можно продолжать операторы на той же строке:
 var arr = ["раз", "два", "три", "четыре", "пять", "шесть", "семь", "восемь", "девять", "десять"],  
    i   = arr.length - i;  
  
while (i) console.log( arr[i--] );  

Но это не разумно по нескольким причинам. Во-первых, такой прием оставляет неясности:
if (true)  
  console.log("В операторе if");  
  console.log("Вне оператора if");  

Посмотрите, что я имею в виду? Вторая линия не в операторе, но выглядит она очень правдоподобно. Скобки внесут здесь ясность. Кроме того, если вы хотите добавить строку в if, не забудьте скобки. Лучше добавьте их сразу, это гораздо проще. Сделайте это.

Ошибка 9 — добавление элементов в DOM поодиночке


Да, да, это не JavaScript. Но в 99 случаях из 100 в JavaScript затрагивается DOM. Хотя есть много ошибок, которые вы можете допустить в DOM, здесь самая большая.

Вставлять элемент DOM с помощью JavaScript весело и полезно, но, к сожалению, грузит страницу. Поэтому вставлять много элементов DOM один за одним — плохая идея:
var list = document.getElementById("list"),  
    items = ["раз", "два", "три", "четыре"],  
    el;  
  
for (var i = 0; items[i]; i++) {  
  el = document.createElement("li");  
  el.appendChild( document.createTextNode(items[i]) );  
  list.appendChild(el); // очень плохая идея 
}    

Вот что вы должны сделать вместо этого: используйте фрагменты документа. Фрагменты документа — контейнеры для сохранения элементов DOM; тогда, вместо раздельной вставки, можно сделать все одним махом. Фрагмент документа сам по себе не является узлом, и ничего не будет, если показать его в объектной модели. Она будет невидимой сетью для проведения элементов перед заложением их DOM. Вот пример:
 var list = document.getElementById("list"),  
    frag = document.createDocumentFragment(),  
    items = ["раз", "два", "три", "четыре"],
    el;  
  
for (var i = 0; items[i]; i++) {  
  el = document.createElement("li");  
  el.appendChild( document.createTextNode(items[i]) );  
  frag.appendChild(el); // Ништяк!  
}  
  
list.appendChild(frag);  


Ошибка 10 — Ты не учишь JavaScript


JavaScript НЕ jQuery. Ок? Если вы допускаете несколько из перечисленных выше ошибок, вероятно, вам нужно проштудировать JavaScript. JavaScript — язык, который можно использовать практически без изучения, поэтому многие люди не тратят время на это. Не будь одним из них. Есть много хороших учебников по языку, так что у вас не найдется оправданий. Если все что вы знаете — jQuery (MooTools, etc.), вы ставите себя на плохое место.

Многие люди не тратят время на правильное изучение JavaScript.

Ошибка 11 — Ты следуешь всем правилам


Последняя и окончательная ошибка — ты следуешь всем правилам. Да, и некоторые, перечисленные здесь. как и все, правила для того, что бы их ломать. Дело в том, что если в понимаете, почему нельзя использовать тот или иной прием, то он становится инструментом, который вы можете правильно применят в правильной ситуации. Тот же eval — единственный способ разбора JSON. Конечно, есть много способов проверки безопасности на месте (лучше использовать библиотеку). Вы не должны бояться использовать «плохую практику», если знаете, что делаете, и если это — необходимо.

Конечно же, никогда не делайте ошибку 10.




Дуглас Крокфорд — американский программист, разработчик языка JavaScript, популяризовал JSON, ведущий разработчик Yahoo, а также создатель библиотеки YahooUI.
Хм. @BenLaden
карма
0,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (145)

  • +4
    Насчет второго, есть пример лучше:
    var a = []
    (new Date).getTime()
    

    TypeError: [] is not a function

    А вот с; все нормально
    • 0
      И насчет Boolean(false);
      if ( new Boolean (false) ) alert('true')
      

    • +8
      if (true)  
        console.log("В операторе if");  
        console.log("Вне оператора if");  
      

      Ну люди не зря отступы придумали
      if (true)  
          console.log("В операторе if");  
      console.log("Вне оператора if");  
      
    • +4
      Последняя строчка кстати убила:

      Дуглас Крокфорд — американский программист, разработчик языка JavaScript, популяризовал JSON, ведущий разработчик Yhoo, а также создатель библиотеки YhooUI.

      Разработчик JavaScript — Брендан Айк
      Редактор первой версии ECMAScript — Гай Стил (привет лисперам)
      Дуглас Крокфорд — тут вобще не в тему

      Yhoo одну и туже ошибку два раза, да и плюс YhooUI мне прочиталось как хууу*
      • +12
        Разработчик YahooUI уж конечно искал случая записать «JavaScript НЕ jQuery».
      • +2
        > Дуглас Крокфорд — тут вобще не в тему

        Ну, Крокфорд (официально) является членом TC-39. На практике «его» идеи тоже отражаются в ES (например, Object.create, JSON.parse, etc).

        Насчет Yahoo, он мне сам сказал, что уже давно особо ничем там не занимается ("...nothing. Really."), ** пинает и разъезжает по конференциям :D
    • НЛО прилетело и опубликовало эту надпись здесь
    • +1
      я, к примеру, выработал себе правило оформления объявления локальньных переменных:
      var a=1,
          b=2,
          c=3;
      

      и все ок, больше нигде точек с запятыми не нужно)
      • 0
        Ну это уже немного другое, и я бы вам посоветовал писать немного по другому:
        var a = 1,
            b = 2,
            c = 3;
        
        • +1
          это у меня с питона, там при инициализации переменных не принято ставить вокруг равенства пробелы: a = 1, но some_func(b=1)
  • +19
    С == и === нужно знать разницу, а не везде использовать ===.
    • +4
      Ага. Особенно убило вот это место:
      if (typeof d[prop] !== 'function') {
      Нафига, спрашивается, здесь тройное равно? Что ещё может быть равно 'function', кроме 'function'?
      • 0
        Вы по-моему себе противоречите
        >>>Что ещё может быть равно 'function', кроме 'function'?
        А смысл тогда использовать ==, если можно как раз обойтись ===? Ничего приводить как раз и не нужно в данном случае.
        • +1
          Как раз наоборот, зачем использовать ===, когда можно обойтись ==.
          typeof вернет string. Так зачем тут ставить ===?
    • 0
      Все таки тут есть такой фактор, как большое число разработчиков не желающих разбираться в деталях работы этих двух операторов. Для них проще будет всегда использовать ===.
      • +1
        Не могу с вами согласиться, я видел такое непонимание на php, вылилось это в факап.
        • 0
          А можно поподробнее? Интересно в чем там дело было.
          • +1
            Приходило числовое значение в string, естественно if ($var === 1) ничего хорошего не сулило.
            • 0
              Ну тут достаточно, имхо, знать, что по http числа не передаются и писать if ($var === «1») или if ((int)$var === 1), а то и $var = (int)$var; if($var === 1)

              • 0
                Ой ли только в http, а бд и мемкеши без оберток? :)
                • –1
                  И там тоже, просто первая ассоциация php — http :)
              • +1
                В данном случае, просто необходимо приведение к типу.
          • 0
            функция strpos к вашим услугам
      • 0
        «панять эта нивазможна, но запомнить нада»

        Хотя лучше, по-моему, на неявные приведения типов не надеяться, даже если ночью тебя разбуди и они от зубов отскакивают, и всегда приводить явно.
  • +16
    однако, как веб-разработчик, вы, вероятно, используете другие языки (например, PHP), где точки с запятыми ставить не нужно.
    Немного не понял. Конкретно php достаточно строг с ;
    function test(){
    return
    }
    test();

    не скомпилится

    $five=5
    $six=6

    не скомпилится

    Кажется, пример с php притянут за уши.
    • –3
      Да. Возможно, погорячился.
      • +2
        Вы бы еще раз внимательно текст вычитали. Чтобы не было подобных ошибок и казусов вроде «Посмотрите, что я имею в виду?».
    • +1
      Простите, может, я многое забыл из РНР… Но мне всегда чудилось, что РНР-код не компилируется, и интерпретируется.
      • 0
        Это расхожее заблуждение. Он и компилируется и интерпретируется :)
        • +1
          :) Возможно, с фейсбучным ХипХоп компилером так и есть. Однако я отталкивался от первоначального именования РНР как PHP/FI, т.е., интерпретатор форм.
          Не вникая в семантику, допустим, что Fatal Error в РНР и есть ошибка компиляции. Но, чувствую, что несу чушь.
          Если не затруднит, какие-нибудь ссылки о компиляции РНР (не берем во внимание РНР-Gtk) с удовольствием бы посетил.
          • +2
            PHP 5 компилирует исходный текст в свои опкоды и затем их выполняет.
            • 0
              :) Спасибо, с указанной ссылкой знаком, в свое время некоторые интересные вещи почерпнул оттуда.
              Однако я понял комментарий DeusModus несколько в ином ключе. Разработчики, использующие РНР для написания программ, не компилируют код так, как, например, разработчики, использующие С.
              Да, конечно, в случае РНР происходит в некотором роде runtime-компиляция. Но ведь для сборки и выполнения целевой программы разработчики не компилируют чистый РНР сценарий, не так ли? :)
              • 0
                Вы неправильно поняли комментарий DeusModus.
          • +1
            До кучи, сейчас полно кешеров опкода. xcache, wincache, eaccelerator. И опкод можно хранить в памяти оперативной.
  • +8
    Тот же eval — единственный способ разбора JSON.

    Что? В современных браузерах уже давно есть нативная поддержка JSON. Для совместимости со старыми браузерами рекомендуется использовать эту библиотеку от Дугласа Крокфорда.
    • +5
      Ну, для старых браузеров eval применим. И в той библиотеке как раз он и используется. Но зачем быть настолько категоричным(я про автора) да, непонятно.
      • 0
        Очевидно, это очень старая статья.
    • +1
      Не знал. Спасибо!
      • +5
        Статьи для новичков это хорошо, но лучше сначала самому разобраться в языке и научиться его правильно использовать, прежде чем писать статьи и пытаться учить других.
        • +4
          Это перевод :/
          • +1
            Имхо лучше было бы оставить примечание переводчика, что «с написания оригинальной статьи в JS произошли некоторые перемены».
            • +1
              Да, тут переводчик подкачал.
        • –1
          Самое смешное в этой ветке, что это перевод статьи именно Дугласа Крокфорда, на что намекает сноска в конце, до которого вы, горячий русский комментатор, судя по всему, не дочитали.
    • +3
      Как вариант, ещё вместо
      eval(responseText)

      можно записать так:
      var json = (new Function('return ' + responseText))();

      Использовался в старых версиях jQuery.
      • 0
        Тот же eval, вид сбоку.
        javascript.crockford.com/code.html ну и www.jslint.com/lint.html

        Лучше бы написали как без него обходиться. До window["foo" + i] не каждый новичек допрет
        • 0
          Не тот же. При использовании eval имеется потенциальная дыра — мы можем получать и изменять значения переменных из scope функции, в которой он вызывается. Ну и далее по цепочке scope. Вариант с Function всегда выполняет код в глобальном контексте. Собственно, в этом всё различие. Код:

          (function(){
          var data = 'value';
          eval('alert(data)');
          })();
          
          (function(){
          var data = 'value';
          (new Function('return alert(data);'))();
          })();

          Вообще-то я и написал, как можно без него. Или что имелось ввиду?
          • 0
            Оттого что scope спрятан, недостатки eval никуда не исчезают. Вы не обходитесь без него, просто заменяете на неявный вызов.
            С eval, кстати, можно было сделать так:
            
            var data = 'test';
            (function(global){
              var data = 'value';
              global.eval('alert(data);'); //test
            })(this);
            


            Про window["foo" + i] писал скорее к автору. Когда был новичком, сам писал eval("foo"+i) от незнания как это делать правильно, хотя подобных статей начитался :)
            • 0
              правильно писать foo[i] а от переменных вида foo1..X отказатся (и от глобальных переменных тоже)
  • +2
    Тот же eval — единственный способ разбора JSON.

    Что? В современных браузерах уже давно есть нативная поддержка JSON. Для совместимости со старыми браузерами рекомендуется использовать эту библиотеку от Дугласа Крокфорда.
    • +1
      Что-то хабр глючит. Извиняюсь за повторный комментарий.
    • +2
      Нативные JSON-парсеры есть далеко не во всех браузерах + с ними имеется ряд проблем. У того же Крокфорда, в случае отсутсвия нативного парсера все сводится к санитизации json-строки, валидацией RegExp'ом по RFC и все тем же eval'ом.
  • +7
    Это ведь перевод, так?
    • +6
      И причём не вычитанный.

      > Итак, начнем с with. Две основные причины, по которым нельзя использовать его:
      Это же звездец!
    • +7
      Да, вот эти фразы, вроде «плохая идея», «вы ставите себя на плохое место» так и бьют по глазам.
      • 0
        В любом случае, всегда for-in выражения ясными, что бы избежать нежелательных результатов.

        Или мозг взырвают :)
        Но все равно сохранил в избранное дабы было под рукой что показывать. Информация местами довольно стоящая, а дать ссылку проще чем объяснять самому.
  • 0
    у вас в анонимных функциях закрывающая круглая скобка не там.
    должно быть
    (function() { ... })();
    вместо
    (function() { ... }());
    • 0
      Вообще то разницы особой и нет.
      • +3
        но в первом случае хотя бы смысл появляется =)
    • +1
      И тот и другой вариант вполне рабочие ;-)
    • +1
      Оба примера равнозначны т.к. и первый и второй делает из объявления функции функциональное выражение.
      • +2
        делает, делает. но разве не логичнее сначала описать функциональный литерал, окружить его скобками, а потом инвоукнуть?
        наверное я погорячился с «должно быть», я считаю, это просто красивее и понятнее.
        p.s.: руководствовался трудами Флэнагана, и разделом «Self-Invoking Functions» в статье www.hunlock.com/blogs/Functional_Javascript
        • 0
          заметил, кстати, что у Флэнагана с Крокфордом немного разное понимание как должно быть
    • +1
      lslint ругается
      Problem at line 4 character 3: Move the invocation into the parens that contain the function.

      })();

      javascript.crockford.com/code.html
      When a function is to be invoked immediately, the entire invocation expression should be wrapped in parens so that it is clear that the value being produced is the result of the function and not the function itself.

      ну, это просто такое соглашение
      код будет работать в обоих случаях
  • +1
    По пункту 6: eval, порой не так уж страшно использовать, если не эвалится user input. Google, например, не имеет ничего против использования eval для сериализации.

    По пункту 8: думаю такая проблема актуальна, если вы пишите JS в блокноте и не следите за стилем кодирования. Так проблема высосана из пальца. Все современные IDE, работающие с JS умеют форматирование. Да и сам разработчик должен следить за форматированием. Не думаю, что любой более или менее опытный разработчик способен допустить подобную ошибку. Мне, например, нравятся однострочные if'ы без скобок.
    • 0
      Так статья рассчитана на новичков.
      • +1
        А вы себя к какому классу относите?
        • +2
          Программирую на JavaScript уже около года. С заказами справляюсь легко. Но часто вижу такие коды/программы, что появляется мысль: я — нуб.
          • +1
            Нуб — это когда думаешь, что пишешь самый лучший код в мире во всех аспектах и тебе нет равных. Когда приходит понимание, что твой код далеко не идеален — это уже стадия программиста. А когда ты считаешь, что твой код полное говно и над ним ещё работать и работать — ты гуру :)
    • +3
      Да, использование фигурных скобок — это знатная тема для холивара в любом языке )
  • +4
    Еще частенько ставил запятую после последнего элемента массива (php-шная привычка), что приводит к ошибкам в ИЕ.

    [foo, bar, baz, ]
    • +1
      JSLint/JSHint вас спасёт.
      • +1
        Еще, например, JetBrains WebStorm сразу подсвечивает такого рода ошибки.
        • +1
          Большинство IDE это подсвечивает в том или ином виде.
    • 0
      А что, в php так нужно делать?!
      • 0
        там это допускается
        Having a trailing comma after the last defined array entry, while unusual, is a valid syntax.
        php.net/manual/en/function.array.php
      • 0
        это просто удобно, когда копипастишь несколько строк (чтобы не вставлять "" => "", каждый раз)
        ленивый phpшный стиль )
        • 0
          Я себя за это пинаю. Теперь не буду.
  • +1
    Не самое дурное руководству к чистоте кода — www.jslint.com/lint.html
    • 0
      Обожаю www.jslint.com/.
      • +1
        Кстати, проверка куска

        var cartTotaler = (function () {
        var total = 0; tax = 0.05;

        // еще код

        return {
        addItem : function (item) { },
        removeItem : function (item) { },
        calculateTitle : function () { }
        };
        }());


        Выявила несколько ошибок, в том числе и описку(я выделил жирным; вместо ожидаемого символа ,).

        Problem at line 2 character 5: Missing 'use strict' statement.

        var total = 0; tax = 0.05;

        Problem at line 2 character 20: 'tax' was used before it was defined.

        var total = 0; tax = 0.05;

        Problem at line 7 character 7: Expected 'addItem' at column 9, not column 7.

        addItem : function (item) { },

        Problem at line 8 character 7: Expected 'removeItem' at column 9, not column 7.

        removeItem : function (item) { },

        Problem at line 9 character 7: Expected 'calculateTitle' at column 9, not column 7.

        calculateTitle : function () { }

        Undefined variable: tax 1 'cartTotaler'

        Unused variable: total 1 'cartTotaler', item 7 'addItem', item 8 'removeItem'
  • 0
    >>Тот же eval — единственный способ разбора JSON.

    а разве json нельзя перебрать тем же for...in…?
    • 0
      Нет
      • +1
        Есть библиотека, которая парсит JSON как строку без eval.
        • +1
          Появился повод проверить что же для старых браузеров лучше- ацкие регулярки или eval :)
        • НЛО прилетело и опубликовало эту надпись здесь
    • 0
      Подразумевалась все таки “парсинга”. Ну и как выше отметили и он не единственный
    • 0
      JSON это строка, которую надо каким-то образом преобразовать в объект. Именно для этого и используется eval/parseJSON.
  • +3
    На Яндекс.Субботнике в Киеве рассказывали (второй доклад, Михаил Корепанов), что добавление элементов в DOM через JS происходит быстрее, если вместо использования фрагментов генерировать саму верстку и вставлять её через innerHTML(). Это к 9-му пунтку про добавление элементов по одному. Возможно, стоит упомянуть как вариант.
    • +1
      Кстати, так давно делаю. Намного проще даже порой и меньше кода получается.
    • +1
      Как вариант, но часто приходится обработчики событий на добавляемые элементы навешивать, и апостериорно это делать тяжело бывает.
      • 0
        А почему бы не использовать примерно такую конструкцию:

        var addedContent='';

        for (var x=0;x<arrayName.length;x++){

        addedContent=addedContent+'';

        }
        • 0
          Прошу прощения. Почему-то сократилось все. Добавлю пробелов где можно.

          addedContent=addedContent+' '
          • 0
            не помогло. =)

            вобщем onclick и экранируемые скобки внутрь
            • +1
              Если делать innerHTML и onlick=«blabla(this)», то обработчики должны быть глобальные. Не всегда удобно.
          • +1
            У хабраредактора есть тэг source, как раз для вставки кода.
            А зачем тэг code — я не знаю.
            • 0
              спасибо, не знал про него
      • –1
        используйте jQuery:
        $('<div> lalala </div>').click(function(){})
    • +1
      Думаю это относиться только к маленьким кускам HTML. Я замерял скорость генерации достаточной большой таблицы и вставке её из document.fragment — фрагмент победил. Возможно это случайность — надо будет ещё раз попробовать.
  • 0
    Меня больше раздражает то, что схватывание клавиш в поле ввода.
    В ИЕ и Хроме нормально. В опере после ввода ничего не стереть (надо отдельно схватывать и разрешать backspace — ну это фигня), а в Mozilla — пофигу. Вводи что хочешь.
  • +1
    Пересказ 2 главы книги Stoyan Stefanov «Javascript patterns»?
    =)
  • +2
    >>> var cartTotaler…

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

    >>> Любой оператор в JavaScript должен заканчиваться точкой с запятой.
    Не обязательно. Это разрешает спецификация языка. Есть операторы, которым точка с запятой жизненно необходима, там действительно нужно обязательно ставить этот разделитель.

    >>> Использование ==
    О боже… Выучить приведение типов не судьба? Нет, давайте придумывать сложности!

    >>> '0' == '' // true
    false там, строки сравниваются

    >>> Не используются скобки при употреблении операторов if и while
    В спеке написано что можно, значит можно.
  • +2
    думааю эта статья будет полезна таким как я, кто ничего не читал про JS, но пишет на нем =) спасибо)
  • +1
    Я подумал что у меня дежавю, помню что где-то читал это… оказалось на net.tuts =)
  • –1
    спасибо!
  • 0
    Еще часто ставят лишнюю запятую в описании полей json объекта, при этом в FF допустим работает, но в IE падает. Нормальные IDE (Eclipse в моем случае) даже на дифолтных настройках такие вещи светят красным, а отсутствие точки с запятой — желтым. В коде YUI ничего желтого нет :)
  • 0
    Автор, у вас, разве, нет возможности пометить топик как перевод?
    • НЛО прилетело и опубликовало эту надпись здесь
      • +1
        А сделано так, в свою очередь, потому, что в песочницу должен попадать только оригинальный контент. Предполагается, что приглашение можно получить только за собственные статьи, но никак ни за переводы или топики-ссылки.
        • НЛО прилетело и опубликовало эту надпись здесь
          • +1
            Во-первых, пригласивший по знакомству, на мой взгляд, несет ответственность перед Хабрасообществом за тех, кого он таким образом пригласил.

            Во-вторых, люди, приглашенные через песочницу, на самом деле имеют большое преимущество перед теми, кого пригласили просто так. Потому что в первом случае у человека уже есть статья, за которую ему, скорее всего, плюсанут в карму (статья же хорошая, раз пригласили). А человеку, приглашенному без статьи, чтобы что-то написать, придется набивать карму до 5 — комментариями и ответами в Q&A. Что довольно-таки непросто, я вам доложу :)

            А Хабрахабр — просто сайт, да. Но контент на нем качественный как раз за счет того, что идет отбор на этапе регистрации.
            Переводчиков, по-моему, всегда будет достаточно. А вот людей, способных писать оригинальные и интересные статьи, наоборот, всегда не хватает.
            • НЛО прилетело и опубликовало эту надпись здесь
              • +1
                Ок, но только хорошие! ;-)
  • 0
    var person = { name: "Петя", age : 10 },  
        name = "Жора";  
      
    with (person) {  
      console.log(name); // Жора
      job = "Дизайнер";  
    } 
    


    Я чет не понял, почему внутри with «Жора» выходит? В оригинале также.
    • –2
      Скорее всего, это ошибка какого-то из конкретных интерпретаторов.
    • +1
      ну так переменная name перекрывает поле объекта, доступ к которому пытаются получить с помощью with.
      • +1
        Почему переменная name должна перекрывать поле, не понимаю, если мы в контесте объекта.
        Мне кажется, что там ошибка просто… Я проверил в браузерах в разных(ie, ff, chrome) вариант везде «Петя»
        • 0
          Видимо автор оригинальной статьи ошибся — насколько я понимаю, речь идет именно об этом.
  • +5
    Javascript Гарден — очень советую.
  • +3
    Любой оператор в JavaScript должен заканчиваться точкой с запятой

    Пусть это и перевод, но известно же, что операторы могут заканчиваться запятыми? Автор (Andrew Burgess) этого почему-то не упоминает. И непонятно, то ли он хотел сказать, что var a=1, b=2; может создать ошибки, то ли вообще забыл об этом. Но вот случай, когда использование запятой более наглядно:

    a=[1, 2, 3, 4, 5];
    for(var i=0, aLen = a.length, s = 0; i < aLen; i++){
        s += a[i];
    }
    Кстати, почему (до сих пор) не указано, что автор текста — Andrew Burgess?
    • 0
      А по-моему вообще путаются инструкции, выражения и операторы. Большинству операторов требуется после него операнд, запятая такой же оператор как все остальные, формирующей значение выражения, а вот точкой с запятой должны оканчиваться большинство инструкций, но эта точка с запятой может добавляться транслятором автоматически в не всегда очевидных случаях.

  • 0
    Уууух! Всего год как Javascript серьезно занимаюсь, но большую часть этих шишек набил еще на этапе подготовки к собеседованиям.
  • +2
    Путают NodeList, HTMLCollection и прочие массивоподобные объекты с Array. Например функция getElementsByTagName возвращает «живой список» (не Array). querySelectorAll возвращает тоже не массив. Т.к. все они возвращают не массивы, то у этих объектов нет методов прототипа Array - forEach, map, slice

    Ошибка: Бесконечный цикл и использование метода Array на не-Array
    var divs = document.getElementsByTagName("div"),
        i = 0;
    
    while (i < divs.length) { // (1) Бесконечный цикл
        document.body.appendChild(document.createElement("div"));
        i++;
    }
    
    divs.forEach(function () { // (2) Нет свойства forEach
    
    });
    

    Чтобы не допустить этой ошибки нужно убить живой список либо использовать querySelectorAll
    // getElementsByTagName возвращает "живой список" HTMLCollection - это НЕ Array
    var divs = Array.prototype.slice.call(document.getElementsByTagName("div")), // делаем Array
        i = 0;
    
    while (i < divs.length) { // (1) OK
        document.body.appendChild(document.createElement("div"));
        i++;
    }

    // querySelectorAll возвращает NodeList - это НЕ Array
    var divs = Array.prototype.slice.call(document.querySelectorAll("div")), // делаем Array
        i = 0;
    
    divs.forEach(function () { // (2) OK
        document.body.appendChild(document.createElement("div"));
        i++;
    });

    Подробнее Why is getElementsByTagName() faster that querySelectorAll() — Nicholas C. Zakas
    • 0
      а почему решили возвращать Nodelist?
      • 0
        Оптимизация. Nodelist — некое подобие связанного списка, хождение по которому осуществляется итератором (даже если вы используете индексер). За счет этого можно не подгружать все DOM-элементы на данном уровне иерархии, как это потребовалось бы с массивом, а делать это по запросу, когда до нужной ноды доберется итератор. Кстати отсюда же вытекает и совет о предпочтительном использовании итераторов для Nodelist перед индексерами, в первом случае поиск будет осуществляться за O(N), во — втором за O(N^2).
      • 0
        Каждый раз, когда создается какой-то элемент он попадает в HTMLCollection это объект в котором хранятся элементы с определенным именем. При вызове метода getElementsByTagName мы просто «получаем ссылку» на этот объект и ничего не вычисляем.
        Live NodeList objects can be created and returned faster by the browser because they don’t have to have all of the information up front while static NodeLists need to have all of their data from the start. To hammer home the point, the WebKit source code has a separate source file for each type of NodeList: DynamicNodeList.cpp and StaticNodeList.cpp. The two object types are created in very different ways.
  • 0
    Может тупой вопрос, но все же, вот два примера, в чем между ними разница?

    var list = document.getElementById(«list»),
        frag = document.createDocumentFragment(),
        items = [«раз», «два», «три», «четыре»],
        el;
    for (var i = 0; items[i]; i++) {
        el = document.createElement(«li»);
        el.appendChild( document.createTextNode(items[i]) );
        frag.appendChild(el); // Ништяк!
    }
    list.appendChild(frag);

    и мой вариант:

    var list = document.getElementById(«list»),
        frag = document.createElement('div'),
        items = [«раз», «два», «три», «четыре»],
        el;
    for (var i = 0; items[i]; i++) {
        el = document.createElement(«li»);
        el.appendChild( document.createTextNode(items[i]) );
        frag.appendChild(el); // Ништяк!
    }
    list.appendChild(frag);

    Есть ли существенная разница между ними?
    • 0
      в первом случае вы кладёте в UL четыре LI, во втором — DIV с четыремя LI? Вот поэтому первый вариант и предпочтительнее. Иными словами — фрагмент пропадает при добавлении в DOM и является универсальным контейнером.
      • 0
        Ладно, отойдем от конкретики, получается что createDocumentFragment создает пустой элемент «ничего» и затем при добавлении его куда либо он просто отдает свое содержимое?
        • 0
          var frag = document.createDocumentFragment();
          var div = document.createElement('div')
          console.log(frag, div)

          Вы правы, фрагмент это практически пусто.
          Сравните сколько методов у дива (который висит в памяти и в DOM вставляется), а сколько у фрагмента
        • 0
          Совершенно верно — у Вас уже есть элемент в DOM и Вам надо его наполнить несколькими элементами — сделать это за одно обращение к DOM можно только таким способом(AFAIK).
          • 0
            Не знал про него =) Оч полезная штука, ато я дивами пользовался.
            • 0
              Фрагмент вообще шикарная штука. Без него взять и переместить кусок DOM-дерева никак не получится. Вообще поколение jQuery понятия не имеет, как красиво можно работать с методами DOM…
              • 0
                Кусок дом дерева можно элементарно переместить =) Создать новые несколько нод за раз да, а переместить как два пальца.
                И я не поколение jQuery, если на то пошло, учу исключительно чистый JS, но вот тоже есть пробелы.
                • 0
                  Чтобы не было пробелов, нужно учить DOM не по комментариям на Хабре, а по первоисточникам: спецификациям w3c. ;)
          • 0
            JFYI: через innerHTML тоже за одно.
  • 0
    перевод самопальный, лучше попробуй перевести поновей статейки.
  • 0
    Ошибка 8 часто встречается независимо от языка
    По поводу ошибки 9 хочется сказать, что иногда нужно добавлять объекты в DOM поодиночке, чтобы знать изменение размера элемента, например
  • –2
    блеать, джаваскрипт ужасен…
  • 0
    У нас есть такой код:
    
    <html>
    <head>
    <title>For test</title>
    </head>
    <body>
    <form name="abc">
    1 <input type="text" name="a">
    2 <input type="text" name="b">
    3 <input type="text" name="c">
    </form>
    <script>
    <!--
    function w_test() {
    	with(document.abc) {
    		for(i = 0; i < /*document.abc.*/elements.length; i++) {
    			el = /*document.abc.*/elements[i];
    			if(el.tagName == 'INPUT' && el.type == 'text')
    				alert('Поле: ' + el.name + '\nЗначение: ' + el.value);
    		}
    	}
    }
    // -->
    </script>
    <a href="javascript:w_test();">Проверить</a>
    </body>
    

    использование with тут оправдано?
    • 0
      Нет. Ваш пример легко заменяется на

      var abc = document.forms.abc;

      и дальше

      for ( var i = abc.length; i-- ;) {… }

      Подумайте, к какому scope относятся переменные el, i?
      • 0
        Просто, как правильнее будет и практичнее?
    • 0
      Нет. Этот tag-soup уже сам по себе не оправдан.

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