Пишем высококачественный JavaScript код

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

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

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

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

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

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

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

Легко поддерживаемый код имеет следующие признаки:

Он легко читается.
Он хорошо структурирован, и части согласованы между собой.
Он предсказуем.
Выглядит так, как будто написан одним человеком.
Документирован.

Минимизация использования глобальных переменных

JavaScript использует функции для управления контекстом. Переменные, объявляемые внутри функций, являются локальными для них и недоступны вне функций. Глобальные переменные объявляются вне функций или просто используются без объявления.

Каждое окружение JavaScript имеет глобальный объект, который используется вне функций. Каждая глобальная переменная, которую вы создаете, становится свойством глобального объекта. В браузерах для удобства существует дополнительное свойство глобального объекта, которое называется window, и которое (обычно) указывает на сам глобальный объект. Следующий код показывает пример создания и доступа к глобальным переменным в окружении браузера:
var myglobal = "hello";
console.log(myglobal); // "hello"
console.log(window.myglobal); // "hello"
console.log(window["myglobal"]); // "hello"
console.log(this.myglobal); // "hello"

Проблемы с глобальными переменными

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

Также, обычно, веб страница включает код, написанный другими разработчиками. Например:

Другие библиотеки JavaScript.
Скрипты партнеров по рекламе.
Код для отслеживания пользователей и аналитики.
Разные виджеты, кнопки и плагины.

Допустим, в одном из сторонних скриптов определяется глобальная переменная, которая называется, например, result. Затем вы в одной из своих функций определяете другую глобальную переменную и называете ее result. В итоге, последнее объявление переменной result перекроет первое, и сторонний скрипт может перестать работать.

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

К сожалению, очень просто непроизвольно создать глобальную переменную в JavaScript из-за двух его особенностей. Во-первых, вы можете использовать переменную без ее объявления. Во-вторых, JavaScript имеет определение подразумеваемого глобального, которое означает, что любая переменная без объявления становится свойством глобального объекта (и будет доступна как правильно объявленная глобальная переменная). Например:
function sum(x, y) {
   // плохо: подразумеваемое глобальное
   result = x + y;
   return result;
}

В данном коде переменная result используется без объявления. Код отлично работает, но после вызова функции вы получите еще одну переменную result в глобальном пространстве имен, что может привести к проблемам.

Правило минимизации заключается в определении переменных с помощью директивы var. Ниже приводится улучшенная версия функции sum():
function sum(x, y) {
   var result = x + y;
   return result;
}

Еще один плохой вариант для создания подразумеваемых глобальных - это цепочное присваивание значения в рамках объявления var. В следующем примере переменная a будет локальной, а переменная b станет глобальной, что наверняка не входит в список целей создателя кода:
// плохо, не надо использовать
function foo() {
   var a = b = 0;
   // ...
}

Если вы удивлены происходящим, то дело здесь в вычислениях справа-налево. Сначала выполняется выражение b = 0, и поэтому переменная b не будет объявлена. Возвращаемое значение выражения будет 0, и оно присваивается новой локальной переменной a, которая объявлена директивой var. Такое определение переменных эквивалентно следующей записи:
var a = (b = 0);

Если вы уже объявили переменные, то цепочное представление будет отлично работать и не создаст нежелательных глобальных переменных:
function foo() {
   var a, b;
   a = b = 0; // обе переменных являются локальными
}

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

Побочный эффект забытой декларации var

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

Глобальная переменная, объявленная декларацией var (созданная в программе вне функций) не может быть удалена.
Подразумеваемая глобальная переменная, созданная без объявления (вне зависимости от места создания) может быть удалена.

Технически, подразумеваемая глобальная переменная является свойством глобального объекта, а не переменной. Свойства могут быть удалены с помощью оператора delete, а переменные - нет:
// определяем три глобальных переменных
var global_var = 1;
global_novar = 2; // плохо
(function () {
   global_fromfunc = 3; // плохо
}());

// Пробуем удалить
delete global_var; // false
delete global_novar; // true
delete global_fromfunc; // true

