Pull to refresh

Оператор with и почему его не стоит использовать

Reading time 2 min
Views 13K
Original author: Axel Rauschmayer
Эта статья объясняет как работает with в JavaScript и почему его не рекомендуется использовать.

Синтаксис оператора with


  with (object)
        statement

with создает новую область видимости «scope» и представляет свойства объекта «object» как локальные переменные выражения «statement». Пример (скобки не обязательны для одного выражения, но их рекомендуется использовать):
with({ first: "John" }) { console.log("Hello " + first); } // Hello John

Существует похожий объект свойства которого одновременно являются глобальными переменными — этот объект называется global (в браузерах это window). Но в отличии от глобального объекта, переменные, которые объявлены в выражении with (блок statement) не добавляются к объекту «object», а просачиваются во внешний scope и существуют дальше.
with({}) { var x = "abc"; }
console.log(x) // 'abc'

Оператор with является устаревшим


Использование выражения with не желательно. Его использование запрещено в Strict Mode:
function foo() { "use strict"; with({}); }
// SyntaxError: strict mode code may not contain 'with' statements

Чем же заменить with:
with(foo.bar.baz) {
    console.log("Hello "+first+" "+last);
}

Вместо передачи переменной в with используйте короткое имя:
var b = foo.bar.baz;
console.log("Hello "+b.first+" "+b.last);

Если вы не желаете объявлять временную переменную в вашем scope, то используйте IIFE:
(function() {
    var b = foo.bar.baz;
    console.log("Hello "+b.first+" "+b.last);
}());

Вы также можете переслать этот объект в качестве параметра IIFE:
(function(b) {
    console.log("Hello "+b.first+" "+b.last);
}(foo.bar.baz));

Так почему же with — это плохо


Чтобы понять почему with является устаревшим, давайте рассмотрим пример и увидим как аргумент функции меняет её поведение. Вот эта функция:
function foo(arg) {
    with(arg) {
        console.log("arg: " + arg)
    }
}

Запустим функцию с разными параметрами:
foo("Hello");
// arg: Hello  - параметр arg
    
foo({});
// arg: [object Object]  - параметр arg
    
foo({ arg: "Hello" });
// arg: Hello  - свойство arg.arg!

Есть две причины по которым with является устаревшим:
Производительность: оператор with невозможно оптимизировать автоматически, потому что заранее не известно куда будет ссылаться arg: на реальную переменную или на свойство переменной внутри with. Это может измениться при каждом вызове.

Безопасность: нельзя определить куда ссылается переменная глядя на её окрестности (её лексическое окружение). По словам Brendan Eich именно из-за этого with считается устаревшим, а не из-за соображений производительности.
Tags:
Hubs:
+34
Comments 26
Comments Comments 26

Articles