Guru meditations on scope chains of closures

Guru meditations on scope chains of closures

  • Comments 11

I just had a really interesting meeting with some of the scripting community MVPs.  I may write up some notes on that meeting here next week, so watch this space.

In other news, someone asked me last night (and I quote)

Oh ECMA guru, can you provide a succinct explanation of why the following code works, but if I replace x with this it fails?

Number.prototype.times = function(f) {
  for(var i = 0; i < this; i++)
    f();
}
Number.prototype.raiseTo = function(n) {
  var answer = 1;
  var x = this;
  n.times(function(){ answer = x * answer;}); // succeeds
//n.times(function(){ answer = this * answer;}); // fails!
  return answer;
}
(3).raiseTo(2).raiseTo(5);

Well I'd hardly describe myself as a guru, but I take the questioner's point -- surely if you set x to this then x and this should be aliases for each other, right?

Often. But not in this case.

The inner function's execution context contains a scope chain that includes the variable object of the outer function, so the inner function can see x by looking up the scope chain. But this is not a variable, so it is not member of the outer function's variable object. Rather, it is a member of the outer function's execution context.

Therefore the inner function's execution context gets the global this, in accordance with the ECMAScript specification, revision 3, section 10.2.3:

The caller provides the this value. If the this value provided by the caller is not an object (including the case where it is null), then the this value is the global object.

The global object is not something that can be multiplied, so the alternate version of the program fails.

The code above was of course not real production code, it was just code that came up while testing an engine implementation. Obviously JScript already has a built-in power function that works much better than this crazy thing. But I'm in an expansive mood, so next time I'll talk a bit about some of the shortcomings of this programming style, and some ideas for improving it.

  • Speaking about this kind of thing. I'd love to hear what you think about Atlas, if you want to write up a post on it.
  • Interesting, even if nothing new. I can hardly wait for the next post, however.
  • Re: nothing new: The JScript and VBScript portions of my blog are about languages that we started developing ten years ago and stopped developing five years ago. There shouldn't be anything new in those posts! The new stuff is in my VSTO and C# posts.

    Re: atlas: I talk occasionally with the Atlas team management to keep up-to-date on what they're doing at a broad level, but I am no expert on the deep technical side. What do YOU think of Atlas? What more would you like to see?
  • Heh, well, maybe there shouldn't be anything new, but from time to time I do find new and interesting thing, perhaps something old from a new perspective. And even when it's not something new, I still enjoy reading about it, I often find something "new" (or forgotten) in the "old" stuff.

    I'm fresh out of college so I didn't have a chance to follow JScript and VBScript when it just came out, but I found that script related juicy articles are scattered around the web, and your blog is one of the greatest sources:)
  • Thanks! I'm glad you enjoy it. If you have suggestions for future subjects -- new or old -- let me know. I'm always looking for more ideas.
  • I haven't really got to play with Atlas much so I can't say a whole lot, but what I saw at the PDC was very impressive. I didn't expect it be as extensive at it seems, essentially turning JScript into an object-oriented language. It looks like it is designed very well. However, it looks like it still takes quite a bit of work to make AJAX-like websites, and the more I get into it the more complex it gets. I'm looking forward to checking it out more in depth - I just need to find the time.
  • Funny, I would said that it failed because 'this' inside the annonymous function referred the function object for the annonymous function rather than it being scoped out to the global object.
  • Why would "this" ever refer to the function? If you had

    function foo() { print(this==foo); }

    you'd expect that to be false, right? It's no different for anonymous functions.

    (Of course, that might be true in some really bizarre case, like "foo.foo=foo; foo.foo();" -- but normally the "this" reference should never be the function.)


  • My confusion came from me being an old hack at javascript from the early days, but my statement was wrong even in that context. There is one case where "this" refers to the function object itself that is in the "new" context. Such as:

    function foo (bar) { this.bar = bar; }
    var baz = new foo(42);

    But that case obviously does not apply to anonymous functions invoked as methods to another object.
  • No, "this" doesn't refer to the function object in that case either. It refers to the value that is about to be assigned to baz. Try it!

    function foo(bar){ print(this==foo); }
    var baz = new foo(42);

    That will be false for sure.
  • If you'd really like to use the 'this' keyword in an anonymous function, you could do so like this:

    Number.prototype.instanceFn = function(f)
    {
    var oThis = this;
    return function() { f.apply(oThis, arguments); }
    }

    ...

    Number.prototype.raiseTo = function(n) {
    var answer = 1;
    n.times(this.instanceFn(function(){ answer = this * answer;})); // succeeds!
    return answer;
    }

    Of course, the resulting call stack can get pretty messy at this point.
Page 1 of 1 (11 items)