Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@kad3nce
Created February 26, 2014 14:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save kad3nce/9230693 to your computer and use it in GitHub Desktop.
Save kad3nce/9230693 to your computer and use it in GitHub Desktop.
<title>You-Dont-Know-JS/scope & closures/ch1.md at master · getify/You-Dont-Know-JS · GitHub</title>
  <meta content="@github" name="twitter:site" /><meta content="summary" name="twitter:card" /><meta content="getify/You-Dont-Know-JS" name="twitter:title" /><meta content="You-Dont-Know-JS - A JavaScript book series launched through Kickstarter" name="twitter:description" /><meta content="https://avatars.githubusercontent.com/u/150330" name="twitter:image:src" />

<meta name="hostname" content="github-fe128-cp1-prd.iad.github.net">
<meta name="ruby" content="ruby 2.1.0p0-github-tcmalloc (87c9373a41) [x86_64-linux]">
<link rel="assets" href="https://github.global.ssl.fastly.net/">
<link rel="conduit-xhr" href="https://ghconduit.com:25035/">
<link rel="xhr-socket" href="/_sockets" />


<meta name="msapplication-TileImage" content="/windows-tile.png" />
<meta name="msapplication-TileColor" content="#ffffff" />
<meta name="selected-link" value="repo_source" data-pjax-transient />
<meta content="collector.githubapp.com" name="octolytics-host" /><meta content="collector-cdn.github.com" name="octolytics-script-host" /><meta content="github" name="octolytics-app-id" /><meta content="32105C85:1015:ACB935:530DFE82" name="octolytics-dimension-request_id" />




<link rel="icon" type="image/x-icon" href="/favicon.ico" />

<meta content="authenticity_token" name="csrf-param" />
<link href="https://github.global.ssl.fastly.net/assets/github-99f0c420247f036251131234f2df1eaaf2ddb386.css" media="all" rel="stylesheet" type="text/css" />
<link href="https://github.global.ssl.fastly.net/assets/github2-c1671115cdd7e4b44317ecd428238f073c37dbc0.css" media="all" rel="stylesheet" type="text/css" />




  <script crossorigin="anonymous" src="https://github.global.ssl.fastly.net/assets/frameworks-01ab94ef47d6293597922a1fab60e274e1d8f37e.js" type="text/javascript"></script>
  <script async="async" crossorigin="anonymous" src="https://github.global.ssl.fastly.net/assets/github-4080dfd1919155cf72a9b56c3bf746e67b898aa6.js" type="text/javascript"></script>
  
  <meta http-equiv="x-pjax-version" content="285f2c751e45e1751b37093c33d9affd">

    <link data-pjax-transient rel='permalink' href='/getify/You-Dont-Know-JS/blob/fc55fef463231eefa632e185ceb197061c9eda4e/scope%20%26%20closures/ch1.md'>

  <div class="header header-logged-out">
<a class="header-logo-wordmark" href="https://github.com/">
  <span class="mega-octicon octicon-logo-github"></span>
</a>

<div class="header-actions">
    <a class="button primary" href="/join">Sign up</a>
  <a class="button signin" href="/login?return_to=%2Fgetify%2FYou-Dont-Know-JS%2Fblob%2Fmaster%2Fscope%2520%26%2520closures%2Fch1.md">Sign in</a>
</div>

<div class="command-bar js-command-bar  in-repository">

  <ul class="top-nav">
      <li class="explore"><a href="/explore">Explore</a></li>
    <li class="features"><a href="/features">Features</a></li>
      <li class="enterprise"><a href="https://enterprise.github.com/">Enterprise</a></li>
      <li class="blog"><a href="/blog">Blog</a></li>
  </ul>
    <form accept-charset="UTF-8" action="/search" class="command-bar-form" id="top_search_form" method="get">

<input type="text" data-hotkey="/ s" name="q" id="js-command-bar-field" placeholder="Search or type a command" tabindex="1" autocapitalize="off"

  data-repo="getify/You-Dont-Know-JS"
  data-branch="master"
  data-sha="ab9358f636dd64a7c6987bd47667cb798594a8a1"
<input type="hidden" name="nwo" value="getify/You-Dont-Know-JS" />

<div class="select-menu js-menu-container js-select-menu search-context-select-menu">
  <span class="minibutton select-menu-button js-menu-target" role="button" aria-haspopup="true">
    <span class="js-select-button">This repository</span>
  </span>

  <div class="select-menu-modal-holder js-menu-content js-navigation-container" aria-hidden="true">
    <div class="select-menu-modal">

      <div class="select-menu-item js-navigation-item js-this-repository-navigation-item selected">
        <span class="select-menu-item-icon octicon octicon-check"></span>
        <input type="radio" class="js-search-this-repository" name="search_target" value="repository" checked="checked" />
        <div class="select-menu-item-text js-select-button-text">This repository</div>
      </div> <!-- /.select-menu-item -->

      <div class="select-menu-item js-navigation-item js-all-repositories-navigation-item">
        <span class="select-menu-item-icon octicon octicon-check"></span>
        <input type="radio" name="search_target" value="global" />
        <div class="select-menu-item-text js-select-button-text">All repositories</div>
      </div> <!-- /.select-menu-item -->

    </div>
  </div>
</div>
      <div class="site" itemscope itemtype="http://schema.org/WebPage">

<div class="pagehead repohead instapaper_ignore readability-menu">
  <div class="container">
  • Star
    <a class="social-count js-social-count" href="/getify/You-Dont-Know-JS/stargazers">
      3,529
    </a>
    
  • <li>
      <a href="/login?return_to=%2Fgetify%2FYou-Dont-Know-JS"
        class="minibutton with-count js-toggler-target fork-button tooltipped tooltipped-n"
        aria-label="You must be signed in to fork a repository" rel="nofollow">
        <span class="octicon octicon-git-branch"></span>Fork
      </a>
      <a href="/getify/You-Dont-Know-JS/network" class="social-count">
        180
      </a>
    </li>
    
    <h1 itemscope itemtype="http://data-vocabulary.org/Breadcrumb" class="entry-title public">
      <span class="repo-label"><span>public</span></span>
      <span class="mega-octicon octicon-repo"></span>
      <span class="author">
        <a href="/getify" class="url fn" itemprop="url" rel="author"><span itemprop="title">getify</span></a>
      </span>
      <span class="repohead-name-divider">/</span>
      <strong><a href="/getify/You-Dont-Know-JS" class="js-current-repository js-repo-home-link">You-Dont-Know-JS</a></strong>

      <span class="page-context-loader">
        <img alt="Octocat-spinner-32" height="16" src="https://github.global.ssl.fastly.net/images/spinners/octocat-spinner-32.gif" width="16" />
      </span>

    </h1>
  </div><!-- /.container -->
</div><!-- /.repohead -->

