6 декабря 2010 в 21:20

JavaScript Performance Best Practices


Наткнулся на интересный документ в Твиттере.

JavaScript Performance Best Practices


В заголовке указана категория WRT (Nokia Web Runtime or Widget for S60), то есть конкретная Нокиевская платформа, но, думаю, многим интересно будет почитать, возможно найдёте для себя что-то новое. Есть действительно полезные советы, но есть и вредные, особенно в свете современной разработки _под все браузеры_.
Сначала думал оформить как топик-ссылку, но под катом я обращу внимание на некоторые проблемы этой статьи. Статью прочитать стоит но ни в коем случае не относитесь к ней, как к истине в последней инстанции.



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

Предлагайте в комментариях свои замечания — может, в итоге это выльется в отличную статью про оптимизацию
Я бы сам отредактировал эту статью в Вики, но «You do not have sufficient privileges to view this page or perform this action.»

Начнём с того, что в статье раскрыто множество действительно полезных советов.
Например, использование eval лучше избегать, а при получении и изменении размеров элемента могут появится излишние reflow (Minimize the use of operations determining the dimensions or location of elements).
Avoid modifications while traversing

А контейнер, который возвращает getElementsByTagName действительно «живой» и следующий код повесит ваш браузер:
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>Test</title>
	</head>
	<body>
		<div id="container"><em></em><em></em></div>
	</body>
	<script>/* Script here */</script>
</html>

var doc  = document,
    ems  = doc.getElementsByTagName('em'),
    cont = doc.getElementById('container');
for (var i = 0; i < ems.length; i++) {
	cont.appendChild(doc.createElement('em'));
};


Тем не менее, там есть ряд недостатков.

Во-первых, ошибки. Примеры:
Avoid using eval or the Function constructor

addMethod(myObj, 'methodName', function () { 'this.localVar=foo'; });
// =>
addMethod(myObj, 'methodName', function () { this.localVar=foo; });


Load scripts without blocking for faster startup and to show a splash screen

script.onreadstatechange => script.onreadystatechange

И ошибки не синтасического плана, а логического:

var elem = document.getElementById('elem').propertyOne = 'value of first property';
elem.propertyTwo = 'value of second property';
elem.propertyThree = 'value of third property'
// Не эквивалентно, т.к. [elem] будет равен строке 'value of first property', а не dom-элементу
document.getElementById('elem').propertyOne = 'value of first property';
document.getElementById('elem').propertyTwo = 'value of second property';
document.getElementById('elem').propertyThree = 'value of third property';


Во-вторых, хотя некоторые советы действительно ускорят работу вашего приложения — они не имеют смысл, т.к. имеют совершенно другую логику программы.
Попросту говоря, сделайте «Б» вместо «А» потому что «Б» быстрее.

Пример —
Don't use try-catch-finally inside performance-critical functions

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

Avoid for-in in performance-critical functions

Опять же — разный результат. var i in arr для массивов применимо в крайне редких ситуациях. Самая правильная итерация по массиву (используется в фреймворках):
for (var i = 0, len = arr.length; i < len; i++) if (i in arr) {
   // action
}

Это правильно пройдёт массив, который не полностью заполнен и соответствует поведению метода forEach из последних спецификаций:

var arr = ['zero','one','two'];
arr[10] = 'ten';

arr.forEach(function (elem) {
	console.log(elem); // Выведет "zero, one, two, ten" без семи undefined между "two" и "ten"
});


Если вы знаете содержимое массива и неважно, в каком порядке его обоходить — можно использовать обратный цикл
function sum (arr) {
	var result = 0;
	for (var i = arr.length; i--;) {
		result += arr[i];
	}
	return result;
};

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

Try to keep script comments to a minimum/ Avoid long variable names

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

Store local references to out-of-scope variables

Кеширование document имеет смысл, но пример плохой, который в данном случае не имеет смысла, т.к. чтение из кеша происходит только один раз.

Consider using a custom data exchange format for large datasets, as an alternative to XML and JSON