// Проверка удаления
typeof global_var; // "number"
typeof global_novar; // "undefined"
typeof global_fromfunc; // "undefined"

Доступ к глобальному объекту

В браузерах глобальный объект доступен в любой точке кода через свойство window (до тех пор, пока вы не сделаете что-нибудь специальное или неожиданное, например, объявите локальную переменную с именем window). Но в других средах окружения данное удобное свойство может быть доступно другим способом (или даже вообще недоступно программисту). Если вам нужен доступ к глобальному объекту без использования идентификатора window, то вы можете использовать следующий способ на любом уровне вложенного пространства имен функции:
var global = (function () {
   return this;
}());

Таким образом, всегда можно получить доступ к глобальному объекту, так как внутри функции, которая вызвана как функция (а не как конструктор с декларацией new) this всегда указывает на глобальный объект.
Шаблон одного объявления var

Использование одного объявления var вверху вашей функции является очень полезной практикой. Такой метод имеет следующие преимущества:

Обеспечивается единственное место для объявления всех локальных переменных функции.
Предотвращаются логические ошибки, когда переменная используется раньше, чем она объявляется.
Помогает помнить об объявлении локальных переменных и, следовательно, сокращает количество глобальных переменных.

Шаблон с одним объявлением var выглядит следующим образом:
function func() {
   var a = 1,
       b = 2,
       sum = a + b,
       myobject = {},
       i,
       j;
   // Код функции...
}

Вы используете одну декларацию var для объявления нескольких переменных, разделенных запятой. Отличным дополнением будет инициализация переменных исходными данными при их объявлении. Так предотвращаются логические ошибки (все неинициализированные переменные по умолчанию получают значение undefined) и улучшается читабельность кода. Когда вы позже будете просматривать код, как вы сможете определить назначение переменной по ее начальному значению (например, сразу будет видно, что это объект или целое число).

Также можно выполнить операцию при объявлении переменной, например, sum = a + b из предыдущего кода примера. Другим рабочим примером служит оперирование с DOM. Вы можете назначать ссылки на элементы DOM локальным переменным при объявлении:
function updateElement() {
   var el = document.getElementById("result"),
       style = el.style;
   // выполняем операции с el и style...
}

Подъем: проблема с разбросанными декларациями var

JavaScript допускает использование нескольких деклараций var в любом месте функции, и они действуют одинаково, вне зависимости от места размещения. Данная особенность известна как "подъем". Такое функционирование может привести к логическим ошибкам, когда вы используете переменную, а затем объявляете ее для дальнейшего кода функции. Для JavaScript, так как переменная находится в одном пространстве имен (в одной функции), предполагается ее объявление, даже если они используется до директивы var. Например
// плохо
myname = "global"; // глобальная переменная
function func() {
    alert(myname); // "undefined"
    var myname = "local";
    alert(myname); // "local"
}
func();

В данном примере ожидается, что первый вызов функции alert() выдаст сообщение “global”, а второй - “local.” Разумное ожидание, так как при первом вызове локальная переменная myname не объявлена, и функция должна использовать глобальную переменную myname. Но в действительности все работает иначе. Первый вызов функции alert() выдаст “undefined” потому, что myname рассматривается как объявленная локальная переменная в функции (хотя объявление будет позже). Все объявления переменных поднимаются вверх функции. Следовательно, чтобы избежать такого типа ошибок, нужно объявлять все переменные вверху функции.

Предыдущий пример будет действовать так, как если бы он был реализован следующим образом:
myname = "global"; // глобальная переменная
function func() {
   var myname; // то же, что и -> var myname = undefined;
   alert(myname); // "undefined"
   myname = "local";
   alert(myname); // "local"
}
func();

Стоит упомянуть, что в действительности реализация кода более сложная. Есть две стадии обработки кода. На первой стадии создаются переменные, объявления функций и формальные параметры, а также определяется контекст. На второй стадии выполняется код, вычисление функций и создаются неквалифицированные идентификаторы (необъявленные переменные). Но для практического применения можно использовать концепцию подъема, которая хорошо описывает поведение кода.

