Javascript.RU

Оптимизация Javascript-кода

Оптимизировать код javascript, конечно, надо не везде. Обычно - в ускорении нуждаются

  • интерфейсные компоненты
    • анимация
    • драг'н'дроп
  • обработчики частых событий
    • onmousemove
    • CSS expression (IE)

Основные узкие места - как правило, там, где javascript вызывается очень часто. Мы рассмотрим основные причины тормозов и то, как их преодолеть.

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

Любое обращение к DOM - обычно тяжелее для браузера, чем обращение к переменной javascript. Изменение свойств, влияющих на отображение элемента: className, style, innerHTML и ряда других - особенно сложные операции.

Уменьшение их числа может ускорить скрипт.

Например, эта функция строит элементарный интерфейс:

function buildUI(parent)  {
	parent.innerHTML = ''
	parent.innerHTML += buildTitle()
	parent.innerHTML += buildBody()
	parent.innerHTML += buildFooter()
}

Оптимизация по части доступа к innerHTML будет такая:

function buildUI2(parent) {
	var elementText = ''
	elementText += buildTitle()
	elementText += buildBody()
	elementText += buildFooter()
	parent.innerHTML = elementText
}

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

время buildUI
время buildUI2

Рассмотрим функцию, которая проходит всех детей узла и ставит им свойства:

function testAttachClick(parent) {

	var elements = parent.getElementsByTagName('div')

	for(var i=0; i<elements.length; i++) {
		elements[i].onclick = function() { 
			alert('click on '+this.number) 
		}
		elements[i].number = i
	}
}

Сколько в ней обращений к DOM ?

Правильный ответ - 4 обращения.

Первое - самое очевидное:

var elements = parent.getElementsByTagName('div')

Функция getElementsByTagName() возвращает специальный объект NodeList, который похож на массив: есть длина и нумерованы элементы, но на самом деле это динамический объект DOM.

Например, если один из элементов NodeList будет вдруг удален из документа, то он пропадет и из elements.

Поэтому следующие обращения - тоже работают с DOM, причем на каждой итерации цикла:

elements.length
elements[i].onclick
elements[i].number

По возможности оптимизируем их:

function testAttachClick2(parent) {

	var elements = parent.getElementsByTagName('div')
	var len = elements.length
	var elem
	
	for(var i=0; i<len; i++) {
		elem = elements[i]
		elem.onclick = function() { 
			alert('click on '+this.number) 
		}
		elem.number = i
	}
}

Такая оптимизация полезна и в случае, когда elements - обычный массив, но эффект от уменьшения обращений к DOM NodeList гораздо сильнее.

Рассмотрим заодно еще небольшую оптимизацию. Функция, которая назначается onclick внутри цикла - статическая. Вынесем ее вовне цикла:

function testAttachClick3(parent) {

	var elements = parent.getElementsByTagName('div')
	var len = elements.length
	var elem

	var handler = function() { 
		alert('click on '+this.number) 
	}


	for(var i=0; i<len; i++) {
		elem = elements[i]
		elem.onclick = handler
		elem.number = i
	}
}

В этом тесте цикл пробегает по 30 элементам. Чем больше элементов - тем больше видна разница. Проверьте в разных браузерах.

Время testAttachClick
Время testAttachClick2
Время testAttachClick3

В Internet Explorer конкатенация строк реализована не совсем корректно. Особенно это видно на длинных строках.

Тормоза возникают, например, когда длинная строка конструируется из множества мелких.

Например, из массива данных делается HTML-таблица:

function makeTable() {
	var s = '<table><tr>'
	for(var i=0; i<arrayData.length; i++) {
		s += '<td>' + arrayData[i] + '</td>'
	}
	s+='</tr></table>'
	return s
}

По-видимому, каждый раз при сложении строк:

  1. создается новая строка
  2. туда копируется первая строка
  3. далее копируется вторая строка

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

В некоторых языках предусмотрены специальные классы для сложения многих строк в одну. Например, в Java это StringBuilder.

Соответствующий прием в javascript - сложить все куски в массив, а потом - получить длинную строку вызовом Array#join.

Так будет выглядить оптимизированный пример с таблицей:

function makeTable2() {
	var buffer = []
	for(var i=0; i<arrayData.length; i++) {
		buffer.push(arrayData[i])
	}
        var s = '<table><tr><td>' + buffer.join('</td><td>') + '</td></tr></table>'
	return s
}

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

Тормоза на строках отчетливо видны в Internet Explorer.

Время makeTable
Время makeTable2

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