Вредный совет. Постарайтесь максимально избежать сообственных форматов данных и в статье показан пример такого ужаса:
that.contacts = o.responseText.split("\\c");
 
for (var n = 0, len = that.contacts.length, contactSplit; n < len; n++) {
	contactSplit = that.contacts[n].split("\\a");
	
	that.contacts[n] = {};
	that.contacts[n].n = contactSplit[0];
	// ...
	that.contacts[n].y = contactSplit[8];
}

Во-первых, это записывать намного дольше, чем var contacts = JSON.parse(o.responseText);
Во-вторых, парсинг JSON во многих браузерах встроенный и есть вероятность, что ваш «быстрый» формат будет работать намного медленнее, чем JSON. Но может и быстрее, смотрите комментарии.
В-третьих, есть куча недостатков во время поддержки: неочевидное содержимое o.responseText, приходится писать парсер и компилятор формата на сервере и клиенте, сторонние разработчики будут вШоке и т.д.
Основная идея в том, что такая оптимизация крайне редко имеет смысл. В большистве случаев стоит её избежать в силу множества недостатков

В общем, читайте и думайте. Интересные и полезные советы там есть, но всё анализируйте и не используйте бездумно. Как и всегда)
Павло @TheShock
карма
221,7
рейтинг 0,6
Senior JS Developer
Похожие публикации
Самое читаемое Разработка