Вторая из четырех частей урока, посвященного качественному коду JavaScript, который будет легко поддерживать и развивать.
Цикл for

В цикле for вы проходите по массиву или по объекту, похожему на массив, например arguments или HTMLCollection. Обычно использование цикла for выглядит следующим образом:
// Плохой пример цикла
for (var i = 0; i < myarray.length; i++) {
   // Делаем что-нибудь с элементом myarray[i]
}

Проблема с таким использованием заключается в том, что свойство length массива запрашивается на каждом шаге цикла. Такое функционирование может привести к замедлению кода, особенно когда myarray не массив, а объект HTMLCollection.

HTMLCollections является объектом, который возвращается методами DOM:
    document.getElementsByName()
    document.getElementsByClassName()
    document.getElementsByTagName()

Существует также несколько других объектов HTMLCollections, которые были введены до стандарта DOM и все еще используются. Они включают (среди прочих):

document.images: все элементы IMG на странице
document.links : все элементы A
document.forms : все формы
document.forms[0].elements : все поля в первой форме на странице

Проблема с коллекциями заключается в том, что они все являются запросами к документу (странице HTML). Это значит, что каждый раз, когда вы запрашиваете length у коллекции, вы в действительности формируете запрос к DOM, а операции с DOM в общем достаточно затратны по времени выполнения.

Поэтому лучшим вариантом использования цикла for будет кеширование длины обрабатываемого массива (или коллекции):
for (var i = 0, max = myarray.length; i < max; i++) {
   // Делаем что-нибудь с элементом myarray[i]
}

Так вы получаете значение свойства length только один раз и используете его во всем цикле.

Кэширование свойства length для обработки объекта HTMLCollections увеличивает скорость во всех браузерах (от двух раз в Safari 3 и до 190 раз в IE7).

Обратите внимание, что когда вы явно намереваетесь модифицировать коллекцию в цикле (например, добавляя элементы DOM), вероятно, что длина будет изменена и не будет являться константой.

Следуя концепции одной декларации var вы также можете вынести var из цикла и сделать код следующим образом:
function looper() {
   var i = 0,
        max,
        myarray = [];
   // ...
   for (i = 0, max = myarray.length; i < max; i++) {
      // Делаем что-нибудь с элементом myarray[i]
   }
}

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

Еще одним улучшением цикла будет замена i++ на одно из следующих выражений:
i=i+ 1  
i += 1  

JSLint побуждает к такому действию. Причина заключается в том, что ++ и -- приводят к излишней запутанности. Если вы сомневаетесь, то можно установить опцию JSLint plusplus в значение false (по умолчанию она имеет значение true).

Также можно дополнительно оптимизировать шаблон цикла:

Использовать на одну переменную меньше (без max).
Вести счет на уменьшение до 0, что обычно выполняется быстрее из-за более эффективного сравнения с 0, чем со числовым значением длины массива.

Первая модификация шаблона будет выглядеть так:
 	var i, myarray = [];
 	for (i = myarray.length; i--;) {
 	   // Делаем что-нибудь с элементом myarray[i]
 	}

В вторая модификация будет использовать цикл while:
 	var myarray = [],
 	    i = myarray.length;
 	while (i--) {
 	   // Делаем что-нибудь с элементом myarray[i]
 	}

Цикл for-in

Цикл for-in используется для прохода по объектам, не являющимися массивом. Такой цикл еще имеет название перечисление.

Технически можно использовать цикл for-in для массивов (так как все массивы JavaScript являются объектами). Но такое применение не рекомендуется, так как может привести к логическим ошибкам, если массив расширен пользовательским функционалом. В дополнение, порядок (последовательность) списка свойств не гарантируется в цикле for-in. Поэтому предпочтительно использовать цикл for для массивов, а цикл for-in для объектов.

Важно использовать метод hasOwnProperty() во время итераций по свойствам объекта для фильтрации свойств, которые могут попасть в объект из цепочки прототипа.

Рассмотрим следующий пример:
 	// Объект
 	var man = {
 	   hands: 2,
 	   legs: 2,
 	   heads: 1
 	};
 	 
 	// Где-то в коде
 	// метод добавляется ко всем объектам
 	if (typeof Object.prototype.clone === "undefined") {
 	   Object.prototype.clone = function () {};
 	}