Тем не менее, этот способ оптимизации можно применять везде, т.к он уменьшает максимальное время выполнения (IE).

И, конечно, конструирование через строки работает быстрее создания таблицы через DOM, когда каждый элемент делается document.createElement(..).

Только вот таблицу надо делать целиком, т.к в Internet Explorer свойство innerHTML работает только на самом нижнем уровне таблицы: TD, TH и т.п, и не работает для TABLE, TBODY, TR...

В IE есть такая интересная штука как CSS-expressions.

Как правило они используются для обхода IEшных недостатков и багов в верстке. Например:

p {
	max-width:800px;
	width:expression(document.body.clientWidth > 800? "800px": "auto" );
}

Идея хорошая, спору нет, это работает. Но есть здесь и подводный камень.

CSS expressions вычисляются при каждом событии, включая движение мыши, скроллинг окна и т.п.

Например, посмотрите эту страничку в Internet Explorer - полоса чуть ниже должна быть частично закрашена. Каждые 10 вычислений CSS expression меняют ее ширину на 1.

Клик на полоске покажет, сколько всего раз вычислилось CSS expression.

Если CSS-expression достаточно сложное, например, включает вложенные Javascript-вызовы для определения размеров элементов, то передвижение курсора может стать "рваным", как и при сложном обработчике onmousemove.


Автор: Snipe, дата: 15 мая, 2008 - 19:20
#permalink

Хорошая статья.


Автор: Сергей (не зарегистрирован), дата: 29 мая, 2008 - 10:31
#permalink

Позновательная. Надо будет применить...


Автор: Cutalion (не зарегистрирован), дата: 19 июня, 2008 - 20:14
#permalink

Да, полезно. Даже и не думал, что "CSS expressions вычисляются при каждом событии"...


Автор: Cutalion (не зарегистрирован), дата: 19 июня, 2008 - 20:20
#permalink

А в Safari 3.0.2 (522.13.1) первый тест (buildUI) вообще около 1200 мс выполняется... не ожиданно так... в ишаке и то в 3 раза быстрее...


Автор: zm8, дата: 18 марта, 2009 - 10:06
#permalink

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

for (var i = arr.length; i--;)

, избегание while, Array#push, и так далее)


Автор: Zeroglif, дата: 18 марта, 2009 - 12:48
#permalink

И while тоже плохой?


Автор: zm8, дата: 18 марта, 2009 - 12:55
#permalink

опечатался, извиняюсь (мало спал) =)
имел ввиду with
----------------------------------------
window.open(window.location);


Автор: Ganj (не зарегистрирован), дата: 30 июля, 2009 - 22:08
#permalink

спасибо Вам за очень полезный и интересный сайт


Автор: studio (не зарегистрирован), дата: 21 сентября, 2009 - 17:58
#permalink

Да - разница в скорости разных методов впечатляет


Автор: StagnantIce, дата: 14 мая, 2010 - 15:30
#permalink
function buildUI2(parent) {
	    var elementText = ''
	    elementText += buildTitle()
	    elementText += buildBody()
            elementText += buildFooter()
	    parent.innerHTML = elementText
	}

Это не всегда удается сделать, так как может придется менять не только innerHTML. Может быстрее будет удаление из документа узла средством removeChild() затем создание нового или изменение старого объекта, и в конце appendChild() ?


Автор: goldserg, дата: 8 июня, 2010 - 16:42
#permalink

А теперь внимание!!!
Обнаружил падение скорости у себя в проекте. в итоге обнаружил
что Array.join - с большими строками (под 2-10кб) работает отвратительно под всеми браузерами.
а стандартная конктатенация через '+' - работает.... на 4 порядка быстрее!!! под IE8, и 3 порядка под FF3.6

Можете протестировать.

console.log(new Date().getTime());
q='';
for (var i=0; i < 2500; i++) {
q += 'dsfkbjdkvbnfdkwlfbgnmdekwlq;edfgnmdekwlq;dfgnemwlq;sdfbjnvmdls;afkbnmvd,lsfkbnjfdmsla';
}
console.log(new Date().getTime());

console.log(new Date().getTime());
q='';
for (var i=0; i < 2500; i++) {
q = [q, 'dsfkbjdkvbnfdkwlfbgnmdekwlq;edfgnmdekwlq;dfgnemwlq;sdfbjnvmdls;afkbnmvd,lsfkbnjfdmsla'].join('');
}
console.log(new Date().getTime());


Автор: PieceOfMeat (не зарегистрирован), дата: 13 июля, 2010 - 21:53
#permalink

