Конструкция with
позволяет использовать в качестве области видимости для переменных произвольный объект.
В современном JavaScript от этой конструкции отказались. С use strict
она не работает, но её ещё можно найти в старом коде, так что стоит познакомиться с ней, чтобы если что – понимать, о чём речь.
Синтаксис:
with(obj) {
...код...
}
Любое обращение к переменной внутри with
сначала ищет её среди свойств obj
, а только потом – вне with
.
Пример
В примере ниже переменная будет взята не из глобальной области, а из obj
:
var a = 5;
var obj = {
a: 10
};
with(obj) {
alert( a ); // 10, из obj
}
Попробуем получить переменную, которой в obj
нет:
var b = 1;
var obj = {
a: 10
};
with(obj) {
alert( b ); // 1, из window
}
Здесь интерпретатор сначала проверяет наличие obj.b
, не находит и идёт вне with
.
Особенно забавно выглядит применение вложенных with
:
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
, как и во вложенных функциях – переменная изменяется в той области, где была найдена.
Например:
var obj = {
a: 10
}
with(obj) {
a = 20;
}
alert( obj.a ); // 20, переменная была изменена в объекте
Почему отказались от with?
Есть несколько причин.
-
В современном стандарте
JavaScript
отказались отwith
, потому что конструкцияwith
подвержена ошибкам и непрозрачна.Проблемы возникают в том случае, когда в
with(obj)
присваивается переменная, которая по замыслу должна быть в свойствахobj
, но её там нет.Например:
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 и UglifyJS. Обычно они переименовывают локальные переменные в более короткие имена, но не свойства объектов. С конструкциейwith
до запуска кода непонятно – откуда будет взята переменная. Поэтому выходит, что, на всякий случай (если это свойство), лучше её не переименовывать. Таким образом, качество сжатия кода страдает. -
Ну и, наконец, производительность – усложнение поиска переменной из-за
with
влечёт дополнительные накладные расходы.Современные движки применяют много внутренних оптимизаций, ряд которых не может быть применён к коду, в котором есть
with
.Вот, к примеру, запустите этот код в современном браузере. Производительность функции
fast
существенно отличается отslow
с пустым(!)with
. И дело тут именно вwith
, т.к. наличие этой конструкции препятствует оптимизации.var i = 0; function fast() { i++; } function slow() { with(i) {} i++; } var time = performance.now(); while (i < 1000000) fast(); alert( "Без with: " + (performance.now() - time) ); var time = performance.now(); i = 0; while (i < 1000000) slow(); alert( "С with: " + (performance.now() - 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
устарела и не рекомендуется по ряду причин. Избегайте её.