В данном примере у нас есть простой объект man. Где-то до или после определения объекта man, прототип Object был расширен методом clone(). Цепочка прототипа действует, а значит все объекты автоматически получат доступ к новому методу. Чтобы избежать вывода метода clone() при перечислении объекта man, нужно использовать метод hasOwnProperty() для фильтрации свойств прототипа. Отказ от фильтрации может привести к выводу функции clone(), что будет причиной странного поведения в большинстве случаев:
 	// 1.
 	// Цикл for-in
 	for (var i in man) {
 	   if (man.hasOwnProperty(i)) { // фильтр
 	      console.log(i, ":", man[i]);
 	   }
 	}
 	/* Результат в консоли
 	hands : 2
 	legs : 2
 	heads : 1
 	*/
 	// 2.
 	// Плохой пример:
 	// Цикл for-in без проверки свойств методом hasOwnProperty()
 	for (var i in man) {
	   console.log(i, ":", man[i]);
 	}
 	/*
 	Результат в консоли
 	hands : 2
 	legs : 2
 	heads : 1
 	clone: function()
 	*/

Другой вариант использования метода hasOwnProperty() - вызвать его для Object.prototype:
 	for (var i in man) {
 	   if (Object.prototype.hasOwnProperty.call(man, i)) { // фильтр
 	      console.log(i, ":", man[i]);
 	   }
 	}

Преимуществом такого варианта является обход коллизии имен в случае, если объект man имеет переопределенный метод hasOwnProperty. Можно обойти длительный просмотр свойств прототипа Object с помощью использования локальной переменной для его кеширования:
 	var i, hasOwn = Object.prototype.hasOwnProperty;
 	for (i in man) {
 	    if (hasOwn.call(man, i)) { // фильтр
	        console.log(i, ":", man[i]);
 	    }
 	}

Если говорить откровенно, то пренебрежение методом hasOwnProperty() не является ошибкой. В зависимости от задач и достоверности кода вы можете не использовать его и немного увеличить скорость выполнения цикла. Но если вы не уверены в содержании объекта (и его цепочки прототипа), то лучше добавить проверку методом hasOwnProperty().

Вариацией формата (которая не проходит JSLint) является отбрасывание фигурных скобок и размещение if на той же строке. Преимуществом такого формата является то, что цикл читается как “для каждого элемента, который имеет собственное свойство X, сделать что-то с X”. Также будет использоваться меньше отступов для тела цикла:
 	// Предупреждение: не проходит JSLint
 	var i, hasOwn = Object.prototype.hasOwnProperty;
 	for (i in man) if (hasOwn.call(man, i)) { // фильтр
 	    console.log(i, ":", man[i]);
 	}

(Не) расширяете встроенные прототипы

Расширение свойств прототипов функций конструкторов является мощным инструментом для добавления функционала. Но каждая палка имеет два конца.

Очень заманчиво расширить прототипы встроенных конструкторов, таких как Object(), Array(), или Function(). но такое действие может существенно снизить уровень поддержки кода, так как он становится менее предсказуемым. Другие разработчики, использующие ваш код, вероятно, предполагают, что встроенные методы JavaScript работают единообразно и не ожидают дополнений.

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

Следовательно, лучше всего не расширять встроенные прототипы. Вы можете сделать исключение из данного правила только если соблюдаются все ниже перечисленные условия:

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

Если все три условия выполняются, то вы можете расширить прототип, используя следующий шаблон:
 	if (typeof Object.prototype.myMethod !== "function") {
 	   Object.protoype.myMethod = function () {
 	      // реализация...
 	   };
 	}

Шаблон для switch

Вы можете улучшить читабельность и целостность выражений switch с помощью следующего шаблона:
 	var inspect_me = 0,
 	    result = '';
 	switch (inspect_me) {
 	case 0:
 	   result = "zero";
 	   break;
 	case 1:
 	   result = "one";
 	   break;
 	default:
	   result = "unknown";
 	}

Шаблон сделан в соответствии со следующими соглашениями по стилю:

Выравнивание каждого case по switch;
Отступ кода в каждом case;
Каждый case заканчивается break;.
Избегаем перехода с одного case на другой (когда опущен break). Если такой вариант необходим, то он должен быть тщательно документирован, так как будет выглядеть ошибкой в глазах читателя вашего кода;
Каждый switch заканчивается default: для формирования результата даже в случае несовпадения ни с одним условием case .

Третья часть урока, посвященного коду JavaScript. Простые рекомендации для создания легко поддерживаемого проекта.
Избегайте подразумеваемого преобразования типов

JavaScript подразумевает преобразование типов переменных при их сравнении. Поэтому такие сравнения как false == 0 или "" == 0 возвращают true.

Чтобы избежать проблем, вызванных подразумеваемым преобразованием типов, всегда используйте операторы === и !== для проверки и значений и типов сравниваемых выражений:
 	var zero = 0;
 	if (zero === false) {
	   // код, размещенный здесь не выполнится, так как zero является 0, а не false
 	}
 	 
 	// плохо
 	if (zero == false) {
 	   // данный блок кода будет всегда выполняться
 	}

Существует другая школа программирования, в рамках которой принято считать, что чрезмерно использовать оператор ===, когда достаточно оператора == . Например, когда используется typeof, который возвращает строку, нет причин требовать жесткого соответствия. Но JSLint требует жесткого соответствия. И, кроме того, код будет выглядеть более целостным и уменьшит количество размышлений при чтении ("Данный оператор == используется намеренно или по ошибке?").

Избегайте использования eval()

Функция eval() берет произвольную строку и выполняет ее как код JavaScript. Если код известен (а не определяется в ходе выполнения процесса), вообще нет причин использовать eval(). Если код динамически генерируется во время выполнения, то часто возможно достичь цели лучшим методом, чем использование eval().Например, использование записи с квадратными скобками для динамического доступа к свойствам лучше и проще:
 	// плохо
 	var property = "name";
 	alert(eval("obj." + property));
 	 
 	// предпочтительно делать так
 	var property = "name";
 	alert(obj[property]);

Использование eval() также может повлиять на безопасность, так как вы можете выполнять код (например, полученный из сети), который наносит ущерб. Достаточно распространенная порочная практика работы с ответом JSON на запрос AJAX. В данном случае лучше использовать встроенные методы браузера для разбора ответа JSON, чтобы решить задачу безопасным методом и правильно. Для браузеров, которые не поддерживают JSON.parse(), можно использовать библиотеку с JSON.org.

Также важно помнить, что передача строк функциям setInterval(), setTimeout(), и конструктору Function() в большинстве случаев похоже на использование eval(). Следовательно, таких действий надо избегать. На заднем плане JavaScript оценивает и выполняет строки, которые вы передаете, как программный код:
 	// плохо
 	setTimeout("myFunc()", 1000);
 	setTimeout("myFunc(1, 2, 3)", 1000);
 	 
 	// предпочтиетльно
 	setTimeout(myFunc, 1000);
 	setTimeout(function () {
 	   myFunc(1, 2, 3);
 	}, 1000);

Использование нового конструктора new Function() похоже на eval(), поэтому к нему надо относиться с осторожностью. Это мощный инструмент, но часто используемый неправильно. Если вам абсолютно необходимо использовать eval(), рассмотрите вместо него использование new Function(). Есть небольшое потенциальное преимущество, так как код, определяемый в new Function(), будет запускаться в локальном пространстве функции, поэтому переменные, определенные с директивой var в определяемом коде не будут становиться глобальными автоматически. Другой способ избежать автоматического определения глобальных переменных - оборачивать вызов eval() в функцию.

Рассмотрим следующий пример. Здесь только un остается глобальной переменной, загрязняющей пространство имен:
 	console.log(typeof un); // "undefined"
 	console.log(typeof deux); // "undefined"
 	console.log(typeof trois); // "undefined"
 	 
 	var jsstring = "var un = 1; console.log(un);";
 	eval(jsstring); // Записывает "1"
 	 
 	jsstring = "var deux = 2; console.log(deux);";
 	new Function(jsstring)(); // Записывает "2"
 	 
 	jsstring = "var trois = 3; console.log(trois);";
 	(function () {
 	   eval(jsstring);
	}()); // Записывает "3"
 	 
 	console.log(typeof un); // число
 	console.log(typeof deux); // undefined
 	console.log(typeof trois); // undefined

Другое отличие между eval() и конструктора new Function()заключается в том, что eval() может пересекаться с цепочкой пространства имен, а выполнение Function происходит в песочнице. Не важно, где вы выполняете Function, она использует только глобальное пространство имен. Поэтому она меньше загрязняет локальное пространство имен. В следующем примере eval() может получить доступ и модифицировать переменные в своем внешнем пространстве имен, а Function не может (использование Function и new Function идентично):
 	(function () {
 	   var local = 1;
 	   eval("local = 3; console.log(local)"); // Записывает "3"
 	   console.log(local); // Записывает "3"
 	}());
 	 
 	(function () {
 	   var local = 1;
 	   Function("console.log(typeof local);")(); // Записывает "undefined"
 	}());

Преобразование числа с помощью parseInt()

Используя parseInt(), вы можете получить число из строки. Функция принимает второй параметр - основание системы счисления, который часто опускается. А зря. Проблема проявляется, когда надо разобрать строку, начинающуюся с 0: например, часть даты, которую вводят в поле формы. Строка, которая начинается на 0, обрабатывается как восьмеричное число (основание 8 ), что было определено в ECMAScript 3 (но изменено в ECMAScript 5). Для исключения несовместимости и неожиданных результатов всегда следует использовать параметр основания системы счисления:
	var month = "06",
 	    year = "09";
 	month = parseInt(month, 10);
 	year = parseInt(year, 10);

В данном примере, если вы опустите параметр основания системы счисления ( вызовете функцию как parseInt(year)), то получите значение 0, потому что “09” подразумевается как восьмеричное число (как будто вы сделали вызов parseInt( year, 8 )), а 09 - неправильное число по основанию 8.

Альтернативные методы преобразования строки в число:
 	+"08" // результат 8
 	Number("08") // 8

Данные методы часто выполняются быстрее parseInt(), потому что parseInt() делает разбор строки, а не простое конвертирование. Но если вы предполагаете, что ввод может быть в виде “08 hello”, то parseInt() вернет число, а другие методы - потерпят неудачу с возвратом NaN.

Требования к коду

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

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

Отступы

Код без отступов очень трудно читать. Очень важно установить стандарт на отступы.

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

А где следует делать отступы? Правило простое - везде, где есть фигурные скобки. То есть в теле функций, циклах (do, while, for, for-in), операторах if и switch, и свойствах object . Следующий код показывает примеры использования отступов:
 	function outer(a, b) {
 	    var c = 1,
 	        d = 2,
 	        inner;
 	    if (a > b) {
 	        inner = function () {
 	            return {
 	                r: c - d
 	            };
 	        };
 	    } else {
 	        inner = function () {
 	            return {
 	                r: c + d
 	            };
 	        };
 	    }
 	    return inner;
 	}

Фигурные скобки

Фигурные скобки всегда следует использовать, даже в случаях, когда они являются опциями. Технически, когда вы имеет только одно выражение в операторе if или for, фигурные скобки не требуются, но их следует использовать все равно. Они делают код более целостным и простым для расширения.

Представим, что у вас есть цикл с одним выражением. Вы можете опустить скобки, что не будет синтаксической ошибкой:
 	// плохо
 	for (var i = 0; i < 10; i += 1)
 	   alert(i);

Но что если позже потребуется добавить еще одну строку в тело цикла?
 	// плохо
 	for (var i = 0; i < 10; i += 1)
 	   alert(i);
 	   alert(i + " is " + (i % 2 ? "odd" : "even"));

