# Устаревшая конструкция "with"
Конструкция `with` позволяет использовать в качестве области видимости для переменных произвольный объект.
В современном JavaScript от этой конструкции отказались. С `use strict` она не работает, но её ещё можно найти в старом коде, так что стоит познакомиться с ней, чтобы если что -- понимать, о чём речь.
[cut]
Синтаксис:
```js
with(obj) {
... код ...
}
```
**Любое обращение к переменной внутри `with` сначала ищет её среди свойств `obj`, а только потом -- вне `with`.**
## Пример
В примере ниже переменная будет взята не из глобальной области, а из `obj`:
```js
//+ run
var a = 5;
var obj = { a : 10 };
*!*
with(obj) {
alert(a); // 10, из obj
}
*/!*
```
Попробуем получить переменную, которой в `obj` нет:
```js
//+ run
var b = 1;
var obj = { a : 10 };
*!*
with(obj) {
alert(b); // 1, из window
}
*/!*
```
Здесь интерпретатор сначала проверяет наличие `obj.b`, не находит и идет вне `with`.
Особенно забавно выглядит применение вложенных `with`:
```js
//+ 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`, как и во вложенных функциях -- переменная изменяется в той области, где была найдена.
Например:
```js
//+ run
var obj = { a : 10 }
*!*
with(obj) {
a = 20;
}
*/!*
alert(obj.a); // 20, переменная была изменена в объекте
```
## Почему отказались от with?
Есть несколько причин.
- В современном стандарте `JavaScript` отказались от `with`, потому что **конструкция `with` подвержена ошибкам и непрозрачна.**
Проблемы возникают в том случае, когда в `with(obj)` присваивается переменная, которая по замыслу должна быть в свойствах `obj`, но ее там нет.
Например:
```js
//+ 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`, т.к. наличие этой конструкции препятствует оптимизации.
```js
//+ 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` рекомендуется использовать временную переменную, например:
```js
/* вместо
with(elem.style) {
top = '10px';
left = '20px';
}
*/
var s = elem.style;
s.top = '10px';
s.left = '0';
```
Это не так элегантно, но убирает лишний уровень вложенности и абсолютно точно понятно, что будет происходить и куда присвоятся свойства.
## Итого
- Конструкция `with(obj) { ... }` использует `obj` как дополнительную область видимости. Все переменные, к которым идет обращение внутри блока, сначала ищутся в `obj`.
- Конструкция `with` устарела и не рекомендуется по ряду причин. Избегайте её.