Вы просто очень неэффективно записали второй цикл, нужно так:
q=[];
for (var i=0; i < 2500; i++) {
q.push('dsfkbjdkvbnfdkwlfbgnmdekwlq;edfgnmdekwlq;dfgnemwlq;sdfbjnvmdls;afkbnmvd,lsfkbnjfdmsla');
}
q.join('');

В этом варианте на моей машине при 25000 итерациях второй вариант проигрывает первому 2-3 миллисекунды в FF и Opera. В IE второй вариант вдвое эффективнее.

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


Автор: goldserg, дата: 4 октября, 2010 - 09:07
#permalink

Да возможно, но чем тогда объясняется, данный проигрыш второго варианта?
И, ИМХО, конструкция с push не самая эффективная, быстрее должно быть так.
q[q.length] = 'fdfdfd';


Автор: Чистяков Денис (не зарегистрирован), дата: 30 октября, 2010 - 19:02
#permalink

Хотя бы тем, что вы в качестве индекса используете обращение к изменяемому свойству, это из разряда оптимизации описанной в «Более сложном примере», судя по всему.


Автор: IvanS (не зарегистрирован), дата: 24 июля, 2010 - 11:48
#permalink

Да, очень хорошая и познавательная статья, есть чему подучиться.


Автор: Гость (не зарегистрирован), дата: 13 сентября, 2010 - 19:42
#permalink

интересно что в ходе тестов сугубо на моей машине было выяснено что ишак в 10 раз медленнее чем хром.


Автор: Гость (не зарегистрирован), дата: 11 октября, 2010 - 01:54
#permalink

Мда. Кто-то тут говорил о неприменимости MVC в яваскрипте, хотя эта статья убеждает в обратном.


Автор: Arconas, дата: 14 октября, 2010 - 11:21
#permalink

С вашего позволения задам парочку вопросов.

[code]var s = '' + buffer.join('') + ''[/code]
Метод довольно быстрый, спору нет, что подтверждает вот этот Benchmark.

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

У меня получается пока три шага:
На первом я формирую заголовки столбцов по принципу метода join " ... "

[code]var titleTable = "" + row_buffer.join('') + '\n';[/code]
А вот на втором надо сформировать основное тело таблицы в зависимости от кол-ва столбцов. И вот тут возникает вопрос. Каков самый оптимизированный метод из существующих?


Автор: MODist, дата: 23 декабря, 2010 - 14:59
#permalink

"Рассмотрим заодно еще небольшую оптимизацию. Функция, которая назначается onclick внутри цикла - статическая. Вынесем ее вовне цикла:"
Кто-нибудь может объяснить, почему вынос функции так сильно влияет на скорость выполнения? И что происходит внутри, когда мы выносим функцию таким образом?


Автор: Devunion (не зарегистрирован), дата: 24 декабря, 2010 - 14:38
#permalink

Некоторые оптимизации при проверке на Google Chrome приводят к совершенно обратным результатам. Для ИЕ и ФФ -- да, все работает. Так что аккуратно использовать надо.


Автор: Алексей Павлов (не зарегистрирован), дата: 20 ноября, 2011 - 16:52
#permalink

Подскажите, будет ли разница в следующих скриптах?

for (i=0; i<1000000; i++)
  x += doSomething(i);

и

for (i=0; i<1000000; i++) x += doSomething(i);

Будет ли здесь выигрыш на отсутствии парсинга второй строки на каждой итерации цикла?


Автор: Раед, дата: 6 апреля, 2012 - 22:14
#permalink

нет. на то как вы запишите код (с переносом или без) движку по большому счёту плевать


Автор: B@rmaley.e><e, дата: 8 апреля, 2012 - 15:29
#permalink

Нет, конечно. Парсинг производится один раз: по коду строится дерево разбора, а из него уже другие внутренние структуры движка.


Автор: Regex (не зарегистрирован), дата: 11 ноября, 2013 - 01:31
#permalink

Я так и не понял почему метод make Table 2 отработал в 8 раз медленней чем make Table ??


Автор: Regex (не зарегистрирован), дата: 11 ноября, 2013 - 01:35
#permalink

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


Автор: Гость (не зарегистрирован), дата: 4 октября, 2018 - 12:57
#permalink

Можно использовать специальные приемы и разрабатывать на Javascript еще быстрее.


Автор: patsm00re18 (не зарегистрирован), дата: 7 октября, 2019 - 15:10
#permalink

I have learn a few just right stuff here. love balls pc


Автор: Гость (не зарегистрирован), дата: 3 марта, 2020 - 05:02
#permalink

