Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Язык JavaScript часть1.pdf
Скачиваний:
204
Добавлен:
22.03.2016
Размер:
8.92 Mб
Скачать

Вызов throttle(onmousemove, 100), по сути, предназначен для того, чтобы «притормаживать»

обработку onmousemove. Технически, он должен возвращать обёртку, которая передаёт все вызовы onmousemove, но не чаще чем раз в 100мс.

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

Визуально это даст следующую картину обработки перемещений мыши:

1.Первое обновление произойдёт сразу (это важно, посетитель тут же видит реакцию на своё действие).

2.Дальше может быть много вызовов (микро-передвижений) с разными координатами, но пока не пройдёт 100мс — ничего не будет.

3.По истечении 100мс — опять обновление, с последними координатами. Промежуточные микро-передвижения игнорированы.

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

Ещё раз заметим — задача из реальной жизни, и в ней принципиально важно, что последнее передвижение обрабатывается. Пользователь должен увидеть, где остановил мышь.

Пример использования:

var f = function(a) { console.log(a)

};

// затормозить функцию до одного раза в 1000 мс var f1000 = throttle(f, 1000);

f1000(1); // выведет 1

f1000(2); // (тормозим, не прошло 1000мс) f1000(3); // (тормозим, не прошло 1000мс)

//когда пройдёт 1000мс...

//выведет 3, промежуточное значение 2 игнорируется

Открыть песочницу с тестами для задачи.

К решению

Запуск кода из строки: eval

Функция eval(code)позволяет выполнить код, переданный ей в виде строки.

Этот код будет выполнен в текущей области видимости.

Использование eval

В простейшем случае evalвсего лишь выполняет код, например:

