What You Need To Know About JavaScript Scope

Advertisement

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

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

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

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

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

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

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

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

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

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

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

(al)

Footnotes

  1. 1 http://almaer.com/blog/dealing-with-javascript-scope-issues-the-tale-of-alex-kindly-indulging-me
  2. 2 http://rule52.com/2008/06/yui-event-listeners/
  3. 3 http://www.geekdaily.net/2008/06/03/understanding-scope-in-object-oriented-javascript/
  4. 4 http://www.jibbering.com/faq/faq_notes/closures.html
  5. 5 http://happygiraffe.net/blog/2008/08/27/javascript-scope/

↑ Back to topShare on Twitter

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.

Advertising

Note: Our rating-system has caused errors, so it's disabled at the moment. It will be back the moment the problem has been resolved. We're very sorry. Happy Holidays!

  1. 1

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

  2. 2

    MIssing jQuery, how sad :(

  3. 3

    jquery? hello?

  4. 4

    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.

  5. 5

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

  6. 6

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

  7. 7
  8. 8
  9. 9
  10. 10
  11. 11

    The teaser picture contains PHP – why? :)

  12. 12

    Jônatan Fróes

    August 1, 2009 5:09 am

    lol

  13. 13

    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…

  14. 14

    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 :>

  15. 15

    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.

  16. 16

    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)

  17. 17

    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.

  18. 18

    Very nice roundup!

    Thanks..

  19. 19

    Is this a spam?

  20. 20

    FAIL.

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

    Joepie! De comments in dat plaatje zijn wel Nederlands!

  21. 21

    lol at Radeksonic
    Je hebt gelijk man :P

  22. 22

    Very nice article! I Work with Jquery a lot instead of prototype or mootools..

  23. 23

    Nice article, I work in JS quite a bit and I’ve been meaning to learn some of these scope concepts better (which this has helped considerably). I have one project in particular that needs a major refactor and really should incorporate some of these concepts.

    As for the critical comments here, if you just have a marginal understanding up jQuery then this is probably going to be over your head. I feel like we’re entering the era of ultra crappy jQuery copy/paste code because no one is learning straight JS. Do John Resig proud and understand what you are coding!

  24. 24

    Thank you so much for this article. Sometimes it’s great to brush up on quirks of JS. There are situations where I can’t depend on Libraries and I need to remember how to JS from scratch.

  25. 25

    Nice article. JavaScript is one of those languages where different concepts can overlap. In my opinion the module pattern is a better example of encapsulation and how to create variables or functions that can run in private scopes. I believe the article was trying to cover basic function level scopes and how it behaves with event handling. If you could follow up with an article covering inheritance in Javascript or even just understanding object prototypes that would be nice.

    Also your bindContext function has an error in it. Take a look at your otherArgs array.


    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]);
    }

    If your arguments array is greater then two items you are in trouble. The code snippet below shows why.


    // 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);
    }

    Since you call .reverse() on the otherArgs array you screw up the order of arguments passed into the function. So an example call:


    foo.bindContext(bar, someObj, number)

    Would really be accessed in the function like:


    foo = function(e, number, someObj) {
    var that = this; //'this' should equal bar
    }

    My suggestion would be to traverse the arguments array backwards using a reverse for loop. That way when you append the event object your reverse function call would still preserve the arguments in the proper order. I don’t mean to bust your code chops but I really couldn’t let this one slide.

  26. 26

    You don’t pay to read this stuff. If you don’t like it, don’t read it. Yawn.

  27. 27

    No, it’s not a spam. Just people from Belgium / Netherlands that noticed the Dutch language in the comments on the image of the code.
    Het viel me idd ook meteen op….

  28. 28

    I am not sure a cogent argument against the article is the fact that the picture in the intro is PHP code, that is what you call a Strawman; it means nothing when you knock it down, but I guess it might make you feel better about writing the content off?

    Oh btw what what kind of post is this:

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

    Nice!!! I love sheeple! Oh laugh, so now that Microsoft packs VS with jQuery now, did you all immediately make IE8 your primary browser as well, I mean it is used by sooo many people right? Keep Koolaid drink’in folks…

  29. 29

    I would say that the first part where the writer introduced scope and closures are nicely written. However the second part where call and apply was introduced the writer didn’t go into details of how actually call and apply works. After reading this post I got a better understanding on closure and scoping but I still didn’t get how should I work with call and apply. Instead of introducing Prototype bind and leave the most used library jQuery discussed. I think the writer could simply write the part of call and apply in the pure JavaScript way. It is post about JavaScript library or about JavaScript itself?

  30. 30

    An article on scope that did not even mention the word ‘closure’ ??

    Incidentally: “If you specify an event handler inline in HTML, then you end up with it referencing the global window object.”
    Are you sure about that? It doesn’t sound at all right to me – I am sure I have come across loads of inline event handlers where ‘this’ pointed to the correct HTML element. Perhaps you are talking about some other special situation?

  31. 31

    I am sure I have come across loads of inline event handlers where ‘this’ pointed to the correct HTML element. Perhaps you are talking about some other special situation?

    You have most likely seen inline event handlers where ‘this’ is passed to the function in question and then used as a parameter in place of the ‘this’ keyword.
    A fairly clear explanation of the ‘this’ keyword (and by extension ‘scope’) can be found at PPK’s quirksmode site here.

    The important thing to remember about javascript is that, by itself, at it’s most basic, the language doesn’t actually do anything – you first need to create objects with properties and methods for the language to be able to be of use. In a browser context that is our most common experience with javascript that basic, lowest object we usually encounter is the window object. Once you comprehend that everything attaches itself to ‘window’ by default you can start moving up the chain to different properties and methods of window to alter your current scope.

  32. 32

    There’s no jQuery sample because jQuery doesn’t really have built-in stuff for handling scope binding in the same way as the other libraries which are included. See:

    http://www.codecouch.com/2008/12/replacement-for-bind-or-bindaseventlistener-in-jquery/
    http://groups.google.com/group/prototype-scriptaculous/browse_thread/thread/c88f3d46c09375f0

    @Andrew Velis – thanks for the code correction, let me see if I can arrange an edit.

  33. 33

    Though it doesn’t mention the module pattern, here is another write up focusing specifically on understanding what “this” is in JavaScript.

    Also, I’m surprised this article didn’t mention Doug Crockford’s excellent “JavaScript: The Good Parts”, which addresses the four types of function invocation specifically.

    JS context is complicated and difficult to communicate a proper understanding, so I commend the effort. The more literature on this the better, imo.

    One correction:

    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.

    This is not true. [button onclick="this.value = 'foo'; doSomething();"]

    Browsers essentially call button.onclick = new Function(The inline string assigned to the onclick attribute). That means in the string, ‘this’ refers to the button, but doSomething() executes from the global context, as do all functions that are not executed via new, call/apply, or from an object (ala myApp.doSomething()).

  34. 34

    Hey! how about jQuery???

  35. 35

    For those who are interested to see the most used javascript frameworks:
    http://www.jcargoo.org/2009/08/17-most-widely-used-javascript.html

  36. 36
  37. 37

    This article did not advance my understansind of the javascript scope issues. As laid out here there are predictable global and local scopes, with the quirk being local functions or nested functions can make calls to the the document. How does this impact variable scope?

  38. 38

    In the following bit of code I believe you mean to write, “function showCount(number)”, great article!

    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

  39. 39

    ” Also, a variable defined within a function is accessible to its nested functions.”

    So, you’re saying that you can reference the var products in the click event handler callback. So then there’s no point in basically anything he went through?

  40. 40

    I believe this is the correct:

    for(var i = 0; i < this.length; i++)

  41. 41

    What is the difference between scope and execution context?

↑ Back to top