Skip to content

Instantly share code, notes, and snippets.

@makenova
Created April 15, 2014 20:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save makenova/10771622 to your computer and use it in GitHub Desktop.
Save makenova/10771622 to your computer and use it in GitHub Desktop.
Notes from JSconf video on JS scope

A Little This, a Little _that: Understanding Scope in JavaScript

These are notes I made while watching this video by David Aragon from jQuery conf 2014. He was kind enough to share his slides here.
I have copied some of his examples form the slides to provide context for my notes, some of them have been slightly modified. Any errors, are likely my own. Caveat Lector.

Scope

JS uses functional scope not block scope like C or Scala, e.g. the if(){} 'block' is not its own scope

var episode_1 = "Winter Is Coming";
if (true) {
    // This will overwrite the first episode 1 because it is not in its own scope
    var episode_1 = "The Kingsroad";
}
console.log(episode_1);
// "The Kingsroad"

but a function(){} block is

var episode_1 = "Winter Is Coming";
(function() {
    var episode_2 = "The Kingsroad";
})();
console.log(episode_2);
// Error: episode_2 is not defined

Variables in a function can leak to the global scope if var is missing. This can be undesirable behavior because it can lead to naming collisions in the global object( window in the browser )

function foo () {
  // The following variable will be in the global scope
  bar = "this variable will be in the global scope"
}

console.log( bar );

// This is useful for making sure your variables are not leaking into the global scope.

Object.keys(window);

Context

Lexical scope vs Dynamic scope

Lexical scoping can be inferred visually such as

var a = {
  prop1 :'Dat scope!',
  prop8 : function () {
    console.log(this);
  }
}

a.prop8() // Object {prop1: "Dat scope!", prop8: function}

Context in javascript uses dynamic scoping.

"If a function is a sentence, the context is the subject of that sentence. It's who the function is about".
-- David Aragon

4 ways to set the value of 'this' in a function

  1. Call a method *on* an object
var user = {
   handle: "@davidmaragon",
   alertName: function() {
       alert(this.handle);
   }
};
user.alertName(); // @davidmaragon
  1. Call a function and pass this in, with .call() or .apply()
    apply is similar to call except it accepts arguments as an array.
var user = {
   name: "makenova",
   greet: function( name ) {
       console.log( 'Hello ' + name + ', my name is, ' + this.name );
   }
};
user.greet.call(user, 'andrew'); // Hello andrew, my name is, makenova
user.greet.apply(user, ['andrew']); // Hello andrew, my name is, makenova
  1. Use new to create a brand new function context
// This example assumes that the user object in the previous one is available
function User(handle) {
   this.handle = handle;
}
var andrew = new User("@AndrewWK");
// andrew === {handle: "AndrewWK"}
user.greet(andrew.handle); // Hello @AndrewWK, my name is, makenova
  1. .bind() methods exist ($.proxy, _.bind, native .bind())
var alertName = function() {
   alert(this.handle);
}
var andrew = {handle: "@AndrewWK"};
$.proxy(alertName, andrew)(); // alerts "@AndrewWK"

Performance of bind

A performance comparison of the vendor and native bind metod can be found at the following jsPerf
spoiler, avoid using bind.

Dealing With Callbacks Using _this and _that

Callbacks create a new closure and a new scope so you have to

$('button').on('click', function() {
    // without storing the scope of the onClick callback in a variable, it will
    // be unavailable in the setTimeout callback. Test it!!
    var _this = this;
    setTimeout(function() {
        $(_this).addClass('clicked'); // All better
    }, 1000);
});

The Future of JS Scope: let and arrow functions =>

let gives a variable block scope.

// EcmaScript 6
if (true) {
    let local_var = "I'm local to the if block!";
}
console.log(local_var); // Error: undefined local_var

Arrow functions pass the parent context to the child, that will allow you to convert this:

$('button').click(function() {
    var _this = this;
    setTimeout(function() {
        // lookup _this from parent scope
        $(_this).addClass('clicked');
    }, 1000);
});

to this. Notice that it is unnecessary to write out function is also gone

$('button').click(function() {
    setTimeout(() => {
        $(this).addClass('clicked');
    }, 1000);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment