• 10 hours
  • Easy

Free online content available in this course.

course.header.alt.is_video

course.header.alt.is_certifying

Got it!

Last updated on 4/26/19

Understand Scope

Log in or subscribe for free to enjoy all this course has to offer!

How scopes work in Javascript

You can't understand Javascript without understanding how scopes work.

Scope can seem like a bit of an abstract concept, but it's fairly simple in practice. A scope is basically the area in which the name of a variable or function has a given meaning.

You know that in Javascript you can assign a value to a variable name and then reference that name to get that value back:

var foo = "bar";
console.log(foo) // --> bar

That's because you reference the variable in the same scope where you defined it. Variables and functions, then, are available for use in the scopes where they were defined.

There are two kinds of scopes in Javascript: 1) the global scope, and 2) local scope, aka the scope of each function in your program.

You can think of the global scope as just a giant scope that contains all the other scopes. When you aren't inside any function, you're in the global scope. In the code example above, the variable "foo" is defined on the global scope.

Now, since each function has its own scope, this means that a variable defined inside a function can only be read inside that function.

Example:

function myFunction() {
  var foo = "bar";
}
console.log(foo);
// > Uncaught ReferenceError: foo is not defined

The name foo in this case is created inside myFunction, so the rest of the program can't access it. It's private to that function.

Nesting

Scopes are nested in Javascript. This means that inside a function, you can call (and write to) all the variables and functions from the global scope, as well as those from any other function that your function happens to be nested *inside* of.

An analogy: Nested scopes are like the nested regional laws within a federalized country

If this all seems a bit abstract, it may be helpful to think of scopes in Javascript as analogous to the different national and regional laws within a federalized country.

Take the United States as an example: everyone in every state and county is subject to the federal laws of the nation as a whole. In this way, federal law is like the global scope.

When we look at the state level on the other hand, we see that the laws of each state only apply within that state. New Jersey has different laws about labor, taxation, and pumping gas, than Pennsylvania does. When we consider the laws of counties and cities, the "scope" of their laws becomes even more specific.

In Javascript, scopes work in a somewhat similar way, with functions replacing the states and counties, and variable and function names replacing the laws in the analogy. Let's look at an example to see how this works:

var equalProtection = "...nor shall any State deprive any person of life, liberty, or property, without due process of law; nor deny to any person within its jurisdiction the equal protection of the laws ";

function newJersey() {
  var noSelfServeGas = "Sorry, you can't pump your own gas.";

  function hunterdonCounty() {
    console.log(noSelfServeGas); // --> Sorry, you can't pump your own gas.
    console.log(equalProtection); // --> ...nor shall any State etc.
  }
}

function Pennsylvania() {
  console.log(noSelfServeGas); // --> Uncaught ReferenceError: noSelfServeGas is not defined
  console.log(equalProtection); // --> ...nor shall any State etc.

}

Here, in addition to the global scope, which represents national law, I've defined scopes for the states of New Jersey and Pennsylvania, and for two counties in New Jersey. I've also assigned two variables:

  • equalProtection is on the global scope and represents the equal protection clause of the 14th amendment to the US constitution, which applies as a law everywhere in the country.

  • noSelfServeGas  is on the scope of the newJersey function and represents New Jersey's law forbidding self-service gas stations, and like all state laws, only applies in the state where it was defined.

The point of this is to show you that its pretty intuitive to understand where variable and function names apply in your code. We see how, beyond its own scope, each function can also access everything in the scope where it was defined. This makes sense because, since your function is defined inside another function, all of the code in its body is also inside that other "parent" function. Thus, code can refer to anything that was defined in any of its "ancestor" scopes:

var foo = "bar";

function one() {
  var foo1 = "bar1";
  // you can reference 'foo' and 'foo1' here.

  function two() {
    var foo2 = "bar2";
    // you can reference 'foo', 'foo1', and 'foo2' here.

    function three() {
      var foo3 = "bar3";
      // you can reference 'foo', 'foo1', 'foo2', and 'foo3' here.
    }
  }
}

However, what happens when a function defines a variable local to itself, that has the same name as a variable that was defined on one of its parent scopes?

In laws, the nation's laws usually take precedence over the local laws of zones within the nation. In the United States, federal laws take precedence over state laws, state laws take precedence over city laws, and so on.

