Топ-11 самых частых ошибок в JavaScript
JavaScript — относительно простой язык в изучении. Однако, ошибок в нем допускается более чем достаточно. Вы уверены, что не допускаете их? Сегодня мы рассмотрим 11 самых распространенных ошибок.
Ошибка 1 — Использование глобальных переменных
Если вы только знакомитесь с JavaScript, вы, вероятно, думаете, что это отлично, когда все переменные — глобальные. На самом деле, вы можете не знать всех тонкостей этого инструмента. Глобальные переменные — переменные, которые доступны из любого участка кода, даже если они загружены в разные .js-файлы. Звучит заманчиво, не правда ли? Любая переменная всегда доступна для изменения.
На самом деле, нет.
Это плохая идея, поскольку вы можете перезаписать значения непреднамеренно. Допустим, у вас есть интернет-магазин, и вы используете JavaScript для подсчета суммы цен товаров, добавленных в корзину. Вот пример кода:
1 2 |
var total = 0, // конечные счет tax = 0.05; // 5% |
Теперь, допустим, вы используете код для отображения твитов на странице, или сделали мини-галерею ваших продуктов. И там может содержаться код вроде этого:
1 |
var total = 15; // кол-во твитов из twitter |
Или,
1 |
var tax = function () { /* ... */ }; // Стартер для анимации |
Теперь у вас проблема. Две важные переменные были перезаписаны, и ваш код работает с ошибками. Плата за это — драгоценное время, потраченное на переписывание.
Так в чем же решение? Одним словом, инкапсуляция; но есть множество других способов избежать этого. Во-первых, вы можете записать весь код в виде само вызова, анонимные функции:
1 2 3 4 5 |
(function () { var total = 0, tax = 0.05; // какой-то код }()); |
И никакой код извне не доберется до кода внутри функции. Это отлично работает для «личного» кода, но это не дает той функциональности, которая нам нужна. Например, если мы хотим корзину-счетчик, которую другие могли бы использовать как шаблон, было бы неплохо сделать вот так:
1 2 3 4 5 6 7 8 9 10 11 |
var cartTotaler = (function () { var total = 0; tax = 0.05; // еще код return { addItem : function (item) { }, removeItem : function (item) { }, calculateTitle : function () { } }; }()); |
Еще чуть-чуть о глобальных переменных: заметим, что если вы не используете ключевое слово var при создании переменной, JavaScript-движок определяет её как глобальную по умолчанию. Например:
1 2 3 4 5 |
(function () { tax = 0.05; }()); var totalPrice = 100 + (100 * tax); // 105 |
Переменная tax доступна за пределами нашей функции, поскольку не было использовано слово var при её объявлении. Следите за этим.
Ошибка 2 — Забыли о точке с запятой
Любой оператор в JavaScript должен заканчиваться точкой с запятой. Это очень просто. Если вы забудете, то компилятор сделает дело за вас. Значит, можно их не ставить?
Ну, в некоторых местах это жизненно необходимо. Например, если в теле цикла у вас выполняется несколько операторов подряд. Иначе компилятор выдаст ошибку. А как насчет конца строки?
Сообщество JavaScript разделилось во мнениях по этому поводу. Есть весомые аргументы по обе стороны баррикад. Вот мой аргумент: если вы полагаетесь в этом вопросе на компилятор (даже в самом простом коде), вы играете с огнем.
Например, такая простая функция:
1 2 3 4 5 6 |
function returnPerson (name) { return { name : name }; } |
Кажется, что он должен вернуть маленький миленький объект… но компилятор решит, что вы хотели поставить после return точку с запятой, поэтому ничего не вернется, а объект будет проигнорирован. Вы должны сделать следующее:
1 2 3 |
return { name : name }; |
Так что, мой вам совет — ставьте точки с запятыми; честно, это входит в привычку довольно быстро. Однако, как веб-разработчик, вы, вероятно, используете другие языки (например, PHP), где точки с запятыми ставить не нужно. Тогда, зачем?
Замечание:Если вы не знаете о каждой ситуации, где разрешается опускать, лучше не рисковать.
Ошибка 3 — Использование ==
Если вы сейчас спросите первого попавшегося JavaScript-разработчика:«Какая самая распространенная ошибка в JavaScript?», скорее всего, он/она ответит:«Использование == вместо ===». Что это значит?
Попробуйте это:
1 2 3 |
if (1 == 1) { console.log("Правда!"); } |
Вы ожидали, что код заработает, так? Хорошо, теперь попробуйте следующее:
1 2 3 |
if (1 == '1') { console.log("Правда!"); } |
Да, у тебя в консоли выскочило «Правда!»… и да, это плохо. Происходит здесь следующее, == — оператор приравнивания. Он делает две переменные максимально схожими. В нашем случае происходит преобразование строки «1» в число 1, и наш if-оператор выдает true.
Решение, как вы поняли, заключается в использовании ===; Все это годно и в случае с операторами !== и !=.
А теперь, для веселья, несколько самых лучших ошибок, получившихся из-за двойного равенства:
1 2 3 4 |
'' == '0' // false '0' == '' // true false == '0' // true ' \t\r\n ' == 0 // true |
Ошибка 4 — использование объектов оборачивающих типы
JavaScript любезно(!) дает нам несколько оберток типов для легкого(!) создания типов.
1 2 3 4 5 |
new Number(10); new String("Привет!"); new Boolean(true); new Object(); new Array("один", "два", "три"); |
Во-первых, это супер неудобно. Все это можно делать и при значительно меньшем числе нажатий клавиш.
1 2 3 4 5 |
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 цикл:
1 2 3 4 5 |
var prop, obj = { name: "Вася", job: "Тракторист", age: 55 }; for (var prop in obj) { console.log(prop + ": " + obj[prop]); } |
Если вы запустите этот код, вы должны увидеть следующий вывод:
1 2 3 |
name: Вася job: Тракторист age: 55 |
Однако, браузер будет включать в себя свойства и методы дальше по цепочке. Скорее всего, вы не захотите, что бы свойства эти перечислялись. Вы должны использовать hasOwnProperties, что бы отфильтровать свойства, которые не являются объектами:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
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:
1 2 3 4 5 |
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. Две основные причины, по которым нельзя использовать его:
- Он замедляет ваш код
- Не всегда понятно, что вы делаете
Пункт первый стоит на своем. Так что давайте взглянем на секунду. Вот как with работает: вы отправляете какой-либо объект в with-выражение; затем, внутри блока with, вы можете получить доступ к свойствам объекта как к переменным:
1 2 3 4 5 6 |
var person = { name: "Петя", age : 10 }; with (person) { console.log(name); //Петя console.log(age); // 10 } |
Но что если у нас есть переменная с таким же именем как свойство объекта with? В принципе, если есть и то и другое, переменная будет использоваться. Но вы не сможете добавить свойства объекту, находящемуся внутри with. Если нет свойства или переменная существует, то это сможет сделать переменная извне выражения with:
1 2 3 4 5 6 7 8 9 10 |
var person = { name: "Петя", age : 10 }, name = "Жора"; with (person) { console.log(name); // Жора job = "Дизайнер"; } console.log(person.job); // неопределенно; console.log(job); // Дизайнер |
Перейдем к eval. В двух словах, вы можете передать строку кода функции, и она будет её выполнять.
1 |
eval( "Console.log('Привет!');" ); |
Звучит безобидно, даже мощно, так? На самом деле, это и есть главная проблема — слишком большая мощь. Очевидно, нет причин писать строки так, потому что 1) почему бы не писать по-простому? И 2) eval замедляет работу также как и with. Таким образом, основное применение eval — выполнять код, который вам не нужен в данный момент. Вы могли бы получить это от сервера или напрямую от пользователя. Вы действительно хотите предоставить пользователям сайта полный контроль вашего кода? Надеюсь, что нет. Кроме того, он открывает свой сайт для хакеров: использование EVAL — знак, который говорит:«Я в отъезде, и ключ под ковриком.» Если вы любите себя или ваших пользователей: не используйте его.
Ошибка 7 — Забываем об использовании систем счисления при использовании parseInt
JavaScript дает нам маленькую функцию, которая помогает преобразовать строку, содержащую число в число:
1 2 |
parseInt("200"); // 200 parseInt("043"); // 35 |
И что же там произошло? Разве во втором примере не должно быть 43? На самом деле, parseInt работает не только с десятичными системами. Когда он видит строку, начинающуюся с 0, то он считает её восьмеричным числом. Поэтому нельзя забывать о системах счисления; они говорят функции о базовом числе.
1 2 |
parseInt("020", 10); // 20 parseInt("100", 2); // 4 |
Ошибка 8 — Не используются скобки при употреблении операторов if и while
Одна из заслуг JavaScript — гибкость. Но иногда она может обернуться против вас. Такое происходит в случае с фигурными скобками в составных операторах if и while. Скобки являются необязательными, если в операторе только одна строка кода:
1 2 |
if (true) console.log("внутри оператора if"); |
Это очень удобно, ведь можно продолжать операторы на той же строке:
1 2 3 4 |
var arr = ["раз", "два", "три", "четыре", "пять", "шесть", "семь", "восемь", "девять", "десять"], i = arr.length - i; while (i) console.log( arr[i--] ); |
Но это не разумно по нескольким причинам. Во-первых, такой прием оставляет неясности:
1 2 3 |
if (true) console.log("В операторе if"); console.log("Вне оператора if"); |
Посмотрите, что я имею в виду? Вторая линия не в операторе, но выглядит она очень правдоподобно. Скобки внесут здесь ясность. Кроме того, если вы хотите добавить строку в if, не забудьте скобки. Лучше добавьте их сразу, это гораздо проще. Сделайте это.
Ошибка 9 — добавление элементов в DOM поодиночке
Да, да, это не JavaScript. Но в 99 случаях из 100 в JavaScript затрагивается DOM. Хотя есть много ошибок, которые вы можете допустить в DOM, здесь самая большая.
Вставлять элемент DOM с помощью JavaScript весело и полезно, но, к сожалению, грузит страницу. Поэтому вставлять много элементов DOM один за одним — плохая идея:
1 2 3 4 5 6 7 8 9 |
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. Вот пример:
1 2 3 4 5 6 7 8 9 10 11 12 |
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.
Sorry, the comment form is closed at this time.