I got everything I wanted. Not what you'd think, You wouldn't wonder why you're here. But it felt like… happy wheels run 3


Автор: farhan (не зарегистрирован), дата: 23 марта, 2020 - 17:35
#permalink

I wanted to thank you for this excellent read!! I definitely loved every little bit of it. I have you bookmarked your site to check out the new stuff you post.
maid agency singapore


Автор: Sophie Miller (не зарегистрирован), дата: 1 апреля, 2020 - 14:16
#permalink

192.168.l.254Merci pour l'information utile! Je l'ai aidé vos conseils!


Автор: Sophie Miller (не зарегистрирован), дата: 13 мая, 2020 - 00:20
#permalink

Dziękuję za informacje! Szukałem i nie mogłem znaleźć. Pomogłeś mi!
192.168.1.1


Автор: Гость (не зарегистрирован), дата: 6 июля, 2020 - 01:57
#permalink

I have learn a few just right stuff here. Krunker io


Автор: Гость (не зарегистрирован), дата: 25 сентября, 2020 - 19:41
#permalink

I am definitely enjoying your website. You definitely have some great insight and great stories.
Mudanças cruzeiro - DF


Автор: osama shk (не зарегистрирован), дата: 3 октября, 2020 - 15:53
#permalink

Very nice article, I enjoyed reading your post, very nice share, I want to twit this to my followers. Thanks!.
Mudanças asa norte


Автор: osama shk (не зарегистрирован), дата: 21 октября, 2020 - 13:07
#permalink

This article gives the light in which we can observe the reality. This is very nice one and gives indepth information. Thanks for this nice article.
ที่เที่ยวพังงา


Автор: osama shk (не зарегистрирован), дата: 27 октября, 2020 - 16:51
#permalink

I know your expertise on this. I must say we should have an online discussion on this. Writing only comments will close the discussion straight away! And will restrict the benefits from this information.
Lil peep Merch


Автор: osama shk (не зарегистрирован), дата: 28 декабря, 2020 - 13:01
#permalink

Its a great pleasure reading your post.Its full of information I am looking for and I love to post a comment that "The content of your post is awesome" Great work.
go to my blog


Автор: osama shk (не зарегистрирован), дата: 6 января, 2021 - 17:37
#permalink

Very nice article, I enjoyed reading your post, very nice share, I want to twit this to my followers. Thanks!.
lamps


Автор: osama shk (не зарегистрирован), дата: 7 января, 2021 - 14:08
#permalink

You know your projects stand out of the herd. There is something special about them. It seems to me all of them are really brilliant!
coronavirus holbox


Автор: osama shk (не зарегистрирован), дата: 13 января, 2021 - 18:06
#permalink

Great write-up, I am a big believer in commenting on blogs to inform the blog writers know that they’ve added something worthwhile to the world wide web!..
las vegas carpet cleaning


Автор: osama shk (не зарегистрирован), дата: 28 января, 2021 - 14:57
#permalink

Really a great addition. I have read this marvelous post. Thanks for sharing information about it. I really like that. Thanks so lot for your convene.
cpanel license


Автор: osama shk (не зарегистрирован), дата: 1 февраля, 2021 - 18:44
#permalink

Interesting topic for a blog. I have been searching the Internet for fun and came upon your website. Fabulous post. Thanks a ton for sharing your knowledge! It is great to see that some people still put in an effort into managing their websites. I'll be sure to check back again real soon.
isla mujeres tour


Автор: osama shk (не зарегистрирован), дата: 3 февраля, 2021 - 14:20
#permalink

Great Information sharing .. I am very happy to read this article .. thanks for giving us go through info.Fantastic nice. I appreciate this post.
schlüsseldienste köln


Автор: osama shk (не зарегистрирован), дата: 3 февраля, 2021 - 18:09
#permalink

Thank you for helping people get the information they need. Great stuff as usual. Keep up the great work!!!
ساختمان هوشمند


Автор: osama shk (не зарегистрирован), дата: 13 февраля, 2021 - 19:36
#permalink

Great job for publishing such a beneficial web site. Your web log isn’t only useful but it is additionally really creative too. There tend to be not many people who can certainly write not so simple posts that artistically. Continue the nice writing
tulum


Автор: Гостьsad (не зарегистрирован), дата: 22 февраля, 2021 - 13:50
#permalink

I just found this blog and have high hopes for it to continue. Keep up the great work, its hard to find good ones. I have added to my favorites. Thank You.
cenote in tulum mexico


Автор: osama shk (не зарегистрирован), дата: 27 февраля, 2021 - 13:51
#permalink

