Предисловие
Давно хотел написать. Мысли есть, желание есть, времени нету… Но вот нашлось, так что привет, Хабра.
Здесь я собрал все идеи, которые помогали и помогают в разработке веб-приложений. Для удобства я разбил их на группы:Память
Оптимизация операций
Выделение критических участков
Циклы и объектные свойства
Немножко о DOM
DocumentFragment как промежуточный буфер
О преобразованиях в объекты
Разбитие кода
События перетаскивания
Другие советы
Память
Хоть это и не должно волновать клиентского программиста, но не забываем, что память всё-таки не бесконечна и когда-нибудь может закончиться, например, когда запущено несколько массивных программ: офис, графический редактор, компиляция большой программы и др. Несмотря на то, что приведенный пример тривиален, у меня действительно такое случилось, хоть и не из-за браузера, но он тоже сыграл свою роль: 1,3 Гб оперативы (отладчик, около 30 вкладок), начались тормоза по перегрузке страниц ОП в файл подкачки.
Чтобы уменьшить расход памяти, я предлагаю несколько способов:
1) уменьшение числа локальных переменных.
Вы спросите, что это значит? Объясняю, на своей практике видел, как «code monkey»-студенты писали подобный код:
(function init(){ //здесь и далее я буду использовать функции-обёртки
for(var i=0,n=1;i<10;i++) //суть не в действии внутри цикла
n+=n;
alert(n); //выводим результат
for(var i=0,m=1;i<10;i++)
m*=m;
alert(m);
})();
Может быть, вы сразу и не видите, в чем фишка, но: зачем создавать новые переменные, если у нас есть использованные и уже хранящие в себе совершенно ненужные значения старые переменные? В данном примере для нормального решения необходимо заменить все m на n, что сэкономит память.
Этот метод лучше всего проявляет себя в рекурсивных функциях, потому что каждый вызов такой функции провоцирует создание и, обратите внимание, удаление локальных переменных после завершения работы функции, что тоже требует процессорного времени и памяти.
Для наглядного восприятия можно привести аналогию со шкафчиками: у вас есть 6 шкафчиков, три из которых могут быть заполнены; зачем тогда вам ещё три шкафчика, если в таком случае вам придётся открывать все 6, а потом и закрывать все 6?
2) уменьшение числа замыканий.
Замыкания вызывают ощутимый расход памяти (3 Мб на 1000 объектов для хрома, возможно, в новых версиях другой объем), поэтому используйте их как можно реже. Я использую их в двух случаях:Необходимо скрыть данные внутри некоторого интерфейса, не дать доступа извне;
При рекурсии, когда нужно сделать какие-то пометки в одном общем объекте (например, при обходе HTML занести в массив все узлы, которые имеют пользовательское свойство «dragndrop») в том случае, если выборка по селекторам не подходит.
Оба случая подразумевают какие-то частные, уникальные, случаи. Имеется в виду, что создаются единичные интерфейсы.
Пример первого случая:
(function init(){
var INTERNAL_NUMBER=0;//замкнутая переменная
return {
get:function get(){return INTERNAL_NUMBER;},//функция, возвращающая значенеи замкнутой переменной
set:function set(value){ //функция, фильтрующая передаваемые значения и устанавливающая замкнутую переменную
if(typeof value==”number”)
INTERNAL_NUMBER=value;
return INTERNAL_NUMBER;
}
}
})();
Вот таким образом я и создаю ЕДИНИЧНЫЕ интерфейсы.
Если с первым уже, я думаю, всё понятно, то второй случай следует пояснить: фактически он относится к предыдущему пункту, потому что мы заменяем переменную, которую можно было передать в качестве аргумента в функцию, замыканием, тем самым уменьшая количество локальных переменных внутри этой функции и в то же время соблюдаем принцип минимума замыканий: это замыкание характерно только для это рекурсивной функции (хотя, это уже как вы захотите), ведь при рекурсии используется одна и та же функция (новые замыкания не создаются).
Пример:
(function init(){
var found=[];
(function traverse(html){
for(var i=html.firstChild;i;i=i.nextSibling)
arguments.callee(i);
if(typeof html.dragndrop==”object”)
found.push(html);
})(document.body);
return found;
})();
Как видно из примера, в рекурсивной функции содержатся 2 локальные переменные (html, i) вместо трёх (html, i, found). На практике выигрыш в скорости несущественен (по крайней мере, от замыкания всего лишь одной переменной), зато даёт знать о себе выигрыш в памяти.
И, пожалуйста, не упрекайте за nextSibling, а не за nextElementSibling, все делалось в первую очередь ради разъяснения сути замыкания внутри рекурсивной функции.
ВНИМАНИЕ: Никогда не делайте замыкания посредством цикла — это вызывает чрезмерный расход памяти. Исключения составляют случаи, когда логика скрипта требует безоговорочного сокрытия данных (но в любом случае, если у меня есть отладчик, я и туда доберусь? ). Пример неправильного использования замыканий:
function addEvents2(divs) {
for(var i=0; i