7

If I run this code within the Chrome Developer Tools:

var test = (function () {

  var publicFunction,
      privateFunction1,
      privateFunction2;

  privateFunction1 = function privateFunction1() {
    return true;
  };

  privateFunction2 = function privateFunction2() {
    return true;
  };

  publicFunction = function publicFunction() {
    privateFunction1();
    debugger;
  };

  return {
    publicFunction: publicFunction
  };
})();

why is privateFunction1 in scope at the breakpoint, while privateFunction2 is not?

Screenshot of Chrome Dev Tools

janfoeh
  • 10,243
  • 2
  • 31
  • 56
  • 1
    Side note: There's no purpose served by the variables you've declared, you can (and probably should) just do this instead: http://pastie.org/6150861 Because you're assigning *named function expressions* to those variables, on IE8 and below you're creating two functions for each of your functions (at two different times). A difference that makes no difference unless you happen to hook up an event handler using one of the two and try to unhook it later with the other one. More on my blog: [*Double-take*](http://blog.niftysnippets.org/2010/09/double-take.html) – T.J. Crowder Feb 13 '13 at 12:59
  • 1
    Well, I *thought* it was because V8 (Chrome's JavaScript engine) was removing `privateFunction2` entirely because it wasn't used anywhere (it was dead code). Then I tried to prove it by using it elsewhere, and that didn't change anything. Very, very odd. – T.J. Crowder Feb 13 '13 at 13:06
  • Thanks, I did not know about IEs issues with NFEs. Still, I prefer named function expressions over declarations, if only because I dislike hoisting. – janfoeh Feb 13 '13 at 13:10

1 Answers1

4

Fascinating question.

privateFunction2 is in scope for publicFunction, but publicFunction never actually uses it. I believe what you're seeing in the debugger is because V8 (Chrome's JavaScript engine) optimizes the content of closures for various reasons (including minimizing memory use).

In theory, according to the specification, publicFunction closes over (has an enduring reference to) all symbols in scope where it's defined. Specifically, an execution context was created for the call to your outermost anonymous function, and that execution context has a lexical environment with an associated binding object to which publicFunction has an implicit, anonymous reference. That binding object has properties on it with (in theory) the names publicFunction, privateFunction1, privateFunction2, and a few other things (arguments and such).

But the thing is that publicFunction doesn't actually reference anything but privateFunction1, and with the code in place for it, it cannot reference anything else. For it to reference anything else, you'd have to change its code, and of course them V8 would make a different decision. The code in publicFunction has no eval(string) or new Function(string) calls, so V8 is free to do a static analysis on the symbols it references. That means that, absent the debugger, there is no point whatsoever to the binding object keeping those other properties. They're never used.

Since V8 is an aggressively optimizing compiler (yes, compiler), apparently it removes dead properties from the binding object of the execution context.

If I add something to publicFunction that uses privateFunction2 for anything, I can then refer to it from the console just like I can privateFunction1.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • It seems you're right: if I give publicFunction a parameter and eval it, everything else appears in scope as expected. Thank you! – janfoeh Feb 13 '13 at 13:27