<div class="container">
  <div class="repository-with-sidebar repo-container new-discussion-timeline js-new-discussion-timeline  ">
    <div class="repository-sidebar clearfix">
  • Code Octocat-spinner-32
  •     <li class="tooltipped tooltipped-w" aria-label="Issues">
          <a href="/getify/You-Dont-Know-JS/issues" aria-label="Issues" class="js-selected-navigation-item sunken-menu-item js-disable-pjax" data-gotokey="i" data-selected-links="repo_issues /getify/You-Dont-Know-JS/issues">
            <span class="octicon octicon-issue-opened"></span> <span class="full-word">Issues</span>
            <span class='counter'>28</span>
            <img alt="Octocat-spinner-32" class="mini-loader" height="16" src="https://github.global.ssl.fastly.net/images/spinners/octocat-spinner-32.gif" width="16" />
    

      <li class="tooltipped tooltipped-w" aria-label="Pull Requests">
        <a href="/getify/You-Dont-Know-JS/pulls" aria-label="Pull Requests" class="js-selected-navigation-item sunken-menu-item js-disable-pjax" data-gotokey="p" data-selected-links="repo_pulls /getify/You-Dont-Know-JS/pulls">
            <span class="octicon octicon-git-pull-request"></span> <span class="full-word">Pull Requests</span>
            <span class='counter'>5</span>
            <img alt="Octocat-spinner-32" class="mini-loader" height="16" src="https://github.global.ssl.fastly.net/images/spinners/octocat-spinner-32.gif" width="16" />
    

    </ul>
    <div class="sunken-menu-separator"></div>
    <ul class="sunken-menu-group">
    
      <li class="tooltipped tooltipped-w" aria-label="Pulse">
        <a href="/getify/You-Dont-Know-JS/pulse" aria-label="Pulse" class="js-selected-navigation-item sunken-menu-item" data-pjax="true" data-selected-links="pulse /getify/You-Dont-Know-JS/pulse">
          <span class="octicon octicon-pulse"></span> <span class="full-word">Pulse</span>
          <img alt="Octocat-spinner-32" class="mini-loader" height="16" src="https://github.global.ssl.fastly.net/images/spinners/octocat-spinner-32.gif" width="16" />
    

      <li class="tooltipped tooltipped-w" aria-label="Graphs">
        <a href="/getify/You-Dont-Know-JS/graphs" aria-label="Graphs" class="js-selected-navigation-item sunken-menu-item" data-pjax="true" data-selected-links="repo_graphs repo_contributors /getify/You-Dont-Know-JS/graphs">
          <span class="octicon octicon-graph"></span> <span class="full-word">Graphs</span>
          <img alt="Octocat-spinner-32" class="mini-loader" height="16" src="https://github.global.ssl.fastly.net/images/spinners/octocat-spinner-32.gif" width="16" />
    

      <li class="tooltipped tooltipped-w" aria-label="Network">
        <a href="/getify/You-Dont-Know-JS/network" aria-label="Network" class="js-selected-navigation-item sunken-menu-item js-disable-pjax" data-selected-links="repo_network /getify/You-Dont-Know-JS/network">
          <span class="octicon octicon-git-branch"></span> <span class="full-word">Network</span>
          <img alt="Octocat-spinner-32" class="mini-loader" height="16" src="https://github.global.ssl.fastly.net/images/spinners/octocat-spinner-32.gif" width="16" />
    

          <div class="only-with-full-nav">

HTTPS clone URL

<span aria-label="copy to clipboard" class="js-zeroclipboard url-box-clippy minibutton zeroclipboard-button" data-clipboard-text="https://github.com/getify/You-Dont-Know-JS.git" data-copied-hint="copied!"><span class="octicon octicon-clippy"></span></span>

Subversion checkout URL

<span aria-label="copy to clipboard" class="js-zeroclipboard url-box-clippy minibutton zeroclipboard-button" data-clipboard-text="https://github.com/getify/You-Dont-Know-JS" data-copied-hint="copied!"><span class="octicon octicon-clippy"></span></span>

You can clone with HTTPS or Subversion.

            <a href="/getify/You-Dont-Know-JS/archive/master.zip"
               class="minibutton sidebar-button"
               title="Download this repository as a zip file"
               rel="nofollow">
              <span class="octicon octicon-cloud-download"></span>
              Download ZIP
            </a>
          </div>
    </div><!-- /.repository-sidebar -->

    <div id="js-repo-pjax-container" class="repository-content context-loader-container" data-pjax-container>

Show File Finder

