Menu Search
Jump to the content X X
Smashing Conf New York

We use ad-blockers as well, you know. We gotta keep those servers running though. Did you know that we publish useful books and run friendly conferences — crafted for pros like yourself? E.g. upcoming SmashingConf Barcelona, dedicated to smart front-end techniques and design patterns.

What You Need To Know About JavaScript Scope

Understanding scope in programming is key to appreciating how your variables interact with the rest of your code. In some languages, this can be quite straightforward, but JavaScript’s anonymous functions and event handling features, along with a couple of little quirks, mean that handling scope in your applications can become frustrating.

This article discusses how JavaScript handles scope and how various JavaScript libraries provide methods for dealing with it and how they smooth out a few bumps. We’ll also look at how you can get back to basics and do some interesting scope wrangling without a library, a useful approach if you’re writing code that needs to stand alone.

JavaScript Scope

You Are Here Link

So what is “scope”? We might say that it refers to your current location. If you run some JavaScript like…

var iAmGlobal = 5 * 5;

… then you’re running in the global scope, the big wide world, where you can’t go any further out. For something like…

function doSomething() {
  var inner = 5 * 5;
};

… you’re now boxed in by this function, running within its scope. The phrase “boxed in” is appropriate; take a look at this code:

var g = "global";
function go() { 
  var l = "local";
}
go();
alert(l); // throws a reference error

You’ll see that when we run the go function, the l variable is contained within that function’s scope. It cannot be accessed from a higher level scope.

How it Works Link

As well as variable scope, JavaScript uses the this keyword to get a reference to the current execution context. That rather terrifying term boils down this: at any point in your JavaScript code, you can ask “Help! Where am I?” and get back an object reference. This reference is for the current context, the object that “owns” the currently executing code.

Now, you might think, given what we’ve just learned about scope, the owner of the current code would be the scope in which it is executed. After all, in JavaScript, even functions are objects and can be passed around in variables. But no. Take this function, for instance:

function go() { console.debug(this); }
go();

This gives you a reference to the top-level execution context; in a browser, that’s the browser window itself.

There are a few exceptions to this. For example, if we create a JavaScript object and then call a method on it, then the scope is bound to the object:

var myObject = { 
  go: function() {
    console.debug(this);
  } 
};
myObject.go(); // console.debugs a reference to myObject

Similarly, when using functions as constructors, you see the same behavior:

function MyClass() {
  this.go = function() {
    console.debug(this);
  }
}

var instance1 = new MyClass();
var instance2 = new MyClass();

instance1.go(); // console.debugs a reference to the MyClass instance1
instance2.go(); // console.debugs a reference to the MyClass instance2

However, notice in this case that the reference is to the individual object instance rather than the class definition, which contrasts with the previous object literal example in which we will always receive a reference to the same object.

With event handlers, things get a little more confusing. If you specify an event handler inline in HTML, then you end up with it referencing the global window object. However, if you use JavaScript to wire your events, then you get a reference to the DOM object that raised it; for example, a click handler on a button would have the button element as the reference.

Event handlers are a common situation in which you would want to bind a function to a different scope; many JavaScript libraries provide features to help do just that. Let’s take a look at some common options.

Libraries Link

Many developers use JavaScript libraries to avoid having to deal with browser inconsistencies and to take advantage of the many shortcuts they offer. Scope handling is something most libraries give a helping hand with, so let’s take a look at what a few of the major players offer.

Prototype Link

Prototype comes with a bind method that allows a developer to specify the bound context for a function.

var products = ['Shoes', 'Sweater', 'Jeans', 'Wig'];

function showCount() {
  for(var i = 0; i < number; i++) {
    document.body.innerHTML += this[i] + '. ';
  }
}

var fn = showCount.bind(products);
fn(2); // outputs Shoes. Sweater. to the document

It also supports passing arguments that are “remembered” when you call the function, and these can be used to create shortcut functions; basically a version of a function that defaults to passing in certain arguments:

var showOne = showCount.bind(products, 1);
var showFour = showCount.bind(products, 4);
showOne(); // outputs Shoes.
showFour(); // output Shoes. Sweater. Jeans. Wig.

See Prototype’s Function.curry for more information on this particular aspect of Function.bind. The second useful feature of Prototype’s scope handling is bindAsEventListener. This is very similar to bind but ensures that the first argument passed to the event handler is the event object.

Event.observe(
  $('showCountButton'),
  'click',
  showCountHandler.bindAsEventListener(products, 2)
);

Here we’re using Prototype’s event functions to set up an event listener when the showCountButton is clicked. We’re passing our products array as the context, which the function is bound to, but in this case the showCountHandler would look something like this:

