Fundamentals 8 min read

Why JavaScript’s with Statement Can Leak Data and Slow Down Your Code

This article explains how the JavaScript with statement creates a new lexical scope that can unintentionally create global variables, cause data leakage, and dramatically degrade performance, while also covering LHS/RHS lookup, execution contexts, variable/activation objects, and scope chain mechanics.

Tencent IMWeb Frontend Team
Tencent IMWeb Frontend Team
Tencent IMWeb Frontend Team
Why JavaScript’s with Statement Can Leak Data and Slow Down Your Code

1. Background

During a casual conversation the author wondered why the

with

statement is considered problematic, and traced its origins through VO (Variable Object) and AO (Activation Object) to understand its behavior.

2. with

The

with

statement provides a namespace‑like access to an object's properties, allowing direct reference without the object prefix.

<code>const o = { a: 1, b: 2 };
with (o) {
    console.log(a); // 1
    b = 3; // o: { a: 1, b: 3 }
}
</code>

It looks convenient, but nesting

with

can lead to scope‑chain side effects.

<code>const o = { a: 1, b: 2 };
const p = { a: 3 };
with (o) {
    console.log(a); // 1
    with (p) {
        console.log(a); // 3
        b = 4; // o: { a: 1, b: 4 }
        c = 5; // window.c = 5
    }
}
</code>

If a property does not exist, the assignment follows the scope chain and, in non‑strict mode, creates an implicit global variable, leading to data leakage.

The failure to find a variable during an LHS lookup results in an implicit global (or a

ReferenceError

in strict mode), whereas a failed RHS lookup throws a

ReferenceError

immediately.

2.1 Performance issue

Using

with

prevents the engine from optimizing property access because it introduces a new lexical environment.

<code>function f () {
    const o = { a: 1 };
    console.time();
    with (o) {
        for (let i = 0; i < 100000; i++) {
            a = 2;
        }
    }
    console.timeEnd();
}
</code>

Benchmark results (image omitted) show a nearly ten‑fold slowdown compared to the direct property access version:

<code>function f () {
    const o = { a: 1 };
    console.time();
    for (let i = 0; i < 100000; i++) {
        o.a = 2;
    }
    console.timeEnd();
}
</code>

The reason is that the pre‑compilation phase cannot optimize the property lookup when

with

creates a separate lexical scope, forcing the engine to resolve identifiers at runtime.

3. LHS and RHS

LHS (Left‑Hand Side) is the target of an assignment.

RHS (Right‑Hand Side) is the source value of an assignment.

Example:

<code>var a = 1;
</code>

The code is split into declaration and assignment:

<code>var a;
a = 1;
</code>

When accessing

a

, a RHS lookup obtains its value, while an LHS lookup determines where to store a new value. A failed RHS throws

ReferenceError

; a failed LHS creates an implicit global (non‑strict) or also throws in strict mode.

4. Execution context and scope chain

JavaScript has three execution environments: global, function, and eval. Each execution context (EC) has a Variable Object (VO), a scope chain, and a

this

binding, forming an Execution Context Stack (ECS).

4.1 VO

The Variable Object stores variables, function declarations, and parameters. In the global EC,

VO === this === global

.

4.2 AO

In a function EC, the Activation Object (AO) replaces the VO; they are identical (

VO === AO

).

<code>function foo(b) {
    const a = 1;
}
foo(1);
// AO: { arguments: {...}, a: 1, b: 1 }
</code>

4.3 Scope chain

The scope chain links the current VO/AO with all parent VOs/AOs, enabling identifier resolution.

<code>var x = 10;
function foo() {
    var y = 20;
    function bar() {
        var z = 30;
        console.log(x + y + z);
    }
    bar();
}
foo();
</code>

Diagram (image omitted) shows orange arrows pointing to VO/AO and blue arrows representing the scope chain.

Scope chain diagram
Scope chain diagram

Understanding the scope chain makes it easy to relate to JavaScript’s prototype chain; together they form a two‑dimensional lookup: first locate an object via the scope chain, then locate a property via the prototype chain.

<code>const foo = {};
function bar() {
    Object.prototype.a = 'Set foo.a from prototype';
    return function () {
        console.log(foo.a);
    };
}
bar()(); // Set foo.a from prototype
</code>
PerformanceJavaScriptExecution ContextScope Chainwith statementLHSRHS
Tencent IMWeb Frontend Team
Written by

Tencent IMWeb Frontend Team

IMWeb Frontend Community gathering frontend development enthusiasts. Follow us for refined live courses by top experts, cutting‑edge technical posts, and to sharpen your frontend skills.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.