branch: master
<div class="select-menu-modal">
  <div class="select-menu-header">
    <span class="select-menu-title">Switch branches/tags</span>
    <span class="octicon octicon-remove-close js-menu-close"></span>
  </div> <!-- /.select-menu-header -->

  <div class="select-menu-filters">
    <div class="select-menu-text-filter">
      <input type="text" aria-label="Filter branches/tags" id="context-commitish-filter-field" class="js-filterable-field js-navigation-enable" placeholder="Filter branches/tags">
    </div>
    <div class="select-menu-tabs">
      <ul>
        <li class="select-menu-tab">
          <a href="#" data-tab-filter="branches" class="js-select-menu-tab">Branches</a>
        </li>
        <li class="select-menu-tab">
          <a href="#" data-tab-filter="tags" class="js-select-menu-tab">Tags</a>
        </li>
      </ul>
    </div><!-- /.select-menu-tabs -->
  </div><!-- /.select-menu-filters -->

  <div class="select-menu-list select-menu-tab-bucket js-select-menu-tab-bucket" data-tab-filter="branches">

    <div data-filterable-for="context-commitish-filter-field" data-filterable-type="substring">


        <div class="select-menu-item js-navigation-item selected">
          <span class="select-menu-item-icon octicon octicon-check"></span>
          <a href="/getify/You-Dont-Know-JS/blob/master/scope%20&amp;%20closures/ch1.md"
             data-name="master"
             data-skip-pjax="true"
             rel="nofollow"
             class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target"
             title="master">master</a>
        </div> <!-- /.select-menu-item -->
    </div>

      <div class="select-menu-no-results">Nothing to show</div>
  </div> <!-- /.select-menu-list -->

  <div class="select-menu-list select-menu-tab-bucket js-select-menu-tab-bucket" data-tab-filter="tags">
    <div data-filterable-for="context-commitish-filter-field" data-filterable-type="substring">


    </div>

    <div class="select-menu-no-results">Nothing to show</div>
  </div> <!-- /.select-menu-list -->

</div> <!-- /.select-menu-modal -->
Kyle Simpson getify February 09, 2014
<div class="participation">
  <p class="quickstat"><a href="#blob_contributors_box" rel="facebox"><strong>2</strong> contributors</a></p>
      <a class="avatar tooltipped tooltipped-s" aria-label="getify" href="/getify/You-Dont-Know-JS/commits/master/scope%20%26%20closures/ch1.md?author=getify"><img alt="Kyle Simpson" class=" js-avatar" data-user="150330" height="20" src="https://1.gravatar.com/avatar/35761e3936deba2f8189c2d20982c771?d=https%3A%2F%2Fidenticons.github.com%2Fa89f254b3e61462748b48c4ea3480e2b.png&amp;r=x&amp;s=140" width="20" /></a>
<a class="avatar tooltipped tooltipped-s" aria-label="varun06" href="/getify/You-Dont-Know-JS/commits/master/scope%20%26%20closures/ch1.md?author=varun06"><img alt="Varun" class=" js-avatar" data-user="1284070" height="20" src="https://1.gravatar.com/avatar/6f0ab0bf648041c211675b2bd06313c6?d=https%3A%2F%2Fidenticons.github.com%2F0b334b4aa75bde6749f1a173c2cc5d8d.png&amp;r=x&amp;s=140" width="20" /></a>


</div>
<div id="blob_contributors_box" style="display:none">
  <h2 class="facebox-header">Users who have contributed to this file</h2>
  <ul class="facebox-user-list">
      <li class="facebox-user-list-item">
        <img alt="Kyle Simpson" class=" js-avatar" data-user="150330" height="24" src="https://1.gravatar.com/avatar/35761e3936deba2f8189c2d20982c771?d=https%3A%2F%2Fidenticons.github.com%2Fa89f254b3e61462748b48c4ea3480e2b.png&amp;r=x&amp;s=140" width="24" />
        <a href="/getify">getify</a>
      </li>
      <li class="facebox-user-list-item">
        <img alt="Varun" class=" js-avatar" data-user="1284070" height="24" src="https://1.gravatar.com/avatar/6f0ab0bf648041c211675b2bd06313c6?d=https%3A%2F%2Fidenticons.github.com%2F0b334b4aa75bde6749f1a173c2cc5d8d.png&amp;r=x&amp;s=140" width="24" />
        <a href="/varun06">varun06</a>
      </li>
  </ul>
</div>
file 313 lines (172 sloc) 19.817 kb

You Don't Know JS: Scope & Closures

Chapter 1: What is Scope?

One of the most fundamental paradigms of nearly all programming languages is the ability to store values in variables, and later retrieve or modify those values. In fact, the ability to store values and pull values out of variables is what gives a program state.

Without such a concept, a program could perform some tasks, but they would be extremely limited and not terribly interesting.

But the inclusion of variables into our program begets the most interesting questions we will now address: where do those variables live? In other words, where are they stored? And, most importantly, how does our program find them when it needs them?

These questions speak to the need for a well-defined set of rules for storing variables in some location, and for finding those variables at a later time. We'll call that set of rules: Scope.

