5 самых распространенных ловушек в JavaScript коде

от автора

Доброго времени суток уважаемые хабраюезеры. Сегодня я хочу рассказать про 5 самых распространенных ловушек в JavaScript коде. Пост будет полезным для начинающих JS разработчиков и, возможно, интересным для опытных программистов (Можно знать как правильно, но не знать почему. И вообще, повторение мать учения).

Применение метода hasOwnProperty() в конструкции for…in

Поскольку в JS массивы не ассоциативны, разработчики используют объекты как хеш-таблицы. Перебирать все свойства объекта можно с помощью циклической конструкции for…in:

for (var prop in someObject) {     alert(someObject[prop]); // выводим значения свойств } 

Важно знать, что цикл перебирает все свойства объекта в цепочке прототипов. Эта особенность является проблемой в том случае, если Вы хотите использовать только те свойства, которые существуют в самом объекте. Проблема решается с помощью метода hasOwnProperty():

Очень наглядно эта особенность описана на javascript.ru

ООП Javascript: Наследование — Методы объекта

Свойства-объекты или «иногда прототип это зло

Объявление всех свойств в прототипе может привести к незапланированному разделению одного и того же свойства разными объектами.

Например, объявим объект класса хомяк(Hamster). Метод found набирает еду за щеки, набранное хранит в массиве food.

function Hamster() {  } Hamster.prototype = { 	food: [], 	found: function(something) { 		this.food.push(something) 	} } 

Создадим двух хомячков: speedy и lazy и накормим первого:

speedy = new Hamster() lazy = new Hamster()  speedy.found("apple") speedy.found("orange")  alert(speedy.food.length) // 2 alert(lazy.food.length) // 2 (!??) 

Как видно — второй хомяк тоже оказался накормленным! В чем дело?

Причина заключается в том, что food не является элементарным значением.

Если при простом присвоении hamster.property=»…" меняется свойство property непосредственно в объекте hamster, то при вызове hamster.food.push(…) — яваскрипт сначала находит свойство food — а так, как в hamster его нет, то оно берется из прототипа Hamster.prototype, а затем вызывает для него метод push.

На каком бы хомяке не вызывался hamster.food.push(..) — свойство food будет браться одно и то же, из общего прототипа всех хомяков.

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

Но в данном случае такое ни к чему.

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

function Hamster() {     this.food = [] } Hamster.prototype = {     food: [], // просто для информации     found: function(something) {         this.food.push(something)     } } 

Теперь у каждого объекта-хомяка будет свой собственный массив food.

Свойство food в прототипе оставлено как комментарий. Оно не используется, но может быть полезно для удобства документирования.

for (var prop in someObject) {     if (someObject.hasOwnProperty(prop)) {         alert(someObject[prop]); // выводим значения свойств     } } 

Этот код выводит значения только тех свойств, которые непосредственно существуют в someObject.

Излишняя манипуляция c DOM

Излишнее взаимодействие с DOM, значительно снижает производительность вашего кода:

// anti-pattern for (var i = 0; i < 100; i++){    var li = $("<li>").html("Элемент номер #" + (i+1) );    $("#UL").append(li); } 

Этот код изменяет DOM 100 раз, и в добавок создает 100 объектов jQuery. Намного рациональнее задать цикл, который сгенерирует 100 элементов, запишет их в строку и занесет ее в HTML. В этом случае взаимодействие с DOM происходит один раз:

var listString = ""; for (var i = 0; i < 100; i++){    listString += "<li>Элемент номер #" + (i+1) + "</li>"; } document.getElementById("UL").innerHTML(listString); 

Если необходимо создать вложенные списки то метод конкатенации строк не подходит. Потребуется записать элементы в массив:

var subString = "<li><ul>" var subArray = []; for (var i = 100; i > 0; i--){    sublisttMas.push("Элемент номер #" + (i+1)); } subString += subArray.join("</li><li>") + "</ul></li>"; document.getElementById("UL").innerHTML(subString); 

Это — один из самых быстрых и легких способов сгенерировать вложенные списки без использования дополнительных библиотек.

Сравнение значений типа Boolean

Сравнение булевых значений в условии является пустой тратой производительности:

if (netcribe == true) {     // do something for true } else {     // do something for false } 

Обратите внимание на условие: netcribe == true. Сравнение данной переменной со булевым значением не имеет смысла, поскольку netcribe уже логическое значение.

if (netcribe) {     // do something for true } else {     // do something for false } 

если нужно сравнить со значением false, используем логические НЕ

if (!netcribe) {     // do something for true } else {     // do something for false } 

Присвоение событий

Event — представляет собой сложный JavaScript объект. Для простоты понимания используется jQuery (принципы всплытия и погружения объекта события одинаковы с нативным JS кодом). Представим следующую задачу: необходимо чтобы при клике на ссылку, открывалось модальное lightbox окно:

Соответствующий HTML код:

<div id="container">    <a href="someimage.jpg"><img src="someimage-thumb.jpg"></a>    <a href="someimage.jpg"><img src="someimage-thumb.jpg"></a>    <a href="someimage.jpg"><img src="someimage-thumb.jpg"></a> </div> 

Не корректный JS код:

$('a').on('click', function() {    callLightbox(this); }); 

В данном примере из за погружения Event объекта, событие может принадлежать не элементу <a>, а самому изображению <img>, из за чего функция callLightbox() сработает не верно.

Корректный JS код:

$("#container").on("click", "a", function(event) {    callLightbox(event.target); }); 

В этом случае используется event.target и событие будет присвоено к элементам <a>

Логически несовместимые имена переменных и функций

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

var url = "http://netcribe.com"; var browser = "Firefox"; var lang = "PHP"; 

Для примера, не корректно было бы добавить еще одну переменную somethink, это бы внесло противоречия в модель именования переменных. Вот почему константы в большинстве языков традиционно определяются с заглавными буквами.

Корректная грамматическая структура наименования функций:

function subtractFive(number){    return number - 5; }  function addFive(number){    return number + 5; } 

В определенных случаях имя функции определяется ее значением. Например, функцию которая возвращает HTML строку можно называть getTweetHTML().

Имена функций конструкторов и классов как правило начинаются с заглавной буквы:

function Dog(color){    this.color = color; } 

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

Спасибо всем за внимание.

ссылка на оригинал статьи http://habrahabr.ru/post/161809/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *