16

In my top-level function, I’m importing some dependencies using require.js. And they’re there, no problem. Within this function, I define a callback function and attempt to use some of the variables imported via require.js, that is, variables within the parent closure.

And they just aren’t there, as confirmed by a breakpoint and a peek at the Chrome inspector’s Scope Variables panel.

I understand that fn.apply and friends only set the context as far as this goes, not that they can destroy a reference to a closure or alter the scope chain.

define([
    'backbone',
    'backbone.vent',
    'app/utils/foo',
    'app/services/intent'
], function(Backbone, Vent, Foo) {
    'use strict';

    // Backbone, Vent, and Foo are defined here

    Vent.on('myevent', function(options) {
        // Backbone is defined here, but not Vent or Foo.
    });
});

How is this even possible?

And how can I fix it?

Alan H.
  • 16,219
  • 17
  • 80
  • 113
  • 1
    Relevant code might help diagnose the problem. – Dismissile Feb 20 '14 at 19:11
  • possible duplicate of [garbage collection with node.js](http://stackoverflow.com/questions/5326300/garbage-collection-with-node-js). See also [here](http://stackoverflow.com/q/5368048/1048572) and [there](http://stackoverflow.com/q/8665781/1048572) – Bergi Jul 19 '16 at 19:07

2 Answers2

30

I suspect that the function where you set the breakpoint contains a reference to Backbone, but not Vent or Foo.

Closures are somewhat expensive on the JS runtime. It requires the engine wrap up the object in such a way that it keeps internal references to those variables so they can be resolved correctly at the time the function is executed. So for performance reasons, Chrome (and I suspect most other engines as well) has a tendency to optimize away any closure variables that aren't actually used when the script is compiled. This can lead to some confusing things when debugging, but it's to be expected.

Consider the following example (Note x, y, and z are defined in the scope of the outer function, not in global scope):

window.onload = function() {
  var x = 1, y = 2, z = 3;
  (function() {
    debugger;
    x++;
  })();
}

Alternate JSFiddle demonstration

If you try to output x and y on the console when this script hits the debugger directive, this is what you'll see:

Console Output

And if you look at the Scope Variable panel you'll see:

Scope Variables

Why? because Chrome has determined that y and z are not used within the function so there's no need for the compiled code to keep a reference to the variable. If you add a reference to y within the script, then the compiler will keep a reference to it, and the name will no longer be undefined in the debugger.

p.s.w.g
  • 146,324
  • 30
  • 291
  • 331
  • I’m in Chrome 31 (corporate environment) and this is exactly what was happening. Thanks so much, @p.s.w.g – Alan H. Feb 20 '14 at 20:35
  • @Louis Sorry I should've been more clear. This happens if the variables are in function scope of an outer function. If they are in global scope, then yes, they will be defined within the inner function. – p.s.w.g Feb 20 '14 at 20:35
  • @p.s.w.g Yeah, with the code in the updated answer, I can reproduce it. – Louis Feb 20 '14 at 20:42
  • Here's a note, if your script contains an eval, chrome can't optimize your closure variables away – Ruan Mendes Aug 23 '14 at 23:05
  • Is this optimisation documented somewhere? – Skay Feb 08 '17 at 11:31
  • Related question - http://stackoverflow.com/questions/28388530/why-does-chrome-debugger-think-closed-local-variable-is-undefined – Skay Feb 08 '17 at 11:41
3

if you use eval() inside your closure Chrome will not use any of it's performance optimizations which means you can see the value of y

gdi2290
  • 926
  • 5
  • 7