Javascript.RU

Зло, скрытое в setTimeout/setInterval.

Как известно, функция setTimeout первым параметром принимает функцию или строку кода, которая будет исполнена. Но я рекомендую отказаться от использования строк. Далее я расскажу, какие ошибки могут возникнуть, и как вообще это работает.

Все сказанное ниже одинаково справедливо и для setTimeout, и для setInterval. Но я разберу тему на примере setTimeout.

Рассмотрим простой пример:

(function(){
  var variable = "someValue";
  
  setTimeout(function(){
    alert( variable );
  }, 1);
  
  setTimeout("alert( variable );", 1);
}());

Первый alert показывает значение переменной, а второго - нет. Вместо него получаем ReferenceError: variable is not defined.

Чтобы понять, почему так происходит, разберемся, как setTimeout работает со строкой. Есть два подхода: как делают все и как делает ie ( в общем, как обычно ).

В !ie строка исполняется в глобальном контексте. Выглядит это примерно так:

function myTimeout(code, delay){
  return setTimeout(function(){
    *!*eval.call(window, code);*/!*
  }, delay);
};

myTimeout('alert("OK")', 1);

ie создает из строки функцию и выполняет ее:

function myTimeout(code, delay){
  return setTimeout(function(){
    *!*Function(code)();*/!*
  }, delay);
};

myTimeout('alert("OK")', 1);

Замечу, что конструктор Function всегда имеет в области видимости только глобальный объект.

Разницу в подходах хорошо иллюстрирует следующий пример:

test = "value",
function(){
  setTimeout("var test = null; alert( window.test );", 1);
}();

FF/Chrome/Opera выдают null (т.е. если в глобальном объекте было важное свойство "test", оно будет "затерто"! а потом ищи, куда оно делось), а ie, благодаря оператору var - "value".

Но, несмотря на эту небольшую разницу, код всегда интерпретируется в глобальном контексте:

test = "value",
function(){
  var test = "some";
  setTimeout("alert( test )", 1); // "value"
}();

Таким образом, в setTimeout код можно передавать в виде строки только в том случае, если в нем используются только глобальные объекты. А лучше и вовсе забыть про эту возможность, и всегда пользоваться только функциями.

+8

Автор: kobezzza, дата: 25 мая, 2011 - 12:25
#permalink

Ещё одним существенным недостатком строк является несжимаемость кода.


Автор: Sweet, дата: 25 мая, 2011 - 13:50
#permalink

Вообще, там нечего сжимать, кроме как убрать лишние пробелы.


Автор: I-zone, дата: 25 мая, 2011 - 21:28
#permalink

Ересь. В строку можно засунуть любой код, правила сжатия те-же, что и для js, единственное - это строка, поэтому правила минификатора на нее не распространяются. Сжимаемость - это проблема. Но главная проблема - это медленность, т.к. строка предварительно проходит процедуру eval, которая медлительна и ресурсозатратна...


Автор: Sweet, дата: 25 мая, 2011 - 23:03
#permalink

В строку можно засунуть любой код

Не любой, а только содержащий глобальные переменные, потому что здесь не идет речь о том, как хранить код в строках. Мы говорим про setTimeout, а говорить здесь о чем-то абстрактном смысла нет.
Так вот если сжать код, где только глобальные переменные, он останется таким же, исчезнут только лишние символы вроде пробелов. Разве это утверждение ересь???
Нет, можно написать так:

setTimeout("(function(){ var veryLongName = ... }())"...

Тогда, конечно, есть, что сжимать, но полностью исчезает всякий здравый смысл!!!


Автор: kobezzza, дата: 25 мая, 2011 - 23:31
#permalink

Google Closure Compiler в "продвинутом режиме" жмёт переменные глобального пространства, так что проблемы всё-таки будут.

Вот тест:
1) До сжатия

var myVar = 121;
setTimeout("alert(myVar)", 500);

2) После
setTimeout("alert(myVar)",500);

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


Автор: I-zone, дата: 27 мая, 2011 - 08:22
#permalink

Даже если ты укажешь одну переменную, её имя минификатором сжато не будет. В итоге, попробуй объявить функцию в глобальном контексте, а потом вызвать её из строки в setTimeout, предварительно сжав код минификатором. Имя функции сожмется в 1-2 символа, в строке код останется не изменным, в итоге мы вызываем отсутствующую функцию и получаем исключение. В купе с тем, что строки обрабатываются гораздо медленнее, так как к ним применяется процедура eval, их лучше не использовать.
Но если ты включаешь в setTimeout только строку "alert('OK')", то в этом как раз здравый смысл отсутствует. В других случаях - всегда есть что сжимать...


Автор: cranx, дата: 2 июня, 2011 - 10:47
#permalink

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

+1


Автор: Гость (не зарегистрирован), дата: 21 июля, 2011 - 12:18
#permalink

отличная статья! таймауты действительно зло..


Автор: mister_maxim, дата: 5 августа, 2011 - 17:01
#permalink

Не сами таймауты и интервалы - зло, а строки, которые можно использовать в первом параметре вместо функции - зло, точнее не зло, а некое неудобство.
Раньше когда был только IE я именно это неудобство и терпел, но пользовался, т.к. не было других вариантов.
А возможность сама по себе таймаутов и интервалов - фундаментальна и неотъемлема.


Автор: Гость (не зарегистрирован), дата: 16 апреля, 2022 - 02:26
#permalink

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

Приветствуются комментарии:
  • Полезные.
  • Дополняющие прочитанное.
  • Вопросы по прочитанному. Именно по прочитанному, чтобы ответ на него помог другим разобраться в предмете статьи. Другие вопросы могут быть удалены.
    Для остальных вопросов и обсуждений есть форум.
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
Антиспам
4 + 1 =
Введите результат. Например, для 1+3, введите 4.
 
Поиск по сайту
Другие записи этого автора
Больше записей нет. Прокомментируйте эту запись - может быть, тогда он что-нибудь еще хорошее напишет ;)
Содержание

Учебник javascript

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

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

Интерфейсы

Все об AJAX

Оптимизация

Разное

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

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