Зло, скрытое в 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 код можно передавать в виде строки только в том случае, если в нем используются только глобальные объекты. А лучше и вовсе забыть про эту возможность, и всегда пользоваться только функциями.
|
Ещё одним существенным недостатком строк является несжимаемость кода.
Вообще, там нечего сжимать, кроме как убрать лишние пробелы.
Ересь. В строку можно засунуть любой код, правила сжатия те-же, что и для js, единственное - это строка, поэтому правила минификатора на нее не распространяются. Сжимаемость - это проблема. Но главная проблема - это медленность, т.к. строка предварительно проходит процедуру eval, которая медлительна и ресурсозатратна...
Не любой, а только содержащий глобальные переменные, потому что здесь не идет речь о том, как хранить код в строках. Мы говорим про setTimeout, а говорить здесь о чем-то абстрактном смысла нет.
Так вот если сжать код, где только глобальные переменные, он останется таким же, исчезнут только лишние символы вроде пробелов. Разве это утверждение ересь???
Нет, можно написать так:
Тогда, конечно, есть, что сжимать, но полностью исчезает всякий здравый смысл!!!
Google Closure Compiler в "продвинутом режиме" жмёт переменные глобального пространства, так что проблемы всё-таки будут.
Вот тест:
1) До сжатия
var myVar = 121;
setTimeout("alert(myVar)", 500);
2) После
setTimeout("alert(myVar)",500);
Т.е. гугл не может увидеть в строке вызов переменной и поэтому, поскольку она больше не использовалась - он просто её удалил, т.е. мы получили ошибку.
Если бы переменная всё-таки использовалась бы, то он сжал бы её в однобуквенную, а в строке - нет - опять ошибка.
Даже если ты укажешь одну переменную, её имя минификатором сжато не будет. В итоге, попробуй объявить функцию в глобальном контексте, а потом вызвать её из строки в setTimeout, предварительно сжав код минификатором. Имя функции сожмется в 1-2 символа, в строке код останется не изменным, в итоге мы вызываем отсутствующую функцию и получаем исключение. В купе с тем, что строки обрабатываются гораздо медленнее, так как к ним применяется процедура eval, их лучше не использовать.
Но если ты включаешь в setTimeout только строку "alert('OK')", то в этом как раз здравый смысл отсутствует. В других случаях - всегда есть что сжимать...
+1
отличная статья! таймауты действительно зло..
Не сами таймауты и интервалы - зло, а строки, которые можно использовать в первом параметре вместо функции - зло, точнее не зло, а некое неудобство.
Раньше когда был только IE я именно это неудобство и терпел, но пользовался, т.к. не было других вариантов.
А возможность сама по себе таймаутов и интервалов - фундаментальна и неотъемлема.
Roksa warszawa wola
Отправить комментарий
Приветствуются комментарии:Для остальных вопросов и обсуждений есть форум.