Last Updated: February 25, 2016
·
560
· lowerkey

Scope in JavaScript

What’s Scope?

Scope: What variables are accessible from a given point in the code.

Scope is one of those all important topics in Javascript. My hope is that after reading this document, you will know how and when to manipulate scope to your advantage, and where to place your variable definitions.

Let’s take a look at this example:

function this_land(){
  land = great;
  console.log(land);
  var land;
}

this_land();
> great

console.log(land);
> ReferenceError: land is not defined. 

First, we’re defining a function, called this_land. So far so good. But why are we using land before we declared it?

All variables (including functions) in Javascript are effectively moved to the top of the block they’re in. Usually, this feature stays out of the way, but it is useful to know that even if you define a variable at the bottom of a function, it will already be accessible by other elements in the same block.

If anyone did this, it would be confusing, and so we don’t. This is for demonstration purposes only.

But what about trying to access the variable “land” outside of the function? This is something we sometimes have to do, since we don’t want to go nuts passing everything into parameters every time. Especially with deeply nested functions it’s convenient to just use them.

We used var to define “land” and so it became private to the this_land function. Had we left out the var in front of land, we could have accessed land, after the function was executed.

How Scope is usually used

As a general rule of thumb, it’s ok to access the variables of an outer function, but not of an inner function.

function outer(){
  var some_var;
  var some_other_var;

  some_var = 1;
  some_other_var = 2;

  var inner = function(){
    return some_var + some_other_var;
  };

  return inner();
}

outer();

Oh yeah, functions can be assigned to a variable, or not, but that’s outside the scope of this example. Muahahaha.

You see now what is meant by outer and inner. So inner can access outer, but outer only gets the return value from inner, not access to its variables.

Juicy Tricks

Anonymous Functions

This bit of syntax

function(){
  var something = 0;
  return something;
}

is called an anonymous function because it doesn’t have a name. You may have seen something like this in setTimeout or setInterval. Eval is frowned upon for a variety of reasons, and because setTimeout make use of eval if you pass them a string, those forms of setTimeout and setInterval are frowned upon, too. Instead, we use regular functions, but for convenience leave out the name. jquery callbacks do this a lot, too:

$.ajax(‘someurl’, {
  done: function(data){
    derp derp
  }
});

Immediately Invoked Function Expressions

make use of anonymous functions and look like this:

(function(){
  // function body
})();

You have undoubtedly seen this:

(function($){
  // interact with the page
})(jQuery);

jQuery is defined somewhere else, probably in jquery.min.js and just in case other libraries use the $ variable, too, we specifically assign jQuery to $ for the scope of our function.

Note that we could have written

var do_something_using_jquery = function($){
  // interact with the page
};

(do_something_using_jquery)(jQuery);

We have a function, and by appending the parenthesis at the end, we immediately call it.

What’s this good for?

Preserving Scope

Let’s say we have a function that depends on an outer scope for some of its variables…
Also, we don’t know when this function is going to be called, but we have all the variables it needs available right now.
Problem: Outer variables might have changed by the time the inner function is called.

var fns = []; // an array of functions to be called later
for(var i=0; i<10; i++){
  var fn = (function(a){
    return function(){
      return a*2;
    };
  })(i);
}

// ... let some time pass

for(var i=0; i<fns.length; i++){
  var doubled = fns[i]()
}

This is a complicated example. I’ll exchange it for a real example when I find one in our code base.

Simulating Private and Public Members

var smells_like_a_proper_object = (function(){
  var private1 = "outer has no business changing this";
  return {
    public_access_to_private_member: function(){
      return private1; // private1 is in scope for the readPrivate1 function, which is public
    },
    another_public_function: function(arg){
      return private1 + arg;
    },
    a_public_member: 42
  }
})();

console.log(smells_like_a_proper_object.public_access_to_private_member()); // should work
console.log(smells_like_a_proper_object.private1); // should through an error.

Now we can access everything that is returned from the immediately invoked function expressen (IIFE), but nothing that is merely defined within it.
We need more work to get to the private variable, but we’re in control of what can leak outside. As long as we remember to var our variables, we don’t risk accidentally having outside pieces of code changing values we need.

And that’s pretty much it!