But, where and how do these Scope rules get set?

Compiler Theory

It may be self-evident, or it may be surprising, depending on your level of interaction with various languages, but despite the fact that JavaScript falls under the general category of "dynamic" or "interpreted" languages, it is in fact a compiled language. It is not compiled well in advance, as are many traditionally-compiled languages, nor are the results of compilation portable among various distributed systems.

But, nevertheless, the JavaScript engine performs many of the same steps, albeit in more sophisticated ways than we may commonly be aware, of any traditional language-compiler.

In traditional compiled-language process, a chunk of source code, your program, will undergo typically three steps before it is executed, roughly called "compilation":

  1. Tokenizing/Lexing: breaking up a string of characters into meaningful (to the language) chunks, called tokens. For instance, consider the program: var a = 2;. This program would likely be broken up into the following tokens: var, a, =, 2, and ;. Whitespace may or may not be persisted as a token, depending on whether its meaningful or not.

    Note: The difference between tokenizing and lexing is subtle and academic, but it centers on whether or not these tokens are identified in a stateless or stateful way. Put simply, if the tokenizer were to invoke stateful parsing rules to figure out whether a should be considered a distinct token or just part of another token, that would be lexing.

  2. Parsing: taking a stream (array) of tokens and turning it into a tree of nested elements, which collectively represent the grammatical structure of the program. This tree is called an "AST" (Abstract Syntax Tree).

    The tree for var a = 2; might start with a top-level node called VariableDeclaration, with a child node called Identifier (whose value is a), and another child called AssignmentExpression which itself has a child called NumericLiteral (whose value is 2).

  3. Code-Generation: the process of taking an AST and turning it into executable code. This part varies greatly depending on the language, the platform it's targeting, etc.

    So, rather than get mired in details, we'll just handwave and say that there's a way to take our above described AST for var a = 2; and turn it into a set of machine instructions to actually create a variable called a (including reserving memory, etc), and then store a value into a.

    Note: The details of how the engine manages system resources are deeper than we will dig, so we'll just take it for granted that the engine is able to create and store variables as needed.

The JavaScript engine is vastly more complex than just those three steps, as are most other language compilers. For instance, in the process of parsing and code-generation, there are certainly steps to optimize the performance of the execution, including collapsing redundant elements, etc.

So, I'm painting only with broad strokes here. But I think you'll see shortly why these details we do cover, even at a high level, are relevant.

For one thing, JavaScript engines don't get the luxury (like other language compilers) of having plenty of time to optimize, because JavaScript compilation doesn't happen in a build step ahead of time, as with other languages.

For JavaScript, the compilation that occurs happens, in many cases, mere microseconds (or less!) before the code is executed. To ensure the fastest performance, JS engines use all kinds of tricks (like JITs, which lazy compile and even hot re-compile, etc) which are well beyond the "scope" of our discussion here.

Let's just say, for simplicity sake, that any snippet of JavaScript has to be compiled before (usually right before!) it's executed. So, the JS compiler will take the program var a = 2; and compile it first, and then be ready to execute it, usually right away.

Understanding Scope

The way we will approach learning about scope is to think of the process in terms of a conversation. But, who is having the conversation?

The Cast

Let's meet the cast of characters that interact to process the program var a = 2;, so we understand their conversations that we'll listen in on shortly:

  1. Engine: responsible for start-to-finish compilation and execution of our JavaScript program.

  2. Compiler: one of Engine's friends; handles all the dirty work of parsing and code-generation (see previous section).

  3. Scope: another friend of Engine; collects and maintains a look-up list of all the declared identifiers (variables), and enforces a strict set of rules as to how these are accessible to currently executing code.

For you to fully understand how JavaScript works, you need to begin to think like Engine (and friends) think, ask the questions they ask, and answer those questions the same.

Back & Forth

When you see the program var a = 2;, you most likely think of that as one statement. But that's not how our new friend Engine sees it. In fact, Engine sees two distinct statements, one which Compiler will handle during compilation, and one which Engine will handle during execution.

So, let's break down how Engine and friends will approach the program var a = 2;.