Комментарии (32)

  • 0
    Почему же всё у всех по-своему… Ни к чему тут такая «индивидуальность». Придерживались бы стандартов, было бы отлично. Комментарий наверное и к посту про сохранение страниц в браузерах тоже относится.
    • +1
      не то окно?)
      • –1
        То… Сначала туда хотел написать, потом прочитал этот пост, понял, что сюда тоже как раз. Вот так хитро и написал, чтобы не засорять базу данных хабрахабра, что может привести к излишним затратам электроэнергии.
        P.S. .WWF
        • +2
          Простите, а к чему в этом топике «индивидуальность»? )
          • 0
            И всё же я чего-то напутал) Это скорей к тому посту
  • +1
    С последним зря вы так категоричны. Очень рекомендую книгу high performance web sites.
    • +1
      <s>web sites</s> javascript
      oreilly.com/catalog/9780596802806
      • +1
        Спасибо, а она отличается содержимым от High Performance Web Sites?
        • 0
          да, в ней описано больше техник оптимизации. по сути в hpws в разделах, посвященных javascript есть только отрывки из первой. и, главное, донесено что именно нужно оптимизировать, а что нет.
    • +1
      Я так понимаю, вы про совет «Постарайтесь максимально избежать сообственных форматов данных и в статье показан пример такого ужаса:»?
      На самом деле я не уверен, что встроенный парсер JSON, который начал появлятся в середине 2009-го года в браузерах, но уже крайне актуальный будет медленнее чьего-то «велосипедного» формата. Но спорить не буду.

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

      И подозреваю, что, таки, совет не актуальный в Фоксе3.5+, Хроме, последних Операх и ИЕ8+.
      • 0
        [].split тоже нативный. а насчет поддержки это да, но собствтвенно, оптимизация производительности это рефакторинг наоборот, поэтому поддержка усложняется сознательно.
        • +1
          Регекспы тоже нативные. Вопрос, что быстрее — 50 вызовов split в цикле, или один вызов json-encode
          • 0
            я не говорю, что свое быстрее всегда. есть ситуации, где «свое» быстрее и дешевле по трафику, поэтому однозначно причислять совет к вредным не стоит, хотя в 99.9% случаев действительно лучше пользоваться стандартным форматом.
            • +1
              тут есть еще вопрос, почему они не сделали так:

              that.contacts[n] = {
              	n : contactSplit[0],
              	// ...
              	y : contactSplit[8]
              }
              


              • +1
                что тоже есть одним из советов.

                или даже так (associate из Mootools):
                that.contacts[n] = contactSplit[0].associate('n', /* ... */, 'y');
                

                Что хоть более читаемо, хоть и незначительно медленнее.

                На самом деле я считаю, что советы «напишите свою хрень в 50 строк, которая возможно будет чуточку быстрее» есть однозначно вредными. Тем более, так авторитетно заявленные. Подобные вещи надо делать (а тем более советовать) предельно осторожно и когда точно знаешь, что такая «оптимизация» будет во благо, а не в зло
                • +1
                  тем более, что такой способ не позволит передавать многие вещи аджаксом, например, если пользователь напишет:
                  Я устоновил игрушку в e:\cosmos\arcade\game, а она не запускается
                  и мы постараемся подобным запросом джсоном получить это сообщение — всё поломается. изредка, мы точно знаем формат данных, но часто такие оптимизации могут вылиться еще и кучей багов.
                  • +1
                    *не джосном а аджаксом
                  • 0
                    будьте внимательнее :)
                    я еще раз повторю: я не говорю, что это правило, которое всегда верно. но есть ситуации, где можно передать данные своим форматом и это сэкономит 200 байт и 0,01 секунды на запрос, а в рамках какого-нибудь сервиса это 1 миллион долларов в год, что превышает издержки на поддержку такого «оптимизированного» участка кода.
                    • +1
                      абсолютно согласен с вами)

                      но подобные статьи рассчитаны на совершенно другой уровень разработчиков, а новички будут ошибочно считать, что JSON — должен умереть, потому что он медленный
        • +1
          я не утверждаю, что сплит быстрее или медленнее, я предполагаю, что это может быть мало того, что трудноподдерживаемым, так еще и не иметь смысле в силу последних изменений в браузерах
  • +2
    В заголовке указана категория WRT (Nokia Web Runtime or Widget for S60)
    Т.е. речь про конкретную нокиевскую платформу, а не общие рекомендации для всех случаев.
    Там может и JSON тормозит и нету jit и другие прелести мобильной платформы.
    • +1
      а главного то я и не заметил. спасибо, сейчас исправлю топик
  • +1
    В заголовке указана категория WRT (Nokia Web Runtime or Widget for S60)
    Т.е. речь про конкретную нокиевскую платформу, а не общие рекомендации для всех случаев.
    Там может и JSON тормозит и нету jit и другие прелести мобильной платформы.
    • 0
      Вы случайно отпостили два раза с разницей в 4 минуты?
      • +1
        хабра глючит
      • +1
        был напуган
      • +1
        не случайно, хабр лежал с 500й ошибкой, сделал паузу, посмотрел в соседнем окне, что коммента не появилось и попробовал еще раз и так раза 4
  • 0
    — Consider using a custom data exchange format for large datasets, as an alternative to XML and JSON

    — Крайне вредный совет. Постарайтесь максимально избежать сообственных форматов данных.

    Автор оригинальной статьи приводит ссылку на:
    code.flickr.com/blog/2009/03/18/building-fast-client-side-searches/
    где сравниваются скорость загрузки данных с помощью разных форматов и объясняется, где и когда лучше его использовать. Так что совет совсем не вредный, а гибкий, аргументированный и подкреплен примерами.
    • +2
      статья за March 18th, 2009. И уже неактуальна. Потому что встроенное декодирование ЖСОН появилось:
      * Mozilla Firefox 3.5+ — июнь, 2009 года
      * Microsoft Internet Explorer 8 — 2009 года
      * Браузеры, основанные на WebKit (например, Google Chrome, Apple Safari) — я так понимаю, где-то в 2009-году, до июня
      * Opera 10.5+

      То есть все современные и даже многие устаревшие браузеры имеют встроенную реализацию, о которой в Марте 2009-ого было еще неизвестно.
      • +1
        Соглашусь с аргументом, но точку в этом вопросе поставят результаты актуального теста.
        • +1
          тесты показали, что я — неправ. сейчас обновлю топик
          fx 3.5.11
          json : 116ms
          plain: 53ms

          chrome 7.0.517.41 beta
          json : 162ms
          plain: 33ms
  • 0
    мне одному кажется что заголовок смахивает на Node.js

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