Способы сокращения JavaScript кода (продолжение)

Octane, 23.08.2008

Способы сокращения JavaScript кода (продолжение)Как и обещал, представляю Вам продолжение статьи «Способы сокращения JavaScript кода».

Быстрый способ преобразования «DOMNodeList» в «Array»

Итак, чем нас не устраивает «DOMNodeList»? Объект такого типа хоть и представляет из себя нумерованный массив, но не имеет методов, присущих объекту «Array». Например, в полученную коллекцию DOM-элементов мы хотим добавить еще несколько узлов, но удобного метода «push» у «DOMNodeList» нет, потому что это не «Array».

var list = document.body.childNodes;
alert(typeof list.push);

В результате выполнения этого кода, будет выведено сообщение «undefined». Преобразовать «list» в массив можно простым перебором элементов.

var list = document.body.childNodes,
length = list.length, array = [], i;
for(i = 0; i < length; array.push(list[i++]));
alert(typeof array.push);

Теперь выводится сообщение «function», мы успешно преобразовали «DOMNodeList» в «Array». Казалось бы, если нам потребуется такое преобразование выполнять в коде несколько раз, запишем все это в функцию и будем пользоваться, но есть еще более удобный и короткий способ. В JavaScript объект «Array» имеет метод «slice», который используется для выделения части массива. Так как элементы структуры «DOMNodeList» тоже представляют собой нумерованный список, то метод «slice» успешно преобразует «DOMNodeList» в «Array».

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

var list = document.body.childNodes, array = [];
array = array.slice.call(list);
alert(typeof array.push);

Снова «alert(typeof array.push);» выдает сообщение «function». Теперь запишем преобразование в самом кратком виде:

var array = [].slice.call(document.body.childNodes);

В итоге даже не нужно будет создавать отдельной функции, преобразующей «DOMNodeList» в «Array», итак все коротко и красиво.

Конструкция «for(…in…)» работает не только с ассоциативными массивами

Многие программисты, пришедшие в JavaScript из PHP, очень любят использовать везде ассоциативные массивы (хэши) и аналог «foreach», представленный в JavaScript конструкцией «for(…in…)», даже там, где это совершенно не нужно. Конструкция «for(…in…)» работает с любым объектом, в том числе и с «Array».

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

var key, list = {
	x: 'xxx',
	y: 'yyy',
	z: 'zzz'
}
for(key in list) alert(key);

Ключи «x», «y» и «z» вообще нигде не участвуют в коде. Давайте перепишем код с использованием «Array».

var key, list = ['xxx', 'yyy', 'zzz'];
for(key in list) alert(key);

Запись стала немного короче, за счет удаления ключей ассоциативного массива и разделителя «:» (двоеточие). Таким образом, можно перебрать еще и все символы в строке. Главное не забывайте, что если в «prototype» этого объекта находятся пользовательские методы, то переменная «key» будет получать ссылки и на них тоже. Чтобы этого избежать, необходимо ввести проверку на принадлежность метода/атрибута текущему объекту.

var key, list = ['xxx', 'yyy', 'zzz'];
for(key in list) if(list.hasOwnProperty(key)) alert(key);

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

var i, list = ['xxx', 'yyy', 'zzz'], length = list.length;
for(i = 0; i < length; alert(list[i++]));

В таком виде в цикле всегда перебираются только нужные нам элементы. Использование «for(…in…)» не только для ассоциативных массивов в некоторых случаях помогает сократить код, но нужно быть предельно внимательным, используя эту конструкцию. В общем, забудьте этот пример, зря я его, наверное, написал :)

Сокращаем и упорядочиваем названия переменных и функций

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

function newElement(tag) {
  return document.createElement(tag);
}

function newText(str) {
  return document.createTextNode(str);
}

function getElement(id) {
  return document.getElementById(id);
}

function getElements(tag) {
  return document.getElementsByTagName(tag);
}

function addEvent(obj, type, listener) {
  if(obj.addEventListener)
    obj.addEventListener(type, listener, false);
  else if(obj.attachEvent)
    obj.attachEvent('on' + type, listener);
}

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

var dom = {
  new: {
    tag: function(tag) {
      return document.createElement(tag);
    },
    text: function(str) {
      return document.createTextNode(str);
    }
  },
  get: {
    id: function(id) {
      return document.getElementById(id);
    },
    tag: function(tag) {
      return document.getElementsByTagName(tag);
    }
  },
  event: {
    add: function(obj, type, listener) {
      if(obj.addEventListener)
        obj.addEventListener(type, listener, false);
      else if(obj.attachEvent)
        obj.attachEvent('on' + type, listener);
    }
  }
}

Кода стало немного больше, но теперь внутри объекта «dom» мы можем использовать любые короткие и понятные названия.

// было
var list = getElements('div');
// стало
var list = dom.get.tag('div');

// было
var node = newElement('span');
// стало
var node = dom.new.tag('span');

Помоему, стало все логичнее и удобнее для восприятия. Вы тоже так считаете?

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

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

Всегда ли нужны «true» и «false»?

В условных конструкциях языка JavaScript могут использоваться не только значения «true» и «false». В качестве ложного значения могут приниматься: «0», «undefined», «null». За истинное значение может использоваться, например, факт наличия объекта или любое число, отличное от нуля… Поэтому не усложняйте условные конструкции дополнительными логическими выражениями.

if(typeof document.getElementById('test') != 'undefined')
  alert('exist');
else alert('not find');
// или
if(!!document.getElementById('test')) alert('exist');
else alert('not find');

// тоже самое
if(document.getElementById('test')) alert('exist');
else alert('not find');
// или
alert(document.getElementById('test') ? 'exist' : 'not find');

Или вот еще яркий пример, при определении длины массивов и строк:

var array = ['a', 'b', 'c'];

if(array.length != 0) {
  // что-то делаем c array
}
// или
if(array.length > 0) {
  // что-то делаем c array
}

//тоже самое
if(array.length) {
  // что-то делаем c array
}

«copy-paste» для ленивых

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

Спонсор поста: Агентство «Web++» — продвижение и создание сайтов в Волгограде.

Подписаться на обновления блога

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

Категории: JavaScript | Комментировать

Комментарии

  1. Kolyaj / 24.08.2008 в 21:22

    Вы уверены насчет slice?

    slice(startIndex[, endIndex]);
  2. Octane / 24.08.2008 в 21:40

    Да, действительно, увлекся сокращением, пропустил «call» или «apply». Только теперь заметил, что не работает в IE, попробую найти решение… Странно вообще, с объектом «arguments», который тоже не является массивом, код работает в IE, а вот с «DOMNodeList» — нет.

  3. Sergey / 29.08.2008 в 13:50

    Делаю обычно так

    var array = Array.prototype.slice.call(document.body.childNodes);

    на счет for( .. in ..) вы уверены что оба варианта вернут одно и тоже?
    В конструкции for(var key in ['xxx','yyy','zzz']) key будет 0,1,2
    а в for(var key in [x:'xxx',y:'yyy',z:'zzz']) ,будет x,y,z
    И это я только начал смотреть.

  4. Octane / 29.08.2008 в 14:52

    Записи

    var array = Array.prototype.slice.call(document.body.childNodes);

    и

    var array = [].slice.call(document.body.childNodes);

    практически одно и тоже, но в первом случае не создается пустой массив — это уже можно отнести к оптимизации… Но оба этих варианта не работают в «Internet Explorer».

    Про массивы, да я знаю, что при использовании конструкции «for(key in array)» значениями «key» являются ключи массива, поставьте «array[key]», чтобы использовать значения массива.

    Кстати, Ваш пример «for(var key in [x:'xxx',y:'yyy',z:'zzz'])» не корректный, таким образом массивы записывать нельзя. Правильно будет «{x: 'xxx', y: 'yyy', z: 'zzz'}», но это уже ассоциативный массив…

  5. Гвидон Маляров / 03.09.2008 в 12:11

    не уверен, что стоит полностью отказываться от комментариев. хотя, конечно у себя свожу их к минимуму — ограничиваюсь кратким описанием функции/объекта (не более 3-4 слов) капсом перед определением

Оставить комментарий

480×60
480×60