6.2 KiB
Устаревшая конструкция "with"
Конструкция with
позволяет использовать в качестве области видимости для переменных произвольный объект.
В современном JavaScript от этой конструкции отказались. С use strict
она не работает, но её ещё можно найти в старом коде, так что стоит познакомиться с ней, чтобы если что -- понимать, о чём речь.
[cut] Синтаксис:
with(obj) {
... код ...
}
Любое обращение к переменной внутри with
сначала ищет её среди свойств obj
, а только потом -- вне with
.
Пример
В примере ниже переменная будет взята не из глобальной области, а из obj
:
//+ run
var a = 5;
var obj = { a : 10 };
*!*
with(obj) {
alert(a); // 10, из obj
}
*/!*
Попробуем получить переменную, которой в obj
нет:
//+ run
var b = 1;
var obj = { a : 10 };
*!*
with(obj) {
alert(b); // 1, из window
}
*/!*
Здесь интерпретатор сначала проверяет наличие obj.b
, не находит и идет вне with
.
Особенно забавно выглядит применение вложенных with
:
//+ run
var obj = {
weight: 10,
size: {
width: 5,
height: 7
}
};
with(obj) {
with(size) { // size будет взят из obj
*!*
alert( width*height / weight ); // width,height из size, weight из obj
*/!*
}
}
Свойства из разных объектов используются как обычные переменные... Магия! Порядок поиска переменных в выделенном коде: size => obj => window

Изменения переменной
При использовании with
, как и во вложенных функциях -- переменная изменяется в той области, где была найдена.
Например:
//+ run
var obj = { a : 10 }
*!*
with(obj) {
a = 20;
}
*/!*
alert(obj.a); // 20, переменная была изменена в объекте
Почему отказались от with?
Есть несколько причин.
- В современном стандарте `JavaScript` отказались от `with`, потому что **конструкция `with` подвержена ошибкам и непрозрачна.**
Проблемы возникают в том случае, когда в
with(obj)
присваивается переменная, которая по замыслу должна быть в свойствахobj
, но ее там нет.Например:
//+ run var obj = { weight: 10 }; with(obj) { weight = 20; // (1) size = 35; // (2) } alert(obj.size); alert(window.size);
В строке
(2)
присваивается свойство, отсутствующее вobj
. В результате интерпретатор, не найдя его, создает новую глобальную переменнуюwindow.size
.Такие ошибки редки, но очень сложны в отладке, особенно если
size
изменилась не вwindow
, а где-нибудь во внешнемLexicalEnvironment
. - Еще одна причина -- **алгоритмы сжатия JavaScript не любят `with`**. Перед выкладкой на сервер JavaScript сжимают. Для этого есть много инструментов, например [Closure Compiler](http://code.google.com/intl/ru-RU/closure/compiler/) и [UglifyJS](https://github.com/mishoo/UglifyJS). Если вкратце -- они либо сжимают код с `with` с ошибками, либо оставляют его частично несжатым.
- Ну и, наконец, **производительность -- усложнение поиска переменной из-за `with` влечет дополнительные накладные расходы**. Современные движки применяют много внутренних оптимизаций, ряд которых не могут быть применены к коду, в котором есть `with`.
Вот, к примеру, запустите этот код в современном браузере. Производительность функции
fast
существенно отличаетсяslow
с пустым(!)with
. И дело тут именно вwith
, т.к. наличие этой конструкции препятствует оптимизации.//+ run var i = 0; function fast() { i++; } function slow() { with(i) {} i++; } var time = new Date(); while(i < 1000000) fast(); alert(new Date - time); var time = new Date(); i=0; while(i < 1000000) slow(); alert(new Date - time);
Замена with
Вместо with
рекомендуется использовать временную переменную, например:
/* вместо
with(elem.style) {
top = '10px';
left = '20px';
}
*/
var s = elem.style;
s.top = '10px';
s.left = '0';
Это не так элегантно, но убирает лишний уровень вложенности и абсолютно точно понятно, что будет происходить и куда присвоятся свойства.
Итого
- Конструкция `with(obj) { ... }` использует `obj` как дополнительную область видимости. Все переменные, к которым идет обращение внутри блока, сначала ищутся в `obj`.
- Конструкция `with` устарела и не рекомендуется по ряду причин. Избегайте её.