function showCountHandler(e, number) {
  for(var i = 0; i < number; i++) {
    document.body.innerHTML += this[i] + '. ';
  }
  Event.stop(e);
}

So we have the products array as this, but we also have the e event object automatically passed as the first parameter, which we can later use to stop the default event.

The two Prototype methods for binding context are handy because they’re used in exactly the same way, so you have a very simple and consistent method of taming your context.

Ext JS Link

Ext JS is farther reaching than either Prototype or MooTools in that it provides a full end-to-end framework for UI and application creation. This means it also provides correspondingly more features to control scope. To compare it with Prototype, let’s look at how to bind to a particular context:

var fn = showCount.createDelegate(products, 4);

This is identical in usage to Prototype’s bind method. But is there a difference when dealing with event handlers?

Ext.get('showCountButton').on('click', 
  showCountHandler.createDelegate(products, 4)
);

That’s right: there is no difference. Ext JS will normalize the event object into an Ext.EventObject for you and then append your additional arguments after that. However, there are two caveats to this. First, Ext doesn’t just pass the event object to the handler, but also passes the source of the event (in this case, the showCountButton) and any options that were passed to the on method. So, our handler now looks like this:

function showCountHandler(e, source, options, number) {}

However, there is a shortcut to using createDelegate, and it involves understanding the arguments of the on method. We can do this like so:

Ext.get('showCountButton').on('click', showCountHandler, products, { number: 4 });

The third argument of on is the scope under which the handler should run, which eliminates the need to use createDelegate. However, in order to pass further parameters, we have to use the options parameter. So our handler in this case would be:

function showCountHandler(e, source, options) {
  number = options.number;
}

This is not quite as elegant on the handler side of things, but it’s useful to know that Ext JS provides a variety of methods for accomplishing similar things, and you can use them accordingly when building your applications.

MooTools Link

The MooTools library provides two methods that are essentially like replacements for Prototype versions: bind and bindWithEvent, a.k.a. bindAsEventListener. However, on top of these familiar features, it provides a couple more that lend some extra flexibility. My favorite is Function.create:

var fn = showCount.create({
  bind: products,
  arguments: 4
});

This is nice and succinct, and to turn this into an event handler, we do this:

showCount.create({
  bind: products,
  arguments: 4,
  event: true
});

We can pass additional options, such as delay, which defers the execution of the function by a specified number of milliseconds, and periodical, which fires the function every time the specified interval elapses.

One library conspicuous in its absence is jQuery, which doesn’t offer any context binding facility. But JavaScript does have built-in features that allow you to manage context in many scenarios, and it also provides relatively simple methods of building your own solutions to more complicated problems.

On Your Own Link

I’m no snob: leveraging the hard work of the great developers who have spent a lot of time on their libraries makes total sense. They will have worked through all of the bugs and edge cases so that you don’t have to. On the other hand, understanding what’s happening on the JavaScript level is important, not only as an academic exercise but also for those occasions when you can’t rely on a library.

Sometimes offering standalone and library-independent scripts is best; for example, if you would like to make your code available publicly and for widespread use. By relying on a library, you restrict the use of the code to people who use that library.

Let’s take a look at how scope and context can be handled without using a library.

Call and Apply Link

JavaScript functions have two methods available to them that are of particular interest for handling context. Let’s look at call:

showCount.call(products, 4);

Apply is very similar but is used when you don’t know how many arguments you will be passing. It takes an array as its second parameter:

showCount.apply(products, [4]);

Both of these achieve the same goal, but your usage case will determine which would work best for you.

Event Handler Scope Link

We saw in the explanations of scope how event handlers cause problems, and we also saw how the various JavaScript libraries provide means of getting around this. If you’re stuck with bare-bones JavaScript, then you simply have to write your own means of scoping event handlers, and we’ll look at how to do that now.

Call and apply trigger the function immediately: that’s not what we’re after. Instead, we want to return a new function, which will then be called when the event fires. So:

Function.prototype.bindContext = function() {
  // when adding functions using prototype, "this" is the
  // object which the new function was called on 
  var callingFunction = this;

  // pass the desired scope object as the first arg
  var scope = arguments[0];

  // create a new arguments array with the first arg removed 
  var otherArgs = [];
  for(var i = 1; i < arguments.length; i++){ 
    otherArgs.push(arguments[i]);
  }

  // return a function remembering to include the event 
  return function(e) {
    // Add the event object to the arguments array
    otherArgs.push(e || window.event);
    // Array is in the wrong order so flip it
    otherArgs.reverse();
    
    // Now use apply to set scope and arguments
    callingFunction.apply(scope, otherArgs);
  }
}

This is a basic implementation with no error handling, but it provides a useful base to expand on and for understanding the overall approach. Dealing with event handler scope is essential for most JavaScript applications, and no developer should be tied to a single framework, so an appreciation for handling this problem at a low level is useful for every coder.

Conclusion Link

When building any large JavaScript application, a solid understanding of scope is not only useful but pretty much necessary. While using a common JavaScript library is a useful shortcut, it’s certainly never bad to get back to basics and roll your own solution in order to gain more control of JavaScript scope.

Further Resources Link

(al)

Footnotes Link

  1. 1 http://almaer.com/blog/dealing-with-javascript-scope-issues-the-tale-of-alex-kindly-indulging-me
  2. 2 http://www.jibbering.com/faq/faq_notes/closures.html
  3. 3 http://happygiraffe.net/blog/2008/08/27/javascript-scope/
SmashingConf New York

Hold on, Tiger! Thank you for reading the article. Did you know that we also publish printed books and run friendly conferences – crafted for pros like you? Like SmashingConf Barcelona, on October 25–26, with smart design patterns and front-end techniques.

↑ Back to top Tweet itShare on Facebook

Colin Ramsay is a partner at UK development firm Go Tripod Ltd. He is the co-author of Learning Ext JS and his blog contains articles on a wide range of subjects.

  1. 1

    :)

    -1
  2. 2

    :(

    -1
  3. 3
  4. 4

    !!

    -1
  5. 5

    Jônatan Fróes

    August 1, 2009 5:09 am

    lol

    -1
  6. 6

    Thomas Strobl

    August 1, 2009 6:24 am

    Well people who dont have a clue about JS will not understand this. People who know JS will already know this. Quite useless article hm? But thanks for the effort anyway.
    PS: I lol’d so hard on the first 4 comments. btw #5 is so right :>

    -6
  7. 7

    digitalgravy

    August 1, 2009 6:57 am

    Is there a reason why jQuery wasn’t included in the library list? :

    0
  8. 8

    MIssing jQuery, how sad :(

    -3
  9. 9

    Gabriel Silva

    August 1, 2009 7:34 am

    jquery? hello?

    -3
  10. 10

    Chris Heilmann

    August 1, 2009 7:51 am

    Nice start, but I actually don’t see why the libraries had to come in at all. This is not what you need to know about scope. I am missing a coverage of the module pattern for example. Especially using the Revealing Module Pattern: revealing module made my life much easier.

    7
  11. 11

    cancel bubble

    August 1, 2009 8:01 am

    I have to LOL about the image for this article being PHP code and not JavaScript.

    I was having a scope discussion not too long ago and the big things I pointed out were:

    JavaScript has function-level scope – a variable defined within a function is not accessible outside of the function, so vars within functions are “private”. Also, a variable defined within a function is accessible to its nested functions.

    JavaScript is lexically scoped – functions run in the scope they are defined in, not the scope they are executed in. When you combine this with the previous paragraph, you can protect variables by wrapping them in an anonymous function. This let’s you create private variables for classes.

    Function contexts can get tricky because when the browser invokes the callback handler, it switches the context of the function call to the DOM element receiving the event. The classic way to get around this is to create a closure and make a reference to the current object. Typically via something like var that = this so you’d end up with something along the lines of:

    var that = this;
    this.elem.onclick = function() {
    that.showMessage();
    }

    I always liked how Prototype did this with their various bind methods.

    1
  12. 12

    Who doesn’t use jQuery these days anyway ? :p

    -2
  13. 13

    The ones that know that JavaScript is a real language and an even better one than what 95% of its “programmers” think?

    2
  14. 14

    The teaser picture contains PHP – why? :)

    4
  15. 15

    I barely understood that on the first read. I think if you skip reading the section labelled Prototype the rest don’t make any sense…

    1
  16. 16

    The article did an exceptionally poor job at explaining what a scope is. And I agree with Chris Heilmann, I was surprised that the article talked about scope but did not feature the module pattern.

    2
  17. 17

    Keith Norman

    August 1, 2009 7:58 am

    jQuery wasn’t included because jQuery doesn’t have a way to bind a method to a scope built into the library. Here’s one way to do it for event handlers though – jQuery: Extending bind() to define context of execution (scope)

    0
  18. 18

    Very nice roundup!

    Thanks..

    0
  19. 19

    Is this a spam?

    -1
  20. 20

    FAIL.

    The picture in the very beginning is PHP. Not JavaScript.

    Joepie! De comments in dat plaatje zijn wel Nederlands!

    0

↑ Back to top