Вторая функция alert находится вне цикла, а отступы могут сыграть с вами плохую шутку. Лучше всего в расчете на перспективу всегда использовать скобки, даже для однострочного блока:
 	// предпочтительно
 	for (var i = 0; i < 10; i += 1) {
 	   alert(i);
 	}

Также и для условий:
 	// плохо
 	if (true)
 	   alert(1);
 	else
 	   alert(2);
 	 
 	// предпочтительно
 	if (true) {
 	   alert(1);
 	} else {
 	   alert(2);
 	}

Положение открытой скобки

Часто возникает вопрос, где располагать открытую скобку - на той же строке или на следующей?
 	if (true) {
 	   alert("Сообщение!");
 	}

Или
 	if (true)
 	{
 	   alert("Сообщение");
 	}

В данном примере положение скобки является вопросом предпочтения. Но есть случаи, когда программа будет вести себя по-разному в зависимости от положения скобки. Такая ситуация возникает из-за механизма вставки точки с запятой - JavaScript не разбирается, когда вы решили не заканчивать строку правильно, и добавит точку с запятой за вас. Данное поведение может вызвать проблемы, когда функция возвращает литеральный объект, а открытая скобка находится на следующей строке:
 	// предупреждение: неожиданный return
 	function func() {
 	   return
 	  // далее следует блок кода, который никогда не выполнится
 	   {
 	      name : "Batman"
 	   }
 	}

Если вы ожидали, что данная функция вернет объект со свойством name, то будет неприятно удивлены. По причине подразумеваемой точки с запятой функция вернет undefined. Предыдущий код эквивалентен следующему блоку:
 	// предупреждение: неожиданный return
 	function func() {
 	   return undefined;
 	  // далее следует блок кода, который никогда не выполнится
 	   {
 	      name : "Batman"
 	   }
 	}

В качестве вывода можно рекомендовать всегда использовать фигурные скобки и всегда размещать открытую скобку на той же строке, что и предыдущее выражение:
 	function func() {
 	   return {
	      name : "Batman"
	   };
 	}

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

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

Использование пробелов

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

Хорошим местом для размещения пробела являются:

После точки с запятой, которая разделяет части цикла: например, for (var i = 0; i < 10; i += 1) {...}
Инициализация нескольких переменных в цикле for (в примере, i и max): for (var i = 0, max = 10; i < max; i += 1) {...}
После запятой, которая разделяет элементы массива: var a = [1, 2, 3];
После запятой в свойствах объекта и после двоеточия, которое разделяет имена свойств и их значения: var o = {a: 1, b: 2};
Разделять аргументы функций: myFunc(a, b, c)
Перед фигурными скобками в объявлении функции: function myFunc() {}
После оператора function в объявлении анонимной функции: var myFunc = function () {};

Хорошей практикой также является разделение всех операторов и их операндов пробелами, особенно использование пробелов до и после +, -, *, =, <, >, <=, >=, ===, !==, &&, ||, +=, и так далее:
 	// последовательное использование пробелов
 	// делает код легким для прочтения
 	var d = 0,
 	    a = b + 1;
 	if (a && b && c) {
 	    d = a % c;
 	    a += d;
 	}
 	 
 	// плохо
 	// отсутствие или непостоянство в использовании пробелов
 	// может существенно затруднить чтение кода
 	var d=0,
 	    a=b+1;
 	if (a&&b&&c) {
 	    d= a % c;
 	    a+=d;
 	}

И несколько слов о пробелах рядом с фигурными скобками, которые помогут улучшить внешний вид кода:

Перед открытой фигурной скобкой ({) в функциях, выражении if-else , циклах и объектах;
Между закрытой фигурной скобкой (}) и else или while.

Для тех, кто утверждает, что использование пробелов увеличивает размер кода можно напомнить о минимизации, которую можно использовать для рабочих версий проекта.

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

Соглашение об именовании

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

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

Заглавная буква в начале имени конструктора

JavaScript не имеет классов, но имеет функцию конструктор, которая вызывается с new:
	var adam = new Person();

Так к
Добавлено: 20 Августа 2013 02:36:00 Добавил: Андрей Ковальчук Нравится 0
Добавить
Комментарии:
Нету комментариев для вывода...