In Javascript, though, it's the reverse. Local names take precedence over names defined on parent scopes. For example, if the world were Javascript, a town in New Jersey could pass a law eliminating the equal protection clause of the 14th amendment of the US constitution within the town itself. New Jersey still wouldn't be able to modify the laws of a neighboring state, but it could overwrite or modify the laws of the "global scope", i.e. the whole country.

Fortunately, that's not the case in the real world.

Take the previous code example again: if we rewrote it to reuse the "foo" variable name, the value of "foo" would change depending on which scope you were accessing it from:

var foo = "bar";

function one() {
  var foo = "bar1";
  // this is in the scope of the function "one", foo here will be "bar1"

  function two() {
    var foo = "bar2";
    // this is in the scope of the function "two", foo here will be "bar2"

    function three() {
      var foo = "bar3";
      // this is in the scope of the function "three", foo here will be "bar3"
    }
  }
}

// this is in the global scope, foo here will be "bar"
The Global Object

Earlier, we said that the global scope was like a scope for all the code that exists outside of any function. It's as if Javascript were "making up" a scope for all the code that is outside of any function, and pretending that all that code was wrapped in one big function body.

The global scope in Javascript is special in another way as well, because it behaves like an object.

This object usually has a name, which varies depends on the context in which your javascript code is running. In the browser it is usually called "window".

Unlike functions, defining a variable on the global scope causes it to be available as a property of the global object:

var foo = "bar";

function baz() {
  console.log(foo);
  console.log(this.foo);
}

baz();
// > "bar"
// > "bar"

By contrast, defining a variable inside a function, does not mean that that variable will be available on that function's object:

function baz() {
  var foo = "bar";

  console.log(foo);
  console.log(this.foo);
}

baz();
// > "bar"
// > undefined

Furthermore, just as javascript assumes the global scope for code that isn't in another function, it assumes that this refers to the global object unless the function is bound to another object. That's why we can call this.foo on line 5 in the earlier example, and it's also the source of the bug that we encountered at the end of last chapter, when we tried to instantiate our pastry object without using thenew keyword, and instead ended up writing our pastry's attributes onto the global object.

This has some big implications for bugs in object-oriented programming.

Unfortunately, it means that if you write some code using the keyword this, and someone runs that code in a context other than the one it was meant for, this could suddenly mean the global window object instead of the object that the function's author intended.

This is exactly what happens when someone calls (from the global scope itself) a constructor function without using the new keyword. Remember that constructor functions called with thenew keyword create a new object, and this inside the constructor function will refer to that object. Constructor functions called without the new keyword, on the other hand, are just normal functions, so callingthis inside them will refer to whatever the meaning of this was in the scope in which the constructor function was defined (usually the global object).

In our Pastry example from last chapter, when we forgot to use the new keyword, all of the properties we wanted to assign to our new object got assigned to the global scope instead, and we ended up with this side-effect:

window.type
// > "beignet"

To see what this can lead to, try going to a web page (one that you don't mind unexpectedly navigating away from), and typing the following code into the developer tools console:

function Ache(intensity, location) {
  this.intensity = intensity;
  this.location = location;
}
var headache = Ache(1, 'head');

The problem is that the code is expecting to set ache.location but is in fact settingwindow.location , a special property that controls the browser's url address!

instanceof (and protecting constructor functions against use without new)

There's a handy operator in Javascript called instanceof. It can be used to tell you if an object was instantiated from a given constructor function:

function MyConstructor() {};

var construction = new MyConstructor();
construction instanceof MyConstructor;
// > true

var whoopsForgotToUseNew = MyConstructor();
whoopsForgotToUseNew instanceof MyConstructor;
// > false

Some programmers like to rescue code that forgets to use the new keyword. They do this by adding a check inside their constructor functions to see if the function is being called without the new keyword. This isn't required, but it's a useful technique to know and can help prevent bugs down the road when your object library is being used in unexpected ways.

If we rewrite our Pastry function to do this, it will use instanceof to make sure that this

is actually a Pastry object, and not, for example, the global window. If it isn't, we can assume that the new keyword has been unintentionally omitted, and we can even help the user in that case by calling the constructor function again (the right way this time) and returning that value from thePastry function:

function Pastry(type, flavor, levels, price, occasion) {
  if !( this instanceof Pastry ) {
    return new Pastry(type, flavor, levels, price, occasion);
  }

  // the rest of the function goes here... 
}

Example of certificate of achievement
Example of certificate of achievement