var a = 1; (function() {

var a = 2;

eval(' alert(a) '); // 2 })()

Но он может не только выполнить код, но и вернуть результат.

Вызов evalвозвращает последнее вычисленное выражение:

Например:

alert( eval('1+1') ); // 2

При вызове evalимеет полный доступ к локальным переменным.

Это означает, что текущие переменные могут быть изменены или дополнены:

var x = 5;

eval(" alert( x ); x = 10"); // 5, доступ к старому значению alert( x ); // 10, значение изменено внутри eval

В строгом режиме evalимеет свою область видимости

В строгом режиме функционал evalчуть-чуть меняется.

При use strictкод внутри evalпо-прежнему сможет читать и менять внешние переменные, однако переменные и функции, объявленные внутри eval, не попадут наружу.

"use strict";

eval("var a = 5; function f() { }");

alert( a ); // ошибка, переменная не определена // функция f тоже не видна снаружи

Иными словами, в новом стандарте evalимеет свою область видимости, а к внешним

переменным обращается через замыкание, аналогично тому, как работают обычные функции.

Неграмотное использование eval

Начнём с того, что evalприменяется очень редко. Действительно редко. Есть даже такое выражение «eval is evil» (eval — зло).

Причина проста: когда-то JavaScript был гораздо более слабым языком, чем сейчас, и

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

Но если вы действительно знаете, что это именно тот случай и вам необходим eval— есть ряд вещей, которые нужно иметь в виду.

Доступ к локальным переменным — худшее, что можно сделать при eval.

Дело в том, что локальные переменные могут быть легко переименованы:

function sayHi() {

var phrase = "Привет"; eval(str);

}

Переменная phraseможет быть переименована в hello, и если строка strобращается к ней — будет ошибка.

Современные средства сжатия JavaScript переименовывают локальные переменные автоматически. Это считается безопасным, так как локальная переменная видна лишь внутри функции и если в ней везде поменять phraseна p, то никто этого не заметит.

До сжатия:

function sayHi() {

var phrase = "Привет"; alert( phrase );

}

После сжатия:

function sayHi() { var a = "Привет"; alert( a );

}

На самом деле всё ещё проще — в данном случае утилита сжатия автоматически уберёт переменную aи код станет таким:

function sayHi() { alert( "Привет" );

}

Итак, если где-то в функции есть eval, то его взаимодействие с локальными переменными будет нарушено с непредсказуемыми побочными эффектами.

Некоторые инструменты сжатия предупреждают, когда видят evalили стараются вообще не

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

Как правило, evalне нужен, именно поэтому говорят, «eval is evil».

Запуск скрипта в глобальной области

Ок, взаимодействовать с локальными переменными нельзя.

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

Здесь evalможет пригодиться. Есть два трюка для выполнения кода в глобальной области:

1.Везде, кроме IE8-, достаточно вызвать evalне напрямую, а через window.eval.

Вот так:

var a = 1;

(function() {

var a = 2;

window.eval(' alert(a) '); // 1, выполнено глобально везде, кроме IE8 })();

2.В IE8можно применить нестандартную фунцию execScript . Она, как и eval, выполняет код, но всегда в глобальной области видимости и не возвращает значение.

Оба способа можно объединить в единой функции globalEval(code), выполняющей код без доступа к локальным переменным:

function globalEval(code) { // объединим два способа в одну функцию window.execScript ? execScript(code) : window.eval(code);

}

var a = 1;

(function() {

var a = 2;

globalEval(' alert(a) '); // 1, во всех браузерах

})();

Внешние данные через new Function

Итак, у нас есть код, который, всё же, нужно выполнить динамически, через eval, но не просто скрипт — а ему нужно передать какие-то значения.

Как мы говорили ранее, считать их из локальных переменных нельзя: это подвержено ошибкам при переименовании переменных и сразу ломается при сжатии JavaScript. Да и вообще, неочевидно и криво.

К счастью, существует отличная альтернатива eval, которая позволяет корректно

взаимодействовать c внешним кодом: new Function.

Вызов new Function('a,b', '..тело..')создает функцию с указанными аргументами a,bи

телом. Как мы помним, доступа к текущему замыканию у такой функции не будет, но можно передать параметры и получить результат.

Например:

var a = 2, b = 3;

//вместо обращения к a,b через eval

//будем принимать их как аргументы динамически созданной функции var mul = new Function('a, b', ' return a * b;');

alert( mul(a, b) ); // 6

JSON и eval

В браузерах IE7не было методов JSON.stringifyи JSON.parse, поэтому работа с JSON происходила через eval.

Этот способ работы с JSON давно устарел, но его можно встретить кое-где в старом коде, так что для примера рассмотрим его.

Вызов eval(code)выполняет код и, если это выражение, то возвращает его значение, поэтому можно в качестве кода передать JSON.

Например:

var str = '{ \ "name": "Вася", \ "age": 25 \

}';

var user = eval('(' + str + ')');

alert( user.name ); // Вася

Зачем здесь нужны скобки eval( '(' + str + ')' ), почему не просто eval(str)?

…Всё дело в том, что в JavaScript с фигурной скобки {начинаются не только объекты, а в том

числе и «блоки кода». Что имеется в виду в данном случае — интерпретатор определяет по контексту. Если в основном потоке кода — то блок, если в контексте выражения, то объект.

Поэтому если передать в evalобъект напрямую, то интерпретатор подумает, что это на самом деле блок кода, а там внутри какие-то двоеточия…

Вот, для примера, evalбез скобок, он выдаст ошибку:

var user = eval('{ "name": "Вася", "age": 25 }');

А если evalполучает выражение в скобках ( ... ), то интерпретатор точно знает, что это не блок кода, а объект:

var user = eval('( { "name": "Вася", "age": 25 } )'); alert( user.age ); // 25

Осторожно, злой JSON!

Если мы получаем JSON из недоверенного источника, например с чужого сервера, то разбор через evalможет быть опасен.

Например, чужой сервер может быть взломан (за свой-то код мы отвечаем, а за чужой — нет) и вместо JSON вставлен злонамеренный JavaScript-код.

Поэтому рекомендуется, всё же, использовать JSON.parse.

При разборе через JSON.parseнекорректный JSON просто приведёт к ошибке, а вот при разборе через evalэтот код реально выполнится, он может вывести что-то на странице, перенаправить посетителя куда-то и т.п.

Итого

Функция eval(str)выполняет код и возвращает последнее вычисленное выражение. В современном JavaScript она используется редко.

Вызов evalможет читать и менять локальные переменные. Это — зло, которого нужно избегать.

Для выполнения скрипта в глобальной области используются трюк с window.eval/execScript. При этом локальные переменные не будут затронуты, так что такое выполнение безопасно и иногда, в редких архитектурах, может быть полезным.

Если нужно выполняемый код всё же должен взаимодействовать с локальными переменными — используйте new Function. Создавайте функцию из строки и передавайте переменные ей, это надёжно и безопасно.

Ещё примеры использования evalвы найдёте далее, в главе Формат JSON, метод toJSON.

Задачи

Eval-калькулятор

важность: 4

Напишите интерфейс, который принимает математическое выражение (prompt) и возвращает его результат.