Node.js With-Statement Bug
JavaScript Corners – Part 9
Node.js With-Statement Bug
What does the following evil code print?
var x = 'before'; var obj = { x }; with (obj) { x = (delete x, 'after'); } console.log(x);
If you’re not sure, don’t worry — neither are current JavaScript engines. Firefox prints “after”, while Edge, IE, and Node.js print “before” (node v7.9.0). I believe that Firefox is correct in this case.
The tricky statement is obviously the following one, which sets a property on an object in the same statement that deletes the property:
x = (delete x, 'after');
(Side note: if you’re not very familiar with JavaScript, the relevant language features that are being used here are the delete operator, comma operator, and the good ol’ evil with statement).
What we expect to happen
The statement var x introduces a new variable at the script scope1.
The { x } expression creates a new object with a single property2 x, where the value of x is copied from the variable x in the outer scope, so it has the initial value of ‘before’.
The with statement brings the properties of the object obj into scope in a new lexical environment.
The statement x = (delete x, ‘after’) should perform the following steps:
- Evaluate the left hand side
- Evaluate the right hand side
- Assign the value from the right hand result, to the reference created when evaluating the left hand side
When the left hand side is evaluated, the property x will be found in object obj. The base value of the reference is the object, not the script variable scope.
The right hand side evaluates to ‘after’, but in the process it deletes the property x from obj. However, the reference on the left hand side should still refer to “the property named ‘x’ on the object obj“, even though the property with that name is now deleted.
When the assignment happens, it should create a new property named ‘x’ on object obj, with value ‘after’. The variable x in the outer scope should be left unaffected.
In this case, I think Node.js gets the wrong answer.
Theoretically, the script scope is the global scope. But in Node.js, scripts are wrapped in a module wrapper that changes the behavior of global
var
s. This doesn’t affect the outcome of this experiment though ↩Bonus fact. Object literals inherit from the global intrinsic object
Object.prototype
, which has other properties on it, such astoString
. So when I say that it has a single property, it would be more accurate to instead say that it has a single own property ↩