The first thing Compiler will do with this program is perform lexing to break it down into tokens, which it will then parse into a tree. But when Compiler gets to code-generation, it will treat this program somewhat differently than perhaps assumed.

A reasonable assumption would be that Compiler will produce code that could be summed up by this pseudo-code: "Allocate memory for a variable, label it a, then stick the value 2 into that variable." Unfortunately, that's not quite accurate.

Compiler will instead proceed as:

  1. Encountering var a, Compiler asks Scope to see if a variable a already exists for that particular scope collection. If so, Compiler ignores this declaration and moves on. Otherwise, Compiler asks Scope to declare a new variable called a for that scope collection.

  2. Compiler then produces code for Engine to later execute, to handle the a = 2 assignment. The code Engine runs will first ask Scope if there is a variable called a accessible in the current scope collection. If so, Engine uses that variable. If not, Engine looks elsewhere (see nested Scope section below).

If Engine eventually finds a variable, it assigns the value 2 to it. If not, Engine will raise its hand and yell out an error!

To summaraize: two distinct actions are taken for a variable assignment: First, Compiler declares a variable (if not previously declared), and second, when executing, Engine looks up the variable in Scope and assigns to it, if found.

Compiler Speak

We need a little bit more compiler terminology to proceed further with understanding.

When Engine executes the code that Compiler produced for step (2), it has to look-up the variable a to see if it has been declared, and this look-up is consulting Scope. But the type of look-up Engine performs affects the outcome of the look-up.

In our case, it is said that Engine would be performing an "LHS" look-up for the variable a. The other type of look-up is called "RHS".

I bet you can guess what the "L" and "R" mean. These terms stand for "Left-hand Side" and "Right-hand Side".

Side... of what? Of an assignment operation.

In other words, an LHS look-up is done when a variable appears on the left-hand side of an assignment operation, and an RHS look-up is done when a variable appears on the right-hand side of an assignment operation.

Actually, let's be a little more precise. An RHS look-up is indistiguishable, for our purposes, from simply a look-up of the value of some variable, whereas the LHS look-up is trying to find the variable container itself, so that it can assign. In this way, RHS doesn't really mean "right-hand side of an assignment" per se, it just, more accurately, means "not left-hand side".

Being slightly glib for a moment, you could also say "RHS" instead means "retrieve his/her source (value)", implying that RHS means "go get the value of...".

Let's dig into that deeper.

When I say:

console.log( a );

The reference to a is an RHS reference, because nothing is being assigned to a here. Instead, we're looking-up to retrieve the value of a, so that the value can be passed to console.log(..).

By contrast:

a = 2;

The reference to a here is an LHS reference, because we don't actually care what the current value is, we simply want to find the variable as a target for the = 2 assignment operation.

Note: LHS and RHS meaning "left/right-hand side of an assigment" doesn't necessarily literally mean "left/right side of the = assignment operator". There are several other ways that assignments happen, and so it's better to conceptually think about it as: "who's the target of the assignment (LHS)" and "who's the source of the assignment (RHS)".

Consider this program, which has both LHS and RHS references:

function foo(a) {
    console.log( a ); // 2
}

foo( 2 );

The line that invokes foo(..) as a function call requires an RHS reference to foo, meaning, "go look-up the value of foo, and give it to me." Moreover, (..) means the value of foo should be executed, so it'd better actually be a function!

There's a subtle but important assignment here. Did you spot it?

You may have missed the implied a = 2 in this code snippet. It happens when the value 2 is passed as an argument to the foo(..) function, in which case the 2 value is assigned to the parameter a. To (implicitly) assign to parameter a, an LHS look-up is performed.

There's also an RHS reference for the value of a, and that resulting value is passed to console.log(..). console.log(..) needs a reference to execute. It's an RHS look-up for the console object, then a property-resolution occurs to see if it has a method called log.

Finally, we can conceptualize that there's an LHS/RHS exchange of passing the value 2 (by way of variable a's RHS look-up) into log(..). Inside of the native implementation of log(..), we can assume it has parameters, the first of which (perhaps called arg1) has an LHS reference look-up, before assigning 2 to it.