Great write-up, I am a big believer in commenting on blogs to inform the blog writers know that they’ve added something worthwhile to the world wide web!..
rumah pintar


Автор: farhan (не зарегистрирован), дата: 23 марта, 2021 - 07:51
#permalink

An fascinating discussion is value comment. I think that it is best to write extra on this matter, it won’t be a taboo topic however generally people are not enough to talk on such topics. To the next. Cheers
treatnheal


Автор: farhan (не зарегистрирован), дата: 27 марта, 2021 - 09:18
#permalink

Nice post. I was checking constantly this blog and I’m impressed! Extremely useful info specially the last part I care for such information a lot. I was seeking this certain info for a long time. Thank you and good luck.
Braces Carolina


Автор: fave fave (не зарегистрирован), дата: 28 марта, 2021 - 19:32
#permalink

I recently came across your blog and have been reading along. I thought I would leave my first comment. I don't know what to say except that I have enjoyed reading. Nice blog. I will keep visiting this blog very often.
Inteligentny dom


Автор: osama shk (не зарегистрирован), дата: 30 марта, 2021 - 14:42
#permalink

Great write-up, I am a big believer in commenting on blogs to inform the blog writers know that they’ve added something worthwhile to the world wide web!..
Automatyka domowa


Автор: osama shk (не зарегистрирован), дата: 5 апреля, 2021 - 10:19
#permalink

Great write-up, I am a big believer in commenting on blogs to inform the blog writers know that they’ve added something worthwhile to the world wide web!..
bathroom vanity


Автор: jogazy jogazy (не зарегистрирован), дата: 11 апреля, 2021 - 14:32
#permalink

If you set out to make me think today; mission accomplished! I really like your writing style and how you express your ideas. Thank you.
best resorts in tulum for couples


Автор: osama shk (не зарегистрирован), дата: 13 апреля, 2021 - 13:59
#permalink

Your blog provided us with valuable information to work with. Each & every tips of your post are awesome. Thanks a lot for sharing. Keep blogging..
covid mexico


Автор: fave fave (не зарегистрирован), дата: 15 апреля, 2021 - 11:23
#permalink

Thanks for the nice blog. It was very useful for me. I'm happy I found this blog. Thank you for sharing with us,I too always learn something new from your post.
Hualien food guide


Автор: osama shk (не зарегистрирован), дата: 16 апреля, 2021 - 14:04
#permalink

I am very much pleased with the contents you have mentioned. I wanted to thank you for this great article.
How to get to Hualien from Taipei


Автор: fave fave (не зарегистрирован), дата: 22 апреля, 2021 - 11:29
#permalink

I wanted to thank you for this great read!! I definitely enjoying every little bit of it I have you bookmarked to check out new stuff you post.
mediterranean food


Автор: talleychan (не зарегистрирован), дата: 22 апреля, 2021 - 14:49
#permalink

Share to members on the 2 player games forum with the regularly updated game list.
Experience right away with the cookie clicker game, the game feels relaxing when you create thousands of cakes on a beautiful interface.


Автор: shemales birmingham (не зарегистрирован), дата: 22 апреля, 2021 - 20:36
#permalink

For casual conversation with fine shemales in UK you must to visit shemales birmingham


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

Приветствуются комментарии:
  • Полезные.
  • Дополняющие прочитанное.
  • Вопросы по прочитанному. Именно по прочитанному, чтобы ответ на него помог другим разобраться в предмете статьи. Другие вопросы могут быть удалены.
    Для остальных вопросов и обсуждений есть форум.
P.S. Лучшее "спасибо" - не комментарий, как все здорово, а рекомендация или ссылка на статью.
Содержание этого поля является приватным и не предназначено к показу.
  • Адреса страниц и электронной почты автоматически преобразуются в ссылки.
  • Разрешены HTML-таги: <strike> <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <u> <i> <b> <pre> <img> <abbr> <blockquote> <h1> <h2> <h3> <h4> <h5> <p> <div> <span> <sub> <sup>
  • Строки и параграфы переносятся автоматически.
  • Текстовые смайлы будут заменены на графические.

Подробнее о форматировании

CAPTCHA
Антиспам
2 + 0 =
Введите результат. Например, для 1+3, введите 4.
 
Текущий раздел
Поиск по сайту
Содержание

Учебник javascript

Основные элементы языка

Сундучок с инструментами

Интерфейсы

Все об AJAX

Оптимизация

Разное

Дерево всех статей

Последние комментарии
Последние темы на форуме
Forum