Friday afternoon scoping quiz
On Feb 5, 2010, at 2:14 PM, P T Withington wrote:
({ 'foo': 20, 'test': function () { var f = function () { alert("foo:"+ foo); alert("this.foo:"+ this.foo); alert("bar:"+ bar); } with (this) { var foo = 42; var bar = 21; f.call(this); }
"Doctor, it hurts when I use 'with' in JavaScript!"
} }).test();
--
By my reading,
foo
andbar
are declared and initialized in
test
, and closed over byf
. I expect to see:foo:42 this.foo:20 bar:21
But in Rhino, Firefox, Safari and Opera I am seeing:
foo:undefined this.foo:42 bar:21
This is correct. The vars are hoisted but the initialization of foo
puts 42 where it found foo on the scope chain, in this.foo. There's no
'bar' property in |this| so that goes in the var.
Flash 10 gives me the answer I expected. I have not tested other JS
engines.
Flash 10 bug.
On 2010-02-05, at 17:25, Brendan Eich wrote:
On Feb 5, 2010, at 2:14 PM, P T Withington wrote:
({ 'foo': 20, 'test': function () { var f = function () { alert("foo:"+ foo); alert("this.foo:"+ this.foo); alert("bar:"+ bar); } with (this) { var foo = 42; var bar = 21; f.call(this); }
"Doctor, it hurts when I use 'with' in JavaScript!"
Indeed!
} }).test();
--
By my reading,
foo
andbar
are declared and initialized intest
, and closed over byf
. I expect to see:foo:42 this.foo:20 bar:21
But in Rhino, Firefox, Safari and Opera I am seeing:
foo:undefined this.foo:42 bar:21
This is correct. The vars are hoisted but the initialization of foo puts 42 where it found foo on the scope chain, in this.foo. There's no 'bar' property in |this| so that goes in the var.
Interesting! I couldn't convince myself that var hoisting would be quite so literal.
Flash 10 gives me the answer I expected. I have not tested other JS engines.
Flash 10 bug.
I will report. Thanks.
On 2010-02-05, at 17:42, P T Withington wrote:
Flash 10 gives me the answer I expected. I have not tested other JS engines.
Flash 10 bug.
I will report. Thanks.
FTR, appears to be already reported as: bugs.adobe.com/jira/browse/ASC
Hello P,
Saturday, February 6, 2010, 1:14:33 AM, you wrote:
({ 'foo': 20, 'test': function () { var f = function () { alert("foo:"+ foo); alert("this.foo:"+ this.foo); alert("bar:"+ bar); } with (this) { var foo = 42; var bar = 21; f.call(this); } } }).test();
When method .test is called, its this value on entering the context is set to anonymous object. The [[Scope]] of the internal 'f' function expression is set to current scope chain which is at the moment:
Scope chain (test): [ global, {foo: undefined, bar: undefined} ],
where the last object - is an activation object of the execution context of the 'test' function.
After that 'with' statement augment current scope chain with its object which is again main anonymous object:
Scope chain (test): [ global, {foo: undefined, bar: undefined}, {foo: 20, test: function () {...}} ]
And then, when 'f' internal function is activated, its this value is also set to main anonymous object by the .call method. Scope chain of the 'f' function is:
Scope chain (f): [ global, {foo: undefined, bar: undefined}, {foo: 20, test: function () {...}}, {activation object of 'f' context} ]
And then just identifier resolution works which resolved name bindings in the scope chain - from the deep (from the activation object) and up to the top.
That's why 'foo' identifier will be found in the second object of the scope chain (with value 20), and 'bar' - in third.
--
By my reading,
foo
andbar
are declared and initialized intest
, and closed over byf
. I expect to see:
foo:42 this.foo:20 bar:21
That's possible wihtout using 'with'.
But in Rhino, Firefox, Safari and Opera I am seeing:
foo:undefined this.foo:42 bar:21
Flash 10 gives me the answer I expected. I have not tested other JS engines.
Incorrect behavior.
Hello P,
Scope chain (f): [ global, {foo: undefined, bar: undefined}, {foo: 20, test: function () {...}}, {activation object of 'f' context} ]
Sorry, scope chain of 'f' context sure will not contain object added by the 'with' statement (my typo), scope chain above was described for 'test' context. And for 'f' is:
Scope chain (f): [ global, {foo: undefined, bar: 21}, {activation object of 'f' context} ]
On 2010-02-05, at 18:08, Dmitry A. Soshnikov wrote:
Scope chain (test): [ global, {foo: undefined, bar: undefined}, {foo: 20, test: function () {...}} ]
This is the key that I was missing, as Brendan made clear. Despite foo
and bar
being declared inside the with
, because declarations are hoisted, the var foo
is shadowed by the with (this)
when the assignment foo = 42
is evaluated.
Thanks for the clarifying illustrations.
({ 'foo': 20, 'test': function () { var f = function () { alert("foo:"+ foo); alert("this.foo:"+ this.foo); alert("bar:"+ bar); } with (this) { var foo = 42; var bar = 21; f.call(this); } } }).test();
--
By my reading,
foo
andbar
are declared and initialized intest
, and closed over byf
. I expect to see:foo:42 this.foo:20 bar:21
But in Rhino, Firefox, Safari and Opera I am seeing:
foo:undefined this.foo:42 bar:21
Flash 10 gives me the answer I expected. I have not tested other JS engines.