
- •Введение
- •Введение в JavaScript
- •Справочники и спецификации
- •Редакторы для кода
- •Консоль разработчика
- •Основы JavaScript
- •Привет, мир!
- •Внешние скрипты, порядок исполнения
- •Структура кода
- •Современный стандарт, «use strict»
- •Переменные
- •Правильный выбор имени переменной
- •Шесть типов данных, typeof
- •Основные операторы
- •Операторы сравнения и логические значения
- •Побитовые операторы
- •Взаимодействие с пользователем: alert, prompt, confirm
- •Условные операторы: if, '?'
- •Логические операторы
- •Преобразование типов для примитивов
- •Циклы while, for
- •Конструкция switch
- •Функции
- •Функциональные выражения
- •Именованные функциональные выражения
- •Всё вместе: особенности JavaScript
- •Качество кода
- •Отладка в браузере Chrome
- •Советы по стилю кода
- •Как писать неподдерживаемый код?
- •Автоматические тесты при помощи chai и mocha
- •Структуры данных
- •Введение в методы и свойства
- •Числа
- •Строки
- •Объекты как ассоциативные массивы
- •Объекты: перебор свойств
- •Объекты: передача по ссылке
- •Массивы c числовыми индексами
- •Массивы: методы
- •Массив: перебирающие методы
- •Псевдомассив аргументов «arguments»
- •Дата и Время
- •Замыкания, область видимости
- •Глобальный объект
- •Замыкания, функции изнутри
- •[[Scope]] для new Function
- •Локальные переменные для объекта
- •Модули через замыкания
- •Управление памятью в JavaScript
- •Устаревшая конструкция «with»
- •Методы объектов и контекст вызова
- •Методы объектов, this
- •Преобразование объектов: toString и valueOf
- •Создание объектов через «new»
- •Дескрипторы, геттеры и сеттеры свойств
- •Статические и фабричные методы
- •Явное указание this: «call», «apply»
- •Привязка контекста и карринг: «bind»
- •Функции-обёртки, декораторы
- •Некоторые другие возможности
- •Типы данных: [[Class]], instanceof и утки
- •Формат JSON, метод toJSON
- •setTimeout и setInterval
- •Запуск кода из строки: eval
- •Перехват ошибок, «try..catch»
- •ООП в функциональном стиле
- •Введение
- •Внутренний и внешний интерфейс
- •Геттеры и сеттеры
- •Функциональное наследование
- •ООП в прототипном стиле
- •Прототип объекта
- •Свойство F.prototype и создание объектов через new
- •Встроенные «классы» в JavaScript
- •Свои классы на прототипах
- •Наследование классов в JavaScript
- •Проверка класса: «instanceof»
- •Свои ошибки, наследование от Error
Вызов 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) и возвращает его результат.