Опубликован: 01.07.2011 | Доступ: свободный | Студентов: 6428 / 1052 | Оценка: 4.07 / 3.64 | Длительность: 10:34:00
Лекция 4:

Современные методы применения JavaScript

< Лекция 3 || Лекция 4: 1234 || Лекция 5 >

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

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

Другой проблемой с вложением являются имена переменных и циклы. Вы обычно начинаете первый цикл с переменной итератора 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 элемента страницы, и вызывал функцию с этими данными в качестве параметра. Так как имя пользователя может быть любым символом UTF-8, в том числе символами цитирования и одиночных кавычек. Эти символы завершают любую строку, а оставшаяся часть будет ошибочными данными. Кроме того, любой пользователь, изменяющий HTML с помощью таких инструментов как Firebug или Opera DragonFly, может заменить имя пользователя чем угодно и ввести эти данные в функции.

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

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

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

Как можно видеть в некоторых из приведенных здесь примеров, создание большого объема кода HTML в JavaScript может быть достаточно сложным и ненадежным.

Особенно в Internet Explorer можно встретить всевозможные проблемы, изменяя документ, когда он еще загружается, и манипулируя контентом с помощью innerHTML.

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

Я обнаружил, что для создания приложения, которое существенно зависит от JavaScript, использование шаблона HTML и загрузка этого шаблона с помощью Ajax имеет значительно больше смысла. В этом случае инженеры сопровождения могут изменять структуру HTML и, что более важно, текст, не вмешиваясь при этом в код JavaScript. Единственное затруднение состоит в том, чтобы сообщить им, какие IDs нужны, и если имеются какие-то конструкции HTML, которые должны быть представлены в определенном порядке. Это можно сделать с помощью встроенных комментариев HTML (а затем удалить комментарии, когда загружается шаблон. Просмотрите исходный код шаблона Easy YouTube (http://icant.co.uk/easy-youtube/template.html) в качестве примера.

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

var playercontainer = document.getElementById('easyyoutubeplayer');
if(playercontainer){
  ajax('template.html');
}; 

function ajax(url){
  var request;
  try{
    request = new XMLHttpRequest();
  }catch(error){
    try{
      request = new ActiveXObject("Microsoft.XMLHTTP");
    }catch(error){
      return true;
    }
  }
  request.open('get',url,true);
  request.onreadystatechange = function(){
    if(request.readyState == 4){
      if(request.status){ 
        if(request.status === 200 || request.status === 304){
          if(url === 'template.html'){
            setupPlayer(request.responseText);
          }
        }
      }else{
        alert('Error: Could not find template...');
      }
    }
  };
  request.setRequestHeader('If-Modified-Since','Wed, 05 Apr 2006 00:00:00  GMT');
  request.send(null);
};

Таким образом, люди могут транслировать и изменять плеер любым образом, при этом код JavaScript изменять не требуется.

Опирайтесь на плечи предшественников

Вряд ли кто-то будет отрицать, что за последние несколько лет библиотеки и среды разработки JavaScript захватили рынок Web-разработки. И это совсем неплохо - если использовать их правильно. Все хорошие библиотеки JavaScript хотят одного и только одного: они хотят облегчить жизнь разработчика, решая проблемы несовместимости между различными браузерами и закрывая пробелы в поддержке браузеров. Библиотеки JavaScript предоставляют предсказуемую, функционирующую основу для построения.

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

Я предпочитаю использовать библиотеку Yahoo User Interface -YUI (http://developer.yahoo.com/yui), после чего следуют jQuery (http://jquery.com/), Dojo (http://www.dojotoolkit.org/) и Prototype (http://prototypejs.org/), но существуют десятки других хороших библиотек, и необходимо выбрать такую, которая лучше всего подходит вам и создаваемому продукту.

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

Код разработки не является рабочим кодом

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

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

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

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

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

Заключение

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

Разработка JavaScript превратилась из области пограничного знания в абсолютную необходимость, если вы хотите получить работу в качестве разработчика Web.

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

Об авторе

Крис Хайлман работает Web-разработчиком уже десять лет, с тех пор как бросил радио-журналистику. Он работает для Yahoo! в Великобритании в качестве инструктора и ведущего разработчика, и осуществляет надзор за качеством кода внешнего представления для Европы и Азии.

Крис поддерживает блог на сайте Wait till I come (http://wait-till-i.com/) и доступен во многих социальных сетях под ником "codepo8".

Фото с разрешения: Bluesmoon (http://www.flickr.com/photos/bluesmoon/1545636474/)

< Лекция 3 || Лекция 4: 1234 || Лекция 5 >
Александр Мельников
Александр Мельников

В лекции №7 результат работы ни одного скрипта кроме первого не выводит результат на странице браузера, ни одного. Почему. Автор, разъясните пожалуйста. Сокурсники, можете помочь понять?

Юлия Максимова
Юлия Максимова

Я пишу в блокноте, сохраняю с разрешением html и открываю через браузер. Пустой лист

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

Наталья Горбунова
Наталья Горбунова
Россия, Черноголовка
Андрей Маштак
Андрей Маштак
Россия, Тамбов, ТВВАИУ, 1981