Четверг, 18.04.2024, 18:28
Приветствую Вас Гость | RSS

Программирование на ЯВУ

Меню сайта
Статистика

Онлайн всего: 1
Гостей: 1
Пользователей: 0
Форма входа

Лекция 4.

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

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

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

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

Лекция имеет следующую структуру:

  • Называйте вещи своими именами - легкие, короткие и удобочитаемые имена переменных и функций
  • Избегайте глобальных переменных и функций
  • Придерживайтесь строгого стиля кодирования
  • Комментируйте насколько необходимо, но не больше
  • Избегайте смешивания с другими технологиями
  • Используйте сокращенную нотацию, когда это имеет смысл
  • Разбивайте на модули - одна функция на задачу
  • Улучшайте постепенно
  • Предусматривайте конфигурацию и перевод
  • Избегайте глубокой вложенности кода
  • Оптимизируйте циклы
  • Минимизируйте доступ к DOM
  • Не используйте особенности браузеров
  • Не доверяйте никаким данным
  • Добавляйте с помощью JavaScript функции, не создавайте слишком много контента
  • Опирайтесь на плечи предшественников
  • Код разработки не является рабочим кодом

Называйте вещи своими именами - легкие, короткие и удобочитаемые имена переменных и функций

Это не требует большого ума, но ужасно, как часто можно встретить в JavaScript переменные вида x1, fe2 или xbqne - или на другом конце спектра - с именами переменных типа incrementorForMainLoopWhichSpansFromTenToTwenty или createNewMemberIfAgeOverTwentyOneAndMoonIsFull.

Ни то, ни другое не имеет большого смысла - хорошие имена переменных и функций должны легко пониматься и говорить, что происходит - ни больше и ни меньше. Одной из ловушек, которой надо избежать, будет соединение значений и функций в именах. Функция с именем isLegalDrinkingAge() [ДопустимыйВозрастПьянства()] имеет больше смысла, чем isOverEighteen()[СтаршеВосемнадцати()], так как допустимый возраст принятия алкоголя меняется в различных странах, и существуют другие вещи, кроме принятия алкоголя, которые могут быть ограничены по возрасту.

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

Например, если имеется переменная с именем familyName, и предполагается, что это будет строка, необходимо писать в венгерской нотации sFamilyName. Объект с именем member будет oMember, а булева переменная с именем isLegal будет bIsLegal. Это очень информативно для некоторых разработчиков, но для других кажется избыточным - каждый может решить самостоятельно, использовать это или нет.

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

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

Избегайте глобальных переменных и функций

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

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

var current = null;
function init(){...}
function change(){...}
function verify(){...}
 

Можно защитить это от перезаписи, используя литерал объекта:

var myNameSpace = {
 current:null,
 init:function(){...},
 change:function(){...},
 verify:function(){...}
}
 

Это помогает решить проблему, но имеется недостаток - чтобы вызывать функции или изменить значение переменной, всегда требуется использовать имя основного объекта: init() будет myNameSpace.init(), current будет myNameSpace.current, и т.д. Это может несколько надоедать.

Легче поместить все в анонимную функцию и защитить область действия таким образом. Это также означает, что не требуется изменять синтаксис с function name() на name:function(). Этот прием называется шаблон модуля (Module Pattern):

myNameSpace = function(){
 var current = null;
 function init(){...}
 function change(){...}
 function verify(){...}
}();
 

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

myNameSpace = function(){
 var current = null;
 function verify(){...}
 return{
 init:function(){...}
 change:function(){...}
 }
}();
 

Это в значительной степени возвращает нас к исходной точке в смысле ссылок от одного к другому и изменения синтаксиса. Я поэтому предпочитаю делать что-то вроде следующего (и называю "раскрытие шаблона модуля"):

myNameSpace = function(){
 var current = null;
 function init(){...}
 function change(){...}
 function verify(){...}
 return{
 init:init,
 change:change
 }
}();
 

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

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

myNameSpace = function(){
 var current = null;
 function init(){...}
 function change(){...}
 function verify(){...}
 return{
 init:init,
 set:change
 }
}();
 

Вызов myNameSpace.set() будет вызывать теперь метод change().

Если не требуется, чтобы какие-либо переменные или функции были доступны извне, поместите просто всю конструкцию в другие скобки, чтобы выполнить ее, не присваивая никакого имени:

(function(){
 var current = null;
 function init(){...}
 function change(){...}
 function verify(){...}
})();
 

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

Придерживайтесь строгого стиля кодирования

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

Проще всего проверить синтаксическое качество кода можно, пропуская его через JSLint (http://www.jslint.com/) - инструмент валидации JavaScript, который создает подробный отчет о синтаксических проблемах и их значении. Были также написаны расширения для редакторов (например, JS Tools для TextMate - http://andrewdupont.net/2006/10/01/javascript-tools-textmate-bundle/), которые автоматически проверяют код при его сохранении.

JSLint может быть немного придирчив в отношении возвращаемых результатов - и его разработчик Дуглас Крокфорд говорит - он может оскорбить ваши чувства. Я обнаружил, однако, что стал писать код значительно лучше, с тех пор как установил пакет TextMate JS и начал подвергать код проверке JSLint.

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

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

Комментируйте насколько необходимо, но не больше

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

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

Комментарии никому не мешают, если они сделаны правильно. Мы вернемся к этому вопросу в конце этой лекции, но давайте скажем, что если комментарии отсутствуют в коде, который видят конечные пользователи, то что-то происходит неправильно.

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

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

module = function(){
 var current = null;
 function init(){
 };
/*
 function show(){
 current = 1;
 };
 function hide(){
 show();
 };
*/
 return{init:init,show:show,current:current}
}();
 

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

module = function(){
 var current = null;
 function init(){
 };
/*
 function show(){
 current = 1;
 };
 function hide(){
 show();
 };
// */
 return{init:init,show:show,current:current}
}();
 

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

Для более крупных приложений комментированная документация в стиле JavaDoc (http://www.oracle.com/technetwork/java/javase/documentation/index-137868.html) имеет большой смысл - общая документация продукта создается по мере написания кода. Успех библиотеки Yahoo User Interface обусловлен частично этим, и существует даже инструмент, который можно использовать для создания такой же документации для своих продуктов (http://yuiblog.com/blog/2008/12/08/yuidoc/). Не беспокойтесь особенно об этом, пока не станете более опытным разработчиков сценариев JavaScript - JavaDoc упомянут здесь скорее для полноты представления.

Избегайте смешивания с другими технологиями

Хотя и возможно создать все необходимое в документе с помощью JavaScript и DOM, это не обязательно самый эффективный способ работы. Следующий код создает красную границу вокруг каждого поля ввода, когда его класс будет "mandatory", и в этом нет ничего сложного.

var f = document.getElementById('mainform');
var inputs = f.getElementsByTagName('input');
for(var i=0,j=inputs.length;i<j;i++){
 if(inputs[i].className === 'mandatory' &&
 inputs[i].value === ''){
 inputs[i].style.borderColor = '#f00';
 inputs[i].style.borderStyle = 'solid';
 inputs[i].style.borderWidth = '1px';
 }
}
 

Это работает, однако означает, что если в последующем потребуется изменить эти стили, придется просматривать код JavaScript и вносить в него изменения. Чем сложнее изменение, тем труднее будет выполнить редактирование. Более того, не каждый разработчик на JavaScript достаточно опытен или интересуется CSS, что означает много дополнительной работы, пока будет получен требуемый результат. Добавляя класс с именем "error" в элемент, когда имеется ошибка, можно гарантировать, что данные стиля будут хранится в CSS, что будет более подходящим:

var f = document.getElementById('mainform');
var inputs = f.getElementsByTagName('input');
for(var i=0,j=inputs.length;i<j;i++){
 if(inputs[i].className === 'mandatory' &&
 inputs[i].value === ''){
 inputs[i].className += ' error';
 }
}
 

Это значительно более эффективно, так как CSS предназначен для каскадного (иерархического) применения в документе. Пусть, например, желательно скрыть в документе все DIV с определенным классом. Можно в цикле просмотреть все DIV, проверить их классы, и затем изменить их совокупность стилей. В новых браузерах можно использовать процессор селекторов CSS, а затем изменить совокупность стилей. Проще всего, однако, использовать JavaScript для задания класса на элементе предке и использовать в CSS синтаксис вдоль строк element.triggerclass div.selectorclass{}. Оставьте работу реального сокрытия DIV дизайнеру CSS, так как он лучше знает, как это сделать.

Используйте сокращенную нотацию, когда это имеет смысл

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

Объекты являются, вероятно, наиболее универсальной вещью, имеющейся в JavaScript. Старая школа записывала их примерно следующим образом:

var cow = new Object();
cow.colour = 'brown';
cow.commonQuestion = 'What now?';
cow.moo = function(){
 console.log('moo');
}
cow.feet = 4;
cow.accordingToLarson = 'will take over the world';
 

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

var cow = {
 colour:'brown',
 commonQuestion:'What now?',
 moo:function(){
 console.log('moo');
 },
 feet:4,
 accordingToLarson:'will take over the world'
};
 

Массивы являются в JavaScript несколько путаной темой. Можно найти множество сценариев, определяющих массив (Array) следующим образом:

var aweSomeBands = new Array();
aweSomeBands[0] = 'Bad Religion';
aweSomeBands[1] = 'Dropkick Murphys';
aweSomeBands[2] = 'Flogging Molly';
aweSomeBands[3] = 'Red Hot Chili Peppers';
aweSomeBands[4] = 'Pornophonique';
 

Здесь присутствует много бесполезного повторения. Все это можно переписать значительно быстрее, используя для массива сокращение [ ]:

var aweSomeBands = [
 'Bad Religion',
 'Dropkick Murphys',
 'Flogging Molly',
 'Red Hot Chili Peppers',
 'Pornophonique'
];
 

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

Условия можно сократить, используя "троичную запись". Например, следующая конструкция определяет переменную как 1 или -1, в зависимости от значения другой переменной:

var direction;
if(x > 100){
 direction = 1;
} else {
 direction = -1;
}
 

Это можно сократить до одной строки:

var direction = (x > 100) ? 1 : -1;
 

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

Другая распространенная ситуация в JavaScript состоит в задании предварительно заданного значения для переменной, если оно не определено, как в следующем случае:

if(v){
 var x = v;
} else {
 var x = 10;
}
 

В качестве сокращенной записи для этого случая используют две вертикальные черты:

var x = v || 10;
 

Это выражение автоматически задает для x значение 10, если v не определено - вот и все.

Разбивайте на модули - одна функция на задачу

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

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

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

function addLink(text,url,parentElement){
 var newLink = document.createElement('a');
 newLink.setAttribute('href',url);
 newLink.appendChild(document.createTextNode(text));
 parentElement.appendChild(newLink);
}
 

Это работает нормально, но вам может понадобиться добавлять различные атрибуты, в зависимости от элементов, к которым применяется ссылка. Например:

function addLink(text,url,parentElement){
 var newLink = document.createElement('a');
 newLink.setAttribute('href',url);
 newLink.appendChild(document.createTextNode(text));
 if(parentElement.id === 'menu'){
 newLink.className = 'menu-item';
 }
 if(url.indexOf('mailto:')!==-1){
 newLink.className = 'mail';
 }
 parentElement.appendChild(newLink);
}
 

Это делает функцию более специфической, которую труднее применять в различных ситуациях. Более аккуратный способ состоит в возвращении ссылки и реализации дополнительных случаев в основных функциях, которые этого требуют. Это превращает addLink() в более общую функцию createLink():

function createLink(text,url){
 var newLink = document.createElement('a');
 newLink.setAttribute('href',url);
 newLink.appendChild(document.createTextNode(text));
 return newLink;
}

function createMenu(){
 var menu = document.getElementById('menu');
 var items = [
 {t:'Home',u:'index.html'},
 {t:'Sales',u:'sales.html'},
 {t:'Contact',u:'contact.html'}
 ];
 for(var i=0;i<items.length;i++){
 var item = createLink(items[i].t,items[i].u);
 item.className = 'menu-item';
 menu.appendChild(item);
 }
}
 

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

Улучшайте постепенно

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

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

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

Проблема в том, что если JavaScript будет отключен, ссылки все равно выводятся, но каждый вид поиска будет возвращать стандартные результаты из web, так как действие формы не изменяется. Решение было очень простым: вместо ссылок варианты были представлены как группа радио-кнопок, которые делали ветвление к различным сценариям специализированного поиска, используя серверный сценарий.

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

Предусматривайте конфигурацию и перевод

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

Например, плеер Easy YouTube (http://icant.co.uk/easy-youtube/) имеет следующий конфигурационный объект:

/*
 Это конфигурация плеера. Скорее всего вам никогда 
не придется ничего здесь изменять, но иметь такую 
возможность неплохо, не так ли? 
*/
config = {
 CSS:{
 /* 
 Используемые в документе IDs. Сценарий будет получать
доступ к различным элементам плеера с помощью этих IDs, 
поэтому, если вы изменяете их в коде HTML ниже, 
не забудьте также изменить имена здесь! 
 */
 IDs:{
 container:'eytp-maincontainer',
 canvas:'eytp-playercanvas',
 player:'eytp-player',
 controls:'eytp-controls',

 volumeField:'eytp-volume',
 volumeBar:'eytp-volumebar',

 playerForm:'eytp-playerform',
 urlField:'eytp-url',

 sizeControl:'eytp-sizecontrol',

 searchField:'eytp-searchfield',
 searchForm:'eytp-search',
 searchOutput:'eytp-searchoutput'
 /* 
Отметим, что после последней записи в списке 
никогда не должна стоять запятая, так как 
иначе MSIE будет очень сердит!
 */
 },
 /*
 Это имена классов CSS, которые плеер добавляет 
динамически на панель громкости в определенных ситуациях.
 */
 classes:{
 maxvolume:'maxed',
 disabled:'disabled'
 /* 
Отметим, что после последней записи в списке 
никогда не должна стоять запятая, так как 
иначе MSIE будет очень сердит!

 */
 }
 },
 /* 
 Это конец определений CSS, отсюда и дальше вы можете 
изменять настройки плеера самостоятельно. 
 */
 application:{
 /*
 Базовый URL API YouTube. Он изменился во время 
разработки этого приложения, поэтому будет полезно 
сделать его параметром.
 */
 youtubeAPI:'http://gdata.youtube.com/apiplayer/cl.swf',
 /* 
 Ключ YouTube Developer,
 Замените его своим собственным, когда разместите 
плеер на сервере!!!!!
 */
 devkey:'AI39si7d...Y9fu_cQ',
 /*
 Увеличение/уменьшение громкости в процентах и сообщение 
о громкости, выводимое в скрытом поле формы (для 
считывателя экрана). $x в сообщении будет заменяться 
реальным значением.
 */
 volumeChange:10,
 volumeMessage:'volume $x percent',
 /*
 Количество результатов поиска и сообщение об ошибке, 
если результатов не будет.
 */
 searchResults:6,
 loadingMessage:'Searching, please wait',
 noVideosFoundMessage:'No videos found : (',
 /*
 Количество секунд повторения, когда пользователь 
нажимает кнопку обратной перемотки.
 */
 secondsToRepeat:10,
 /*
 Размер кадра фильма.
 */
 movieWidth:400,
 movieHeight:300
 /* 
Отметим, что после последней записи в списке 
никогда не должна стоять запятая, так как 
иначе MSIE будет очень сердит!

 */
 } 
}
 
Пример . (html, txt)

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

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

Избегайте глубокой вложенности кода

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

Другой проблемой с вложением являются имена переменных и циклы. Вы обычно начинаете первый цикл с переменной итератора i, и продолжаете, используя j,k,l и т.д. Это очень быстро может создать большую путаницу:

function renderProfiles(o){
 var out = document.getElementById('profiles');
 for(var i=0;i<o.members.length;i++){
 var ul = document.createElement('ul');
 var li = document.createElement('li');
 li.appendChild(document.createTextNode(o.members[i].name));
 var nestedul = document.createElement('ul');
 for(var j=0;j<o.members[i].data.length;j++){
 var datali = document.createElement('li');
 datali.appendChild(
 document.createTextNode(
 o.members[i].data[j].label + ' ' +
 o.members[i].data[j].value
 )
 );
 nestedul.appendChild(datali);
 }
 li.appendChild(nestedul);
 } 
 out.appendChild(ul);
}
 

Так как я использую здесь общие - в действительности выбрасываемые - имена переменных ul и li, мне нужны переменные nestedul и datali для вложенных элементов списка. Если вложение списка должно продолжаться еще глубже, понадобятся дополнительные имена переменных, и т.д. и т.д. Больше смысла имеет размещение задачи создания вложенных списков для каждого члена в свою собственную функцию, и вызов ее с правильными данными. Это также избавляет от цикла внутри цикла. Функция addMemberData() является достаточно общей и, скорее всего, может понадобиться и в других местах. С учетом этих соображений я переписал код следующим образом:

function renderProfiles(o){
 var out = document.getElementById('profiles');
 for(var i=0;i<o.members.length;i++){
 var ul = document.createElement('ul');
 var li = document.createElement('li');
 li.appendChild(document.createTextNode(data.members[i].name));
 li.appendChild(addMemberData(o.members[i]));
 } 
 out.appendChild(ul);
}
function addMemberData(member){
 var ul = document.createElement('ul');
 for(var i=0;i<member.data.length;i++){
 var li = document.createElement('li');
 li.appendChild(
 document.createTextNode(
 member.data[i].label + ' ' +
 member.data[i].value
 )
 );
 }
 ul.appendChild(li);
 return ul;
}
 

Оптимизируйте циклы

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

var names = ['George','Ringo','Paul','John'];
for(var i=0;i<names.length;i++){
 doSomeThingWith(names[i]);
}
 

Это означает, что каждый раз при выполнении цикла JavaScript необходимо прочитать длину массива. Можно избежать этого, сохраняя значение длины в другой переменной:

var names = ['George','Ringo','Paul','John'];
var all = names.length;
for(var i=0;i<all;i++){
 doSomeThingWith(names[i]);
}
 

Еще короче это можно сделать, создавая вторую переменную в предварительном операторе цикла:

var names = ['George','Ringo','Paul','John'];
for(var i=0,j=names.length;i<j;i++){
 doSomeThingWith(names[i]);
}
 

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

Минимизируйте доступ к DOM

Доступ к DOM в браузерах выполняется с большой нагрузкой. DOM является очень сложным API, и отображение в браузерах может требовать много времени. Это можно видеть при выполнении сложных Web-приложений, когда компьютер уже перегружен другой работой - изменения выполняются дольше или выводятся частично и т.д.

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

Не используйте особенности браузеров

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

Это пустая трата времени и усилий - необходимо создавать код на основе согласованных стандартов, как описано в данном курсе статей, а не для одного браузера. Сеть Web предназначена для всех, а не для элитной группы пользователей с современной конфигурацией. Так как рынок браузеров изменяется быстро, придется возвращаться к коду и продолжать его исправлять. Это неэффективно и неинтересно.

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

Не доверяйте никаким данным

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

Короче, проверяйте, что все поступающие в систему данные являются хорошими и точно соответствуют тому, что требуется. Это особенно важно на сервере при записи параметров полученных из URL. В JavaScript очень важно проверять тип параметров, передаваемых функциям (используя оператор typeof). Следующее будет ошибкой, если переменная members не будет массивом (например, для строки будет создан элемент списка для каждого символа строки):

function buildMemberList(members){
 var all = members.length;
 var ul = document.createElement('ul');
 for(var i=0;i<all;i++){
 var li = document.createElement('li');
 li.appendChild(document.createTextNode(members[i].name));
 ul.appendChild(li);
 }
 return ul;
}
 

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

function buildMemberList(members){
 if(typeof members === 'object' && 
 typeof members.slice === 'function'){
 var all = members.length;
 var ul = document.createElement('ul');
 for(var i=0;i<all;i++){
 var li = document.createElement('li');
 li.appendChild(document.createTextNode(members[i].name));
 ul.appendChild(li);
 }
 return ul;
 }
}
 

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

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

Поиск

Яндекс.Метрика