Note: You might be tempted to conceptualize the function declaration function foo(a) {... as a normal variable declaration and assignment, such as var foo and foo = function(a){.... In so doing, it would be tempting to think of this function declaration as involving an LHS look-up.

However, the subtle but important difference is that Compiler handles both the declaration and the value definition during code-generation, such that when Engine is executing code, there's no processing necessary to "assign" a function value to foo. Thus, it's not really appropriate to think of a function declaration as an LHS look-up assignment in the way we're discussing them here.

Engine/Scope Conversation

function foo(a) {
    console.log( a ); // 2
}

foo( 2 );

Let's imagine the above exchange (which processes this code snippet) as a conversation. The conversation would go a little something like this:

Engine: Hey Scope, I have an RHS reference for foo. Ever heard of him?

Scope: Why yes, I have. Compiler declared him just a second ago. He's a function. Here you go.

Engine: Great, thanks! OK, I'm executing foo.

Engine: Hey, Scope, I've got an LHS reference for a, ever heard of him?

Scope: Why yes, I have. Compiler declared him as a formal parameter to foo just recently. Here you go.

Engine: Helpful as always, Scope. Thanks again. Now, time to assign 2 to a.

Engine: Hey, Scope, sorry to bother you again. I need an RHS look-up for console. Ever heard of him?

Scope: No problem, Engine, this is what I do all day. Yes, I've got console. He's built-in. Here ya go.

Engine: Perfect. Looking up log(..). OK, great, it's a function.

Engine: Yo, Scope. Can you help me out with an RHS reference to a. I think I remember him, but just want to double-check.

Scope: You're right, Engine. Same guy, hasn't changed. Here ya go.

Engine: Cool. Passing the value of a, which is 2, into log(..).

...

Quiz

Check your understanding so far. Make sure to play the part of Engine and have a "conversation" with the Scope:

function foo(a) {
    var b = a;
    return a + b;
}

var c = foo( 2 );

  1. Identify all the LHS look-ups (there are 3!).

  2. Identify all the RHS look-ups (there are 4!).

Note: See the chapter review for the quiz answers!

Nested Scope

We said that Scope is a set of rules for looking up variables by their identifier name. There's usually more than one Scope to consider, however.

Just as a block or function is nested inside another block or function, scopes are nested inside other scopes. So, if a variable cannot be found in the immediate scope, Engine looks consults the next containing scope, continuning until found or until the outermost (aka, global) scope has been reached.

Consider:

function foo(a) {
    console.log( a + b );
}

var b = 2;

foo( 2 ); // 4

The RHS reference for b cannot be resolved inside the function foo, but it can be resolved in the Scope surrounding it (in this case, the global).

So, revisiting the conversations between Engine and Scope, we'd overhear:

Engine: "Hey, Scope of foo, ever heard of b? Got an RHS reference for him."

Scope: "Nope, never heard of him. Go fish."

Engine: "Hey, Scope outside of foo, oh you're the global Scope, ok cool. Ever heard of b? Got an RHS reference for him."

Scope: "Yep, sure have. Here ya go."

The simple rules for traversing nested Scope: Engine starts at the currently executing Scope, looks for the variable there, then if not found, keeps going up one level, and so on. If the outermost global scope is reached, the search stops, whether it finds the variable or not.

Building on Metaphors

To visualize the process of nested Scope resolution, I want you to think of this tall building.

The building represents our program's nested Scope rule set. The first floor of the building represents your currently executing Scope, wherever you are. The top level of the building is the global Scope.

You resolve LHS and RHS references by looking on your current floor, and if you don't find it, taking the elevator to the next floor, looking there, then the next, and so on. Once you get to the top floor (the global Scope), you either find what you're looking for, or you don't. But you have to stop regardless.

Errors

Why does it matter whether we call it LHS or RHS?

Because these two types of look-ups behave differently in the circumstance where the variable has not yet been declared (is not found in any Scope).

Consider:

function foo(a) {
    console.log( a + b );
    b = a;
}

foo( 2 );

When the RHS look-up occurs for b the first time, it will not be found. This is said to be an "undeclared" variable, because it is not found in the scope.

If an RHS look-up fails to ever find a variable, anyhwere in the nested Scopes, this results in a ReferenceError being thrown by the Engine. It's important to note that the error is of the type ReferenceError.

By contrast, if the Engine is performing an LHS look-up, and it arrives at the top floor (global Scope) without finding it, if the program is not running in "Strict Mode" [^note-strictmode], then the global Scope will create a new variable of that name in the global scope, and hand it back to Engine.

"No, there wasn't one before, but I was helpful and created one for you."

"Strict Mode" [^note-strictmode], which was added in ES5, has a number of different behaviors from normal/relaxed/lazy mode. One such behavior is that it disallows the automatic/implicit global variable creation. In that case, there would be no global Scope'd variable to hand back from an LHS look-up, and Engine would throw a ReferenceError similarly to the RHS case.

Now, if a variable is found for an RHS look-up, but you try to do something with its value that is impossible, such as trying to execute-as-function a non-function value, or reference a property on a null or undefined value, then Engine throws a different kind of error, called a TypeError.

ReferenceError is Scope resolution-failure related, whereas TypeError implies that Scope resolution was successful, but that there was an illegal/impossible action attempted against the result.

Review (TL;DR)

So, what is scope?

Scope is the set of rules that determines where and how a variable (identifier) can be looked-up. This look-up may be for the purposes of assigning to the variable, which is an LHS (left-hand-side) reference, or it may be for the purposes of retrieving its value, which is an RHS (right-hand-side) reference.

LHS references result from assignment operations. Scope-related assignments can occur either with the = operator or by passing arguments to (assign to) function parameters.

The JavaScript Engine first compiles code before it executes, and in so doing, it splits up statements like var a = 2; into two separate steps:

  1. First, var a to declare it in that Scope. This is performed at the beginning, before code execution.

  2. Later, a = 2 to look up the variable (LHS reference) and assign to it if found.

Both LHS and RHS reference look-ups start at the currently executing Scope, and if need be (that is, they don't find what they're looking for there), they work their way up the nested Scope, one scope (floor) at a time, looking for the identifier, until they get to the global (top floor) and stop, and either find it, or don't.

Unfulfilled RHS references result in ReferenceErrors being thrown. Unfulfilled LHS references result in an automatic, implicitly-created global of that name (if not in "Strict Mode" [^note-strictmode]), or a ReferenceError (if in "Strict Mode" [^note-strictmode]).

Quiz Answers

function foo(a) {
    var b = a;
    return a + b;
}

var c = foo( 2 );

  1. Identify all the LHS look-ups (there are 3!).

    c = .., a = 2 (implicit param assignment) and b = ..

  2. Identify all the RHS look-ups (there are 4!).

    foo(2.., = a;, a + .. and .. + b

[^note-strictmode]: MDN: Strict Mode

Jump to Line

Go
    </div>

  </div><!-- /.repo-container -->
  <div class="modal-backdrop"></div>
</div><!-- /.container -->
</div><!-- /.wrapper -->

  <div class="container">
  • Status
  • API
  • Training
  • Shop
  • Blog
  • About
  • </ul>
    
    <a href="/">
      <span class="mega-octicon octicon-mark-github" title="GitHub"></span>
    </a>
    
    <ul class="site-footer-links">
      <li>&copy; 2014 <span title="0.02799s from github-fe128-cp1-prd.iad.github.net">GitHub</span>, Inc.</li>
        <li><a href="/site/terms">Terms</a></li>
        <li><a href="/site/privacy">Privacy</a></li>
        <li><a href="/security">Security</a></li>
        <li><a href="/contact">Contact</a></li>
    </ul>
    
<div class="fullscreen-overlay js-fullscreen-overlay" id="fullscreen_overlay">
<textarea name="fullscreen-contents" id="fullscreen-contents" class="js-fullscreen-contents" placeholder="" data-suggester="fullscreen_suggester"></textarea>
<div id="ajax-error-message" class="flash flash-error">
  <span class="octicon octicon-alert"></span>
  <a href="#" class="octicon octicon-remove-close close js-ajax-error-dismiss"></a>
  Something went wrong with that request. Please try again.
</div>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment