Skip to content

Instantly share code, notes, and snippets.

@kad3nce
Created March 20, 2014 21:07
Show Gist options
  • Save kad3nce/9673827 to your computer and use it in GitHub Desktop.
Save kad3nce/9673827 to your computer and use it in GitHub Desktop.
<title>You-Dont-Know-JS/this &amp; object prototypes/ch2.md at master · getify/You-Dont-Know-JS · GitHub</title>
<link rel="search" type="application/opensearchdescription+xml" href="/opensearch.xml" title="GitHub" />
<link rel="fluid-icon" href="https://github.com/fluidicon.png" title="GitHub" />
<link rel="apple-touch-icon" sizes="57x57" href="/apple-touch-icon-114.png" />
<link rel="apple-touch-icon" sizes="114x114" href="/apple-touch-icon-114.png" />
<link rel="apple-touch-icon" sizes="72x72" href="/apple-touch-icon-144.png" />
<link rel="apple-touch-icon" sizes="144x144" href="/apple-touch-icon-144.png" />
<meta property="fb:app_id" content="1401488693436528"/>

  <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://avatars2.githubusercontent.com/u/150330?s=400" name="twitter:image:src" />

<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="36C47EBE:456B:E7F2B0:532B5882" name="octolytics-dimension-request_id" />




<link rel="icon" type="image/x-icon" href="https://github.global.ssl.fastly.net/favicon.ico" />

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



    <script crossorigin="anonymous" src="https://github.global.ssl.fastly.net/assets/frameworks-40c107d5f9c17b1c5a24d77604a4722218ebdadd.js" type="text/javascript"></script>
    <script async="async" crossorigin="anonymous" src="https://github.global.ssl.fastly.net/assets/github-51c933638ea889d06c5c7c92dc76ffcb5977429a.js" type="text/javascript"></script>
    
    
  <meta http-equiv="x-pjax-version" content="88be32e098a8d8369083372953c2d3f8">

    <link data-pjax-transient rel='permalink' href='/getify/You-Dont-Know-JS/blob/35cd159744930187fb1fd4fa75d0dacf9a26f61e/this%20%26%20object%20prototypes/ch2.md'>

Skip to content
  <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%2Fthis%2520%26%2520object%2520prototypes%2Fch2.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="1a96c274c8fed836994f1a7a263eefceeaefdc67"
<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 id="start-of-content" class="accessibility-aid"></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,660
    </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">
        184
      </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'>22</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'>0</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"
               aria-label="Download getify/You-Dont-Know-JS as a zip file"
               title="Download getify/You-Dont-Know-JS 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/this%20&amp;%20object%20prototypes/ch2.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 -->
Zack Gao zackgao March 10, 2014
<div class="participation">
  <p class="quickstat"><a href="#blob_contributors_box" rel="facebox"><strong>5</strong> contributors</a></p>
      <a class="avatar tooltipped tooltipped-s" aria-label="getify" href="/getify/You-Dont-Know-JS/commits/master/this%20%26%20object%20prototypes/ch2.md?author=getify"><img alt="Kyle Simpson" class=" js-avatar" data-user="150330" height="20" src="https://2.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="zackgao" href="/getify/You-Dont-Know-JS/commits/master/this%20%26%20object%20prototypes/ch2.md?author=zackgao"><img alt="Zack Gao" class=" js-avatar" data-user="1768718" height="20" src="https://0.gravatar.com/avatar/6001ea13de4ede7192c0ebed057d1df4?d=https%3A%2F%2Fidenticons.github.com%2F95c4845ee8cb3fdcc59ed8765f6a058c.png&amp;r=x&amp;s=140" width="20" /></a>
<a class="avatar tooltipped tooltipped-s" aria-label="yannmadeleine" href="/getify/You-Dont-Know-JS/commits/master/this%20%26%20object%20prototypes/ch2.md?author=yannmadeleine"><img alt="yannmadeleine" class=" js-avatar" data-user="629627" height="20" src="https://1.gravatar.com/avatar/9e178b21bd03f54b1c739d56bd0da57c?d=https%3A%2F%2Fidenticons.github.com%2Fbaa08d1b9e789c87694be12160493dda.png&amp;r=x&amp;s=140" width="20" /></a>
<a class="avatar tooltipped tooltipped-s" aria-label="michaellopez" href="/getify/You-Dont-Know-JS/commits/master/this%20%26%20object%20prototypes/ch2.md?author=michaellopez"><img alt="Michael Lopez" class=" js-avatar" data-user="487039" height="20" src="https://1.gravatar.com/avatar/fb34fd87c0b857397c31ed14fc122296?d=https%3A%2F%2Fidenticons.github.com%2F81128775ddf177be6aad988785f2195f.png&amp;r=x&amp;s=140" width="20" /></a>
<a class="avatar tooltipped tooltipped-s" aria-label="davepreston" href="/getify/You-Dont-Know-JS/commits/master/this%20%26%20object%20prototypes/ch2.md?author=davepreston"><img alt="Dave Preston" class=" js-avatar" data-user="334989" height="20" src="https://1.gravatar.com/avatar/ab4cabff326a1b11334cbac550891b49?d=https%3A%2F%2Fidenticons.github.com%2Ffa1e83fe935116a5b157333a5fb11f2f.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://2.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="Zack Gao" class=" js-avatar" data-user="1768718" height="24" src="https://0.gravatar.com/avatar/6001ea13de4ede7192c0ebed057d1df4?d=https%3A%2F%2Fidenticons.github.com%2F95c4845ee8cb3fdcc59ed8765f6a058c.png&amp;r=x&amp;s=140" width="24" />
        <a href="/zackgao">zackgao</a>
      </li>
      <li class="facebox-user-list-item">
        <img alt="yannmadeleine" class=" js-avatar" data-user="629627" height="24" src="https://1.gravatar.com/avatar/9e178b21bd03f54b1c739d56bd0da57c?d=https%3A%2F%2Fidenticons.github.com%2Fbaa08d1b9e789c87694be12160493dda.png&amp;r=x&amp;s=140" width="24" />
        <a href="/yannmadeleine">yannmadeleine</a>
      </li>
      <li class="facebox-user-list-item">
        <img alt="Michael Lopez" class=" js-avatar" data-user="487039" height="24" src="https://1.gravatar.com/avatar/fb34fd87c0b857397c31ed14fc122296?d=https%3A%2F%2Fidenticons.github.com%2F81128775ddf177be6aad988785f2195f.png&amp;r=x&amp;s=140" width="24" />
        <a href="/michaellopez">michaellopez</a>
      </li>
      <li class="facebox-user-list-item">
        <img alt="Dave Preston" class=" js-avatar" data-user="334989" height="24" src="https://1.gravatar.com/avatar/ab4cabff326a1b11334cbac550891b49?d=https%3A%2F%2Fidenticons.github.com%2Ffa1e83fe935116a5b157333a5fb11f2f.png&amp;r=x&amp;s=140" width="24" />
        <a href="/davepreston">davepreston</a>
      </li>
  </ul>
</div>
file 663 lines (448 sloc) 24.375 kb

You Don't Know JS: this & Object Prototypes

Chapter 2: this All Makes Sense Now!

In Chapter 1, we discarded various misconceptions about this and learned instead that this is a binding made for each function invocation, based entirely on its call-site (how the function is called).

Call-site

To understand this binding, we have to understand the call-site: the location in code where a function is called (not where it's declared). We must inspect the call-site to answer the question: what's this this a reference to?

Finding the call-site is generally: "go locate where a function is called from", but it's not always that easy, as certain coding patterns can obscure the true call-site.

What's important is to think about the call-stack (the stack of functions that have been called to get us to the current moment in execution). The call-site we care about is in the invocation before the currently executing function.

Let's demonstrate call-stack and call-site:

function foo() {
    // call-stack is: `baz` -> `bar` -> `foo`
    // so, our call-site is in `bar`
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">"foo"</span><span class="p">);</span>

}

function bar() { // call-stack is: baz -> bar // so, our call-site is in baz

<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">"bar"</span><span class="p">);</span>
<span class="nx">foo</span><span class="p">();</span> <span class="c1">// &lt;-- call-site for `foo`</span>

}

function baz() { // call-stack is: baz // so, our call-site is in the global scope

<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">"baz"</span><span class="p">);</span>
<span class="nx">bar</span><span class="p">();</span> <span class="c1">// &lt;-- call-site for `bar`</span>

}

baz(); // <-- call-site for baz

Take care when analyzing code to find the actual call-site (from the call-stack), because it's the only thing that matters for this binding.

Nothing But Rules

We turn our attention now to how the call-site determines where this will point during the execution of a function.

You must inspect the call-site and determine which of 4 rules applies. We will first explain each of these 4 rules independently, and then we will illustrate their order of precedence, if multiple rules could apply to the call-site.

Default Binding

The first rule we will examine comes from the most common case of function calls: standalone function invocation. Think of this this rule as the default catch-all rule when none of the other rules apply.

Consider this code:

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

var a = 2;

foo(); // 2

The first thing to note, if you were not already aware, is that variables declared in the global scope, as var a = 2 is, are synonymous with global-object properties of the same name. They're not copies of each other, they are each other. Think of it as two sides of the same coin.

Secondly, we see that when foo() is called, this.a resolves to our global variable a. Why? Because in this case, the default binding for this applied to the function call, and so points this at the global object.

How do we know that the default binding rule applies here? We examine the call-site to see how foo() is called. In our snippet, foo() is called with a plain, un-decorated function reference. None of the other rules we will demonstrate will apply here, so the default binding applies instead.

Note: If Strict Mode is in effect, the global object is not eligible for the default binding, so the this is instead set to undefined.

function foo() {
    "use strict";
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">a</span><span class="p">);</span>

}

var a = 2;

foo(); // TypeError: this is undefined

A subtle but important detail is: even though the overall this binding rules are entirely based on the call-site, the global object is only eligible for the default binding if the contents of foo() are not running in Strict Mode; the Strict Mode state of the foo() call-site is irrelevant.

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

var a = 2;

(function(){ "use strict";

<span class="nx">foo</span><span class="p">();</span> <span class="c1">// 2</span>

})();

Note: Intentionally mixing Strict Mode and non-Strict Mode together in your own code is generally frowned upon. Your entire program should probably either be Strict or non-Strict. However, sometimes you include a third-party library that has different Strict'ness than your own code, so care must be taken over these subtle details.

Implicit Binding

Another rule to consider is: does the call-site have a context object, also referred to as an owning or containing object, though these alternate terms could be slightly misleading.

Consider:

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

var obj = { a: 2, foo: foo };

obj.foo(); // 2

Firstly, notice the manner in which foo() is declared and then later added as a reference property onto obj. Regardless of whether foo() is initially declared on foo, or is added as a reference later (as this snippet shows), in neither case is the function really "owned" or "contained" by the obj object.

However, the call-site uses the obj context to reference the function, so you could say that the obj object "owns" or "contains" the function reference at the time the function is called.

Whatever you choose to call this pattern, at the point that foo() is called, it's preceeded by an object reference to obj. When there is a context object for a function reference, the implicit binding rule says that it's that object which should be used for the function call's this binding.

Because obj is the this for the foo() call, this.a is synonymous with obj.a.

Only the top/last level of an object property reference chain matters to the call-site. For instance:

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

var obj2 = { a: 42, foo: foo };

var obj1 = { a: 2, obj2: obj2 };

obj1.obj2.foo(); // 42

Implicitly Lost

One of the most common frustrations that this binding creates is when an implicitly bound function loses that binding, which usually means it falls back to the default binding, of either the global object or undefined, depending on Strict Mode.

Consider:

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

var obj = { a: 2, foo: foo };

var bar = obj.foo; // function reference/alias!

bar(); // undefined

Even though bar appears to be a reference to obj.foo, in fact, it's really just another reference to foo itself. Moreover, the call-site is what matters, and the call-site is bar(), which is a plain, un-decorated call and thus the default binding applies.

The more subtle, more common, and more unexpected way this occurs in when we consider passing a callback function:

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

function doFoo(fn) { // fn is just another reference to foo

<span class="nx">fn</span><span class="p">();</span> <span class="c1">// &lt;-- call-site!</span>

}

var obj = { a: 2, foo: foo };

doFoo(obj.foo); // undefined

Parameter passing is just an implicit assignment, and since we're passing a function, it's an implicit reference assignment, so the end result is the same as the previous snippet.

What if the function you're passing your callback to is not your own, but built-in to the language? No difference, same outcome.

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

var obj = { a: 2, foo: foo };

setTimeout(obj.foo, 100); // undefined

Think about this crude theoretical pseudo-implementation of setTimeout() provided as a built-in from the JavaScript environment:

function setTimeout(fn, delay) {
    // wait (somehow) for `delay` milliseconds
    fn(); // <-- call-site!
}

It's quite common that our function callbacks lose their this binding, as we've just seen. But another way that this can surprise us is when the function we've passed our callback to intentionally changes the this for the call. Event handlers in popular JavaScript libraries are quite fond of forcing your callback to have a this which points to, for instance, the DOM element that triggered the event. While that may sometimes be useful, other times it can be downright infuriating. Unfortunately, these tools rarely let you choose.

Either way the this is changed unexpectedly, you are not really in control of how your callback function reference will be executed, so you have no way (yet) of controlling the call-site to give your intended binding. We'll see shortly a way of "fixing" that problem by fixing the this.

Explicit Binding

With implicit binding as we just saw, we had to mutate the object in question to include a reference on itself to the function, and use this property function reference to indirectly (implicitly) bind this to the object.

But, what if you want to force a function call to use a particular object for the this binding, without putting a property function reference on the object?

"All" functions in the language have some utilities available to them (via their [[Prototype]] -- more on that later) which can be useful for this task. Specifically, functions have call(..) and apply(..) methods. Technically, JavaScript host environments sometimes provide functions which are special enough (a kind way of putting it!) that they do not have such functionality. But those are few. The vast majority of functions provided, and certainly all functions you will create, do have access to call(..) and apply(..).

How do these utilities work? They both take, as their first parameter, an object to use for the this, and then invoke the function with that this specified. Since you are directly stating what you want the this to be, we call it explicit binding.

Consider:

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

var obj = { a: 2 };

foo.call(obj); // 2

Invoking foo with explicit binding by foo.call(..) allows us to force its this to be obj.

Note: With respect to this binding, call(..) and apply(..) are identical. They do behave differently with their additional parameters, but that's not something we care about presently.

Unfortunately, explicit binding alone still doesn't offer any solution to the issue mentioned previously, of a function "losing" its intended this binding, or just having it paved over by a framework, etc.

Hard Binding

But a variation pattern around explicit binding actually does the trick. Consider:

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

var obj = { a: 2 };

var bar = function() { foo.call(obj); };

bar(); // 2 setTimeout(bar, 100); // 2

// hard-bound bar can no longer have its this overriden bar.call(window); // 2

Let's examine how this variation works. We create a function bar() which, internally, manually calls foo.call(obj), thereby forcibly invoking foo with obj binding for this. No matter how you later invoke the function bar, he will always manually invoke foo with obj. This binding is both explicit and strong, so we call it hard binding.

The most typical way to wrap a function with a hard binding creates a pass-thru of any arguments passed and any return value received:

function foo(something) {
    console.log(this.a, something);
    return this.a + something;
}

var obj = { a: 2 };

var bar = function() { return foo.apply(obj, arguments); };

var b = bar(3); // 2 3 console.log(b); // 5

Another way to express this pattern is to create a re-usable helper:

function foo(something) {
    console.log(this.a, something);
    return this.a + something;
}

// simple PoC bind helper function bind(fn, obj) { return function() { fn.apply(obj, arguments); }; }

var obj = { a: 2 };

var bar = bind(foo, obj);

var b = bar(3); // 2 3 console.log(b); // 5

Since hard binding is such a common pattern, it's provided with a built-in utility as of ES5: Function.prototype.bind, and it's used like this:

function foo(something) {
    console.log(this.a, something);
    return this.a + something;
}

var obj = { a: 2 };

var bar = foo.bind(obj);

var b = bar(3); // 2 3 console.log(b); // 5

bind(..) returns a new function that is hard-coded to call the original function with the this context set as you specified.

API call "contexts"

Many libraries' functions, and indeed many new built-in functions in the JavaScript language and host environment, provide an optional parameter, usually called "context", which is designed as a work-around for you not having to use bind(..) to ensure your callback function uses a particular this.

For instance:

function foo(el) {
    console.log(el, this.id);
}

var obj = { id: "awesome" };

// use obj as this for foo(..) calls [1, 2, 3].forEach(foo, obj); // 1 awesome 2 awesome 3 awesome

Internally, these various functions almost certainly use explicit binding via call(..) or apply(..), saving you the trouble.

new Binding

The fourth and final rule for this binding requires us to re-think a very common misconception about functions and objects in JavaScript.

In traditional class-oriented languages, "constructors" are special methods attached to classes, that when the class is instantiated with a new operator, the constructor of that class is called. This usually looks something like:

something = new MyClass(..);

JavaScript has a new operator, and the code pattern to use it looks basically identical to what we see in those class-oriented languages; most developers assume that JavaScript's mechanism is doing something similar. However, there really is no connection to class-oriented functionality implied by new usage in JS.

First, let's re-define what a "constructor" in JavaScript is. In JS, constructors are just functions that happen to be called with the new operator in front of them. They are not attached to classes, nor are they instantiating a class. They are not even special types of functions. They're just regular functions that are, in essence, hijacked by the use of new in their invocation.

So, pretty much any ol' function can be called with new in front of it, and that makes that function call a constructor call. This is an important but subtle distinction: there's really no such thing as "constructor functions", but rather construction calls of functions.

When a function is invoked with new in front of it, otherwise known as a constructor call, the following things are done automatically:

  1. a brand new object is created (aka, constructed) out of thin air
  2. the newly constructed object is [[Prototype]]-linked
  3. the newly constructed object is set as the this binding for that function call
  4. unless the function returns its own alternate object, the new-invoked function call will automatically return the newly constructed object.

Steps 1, 3, and 4 apply to our current discussion. We'll skip over step 2 for now and come back to it in Chapter 5.

Consider this code:

function foo(a) {
    this.a = a;
}

var bar = new foo(2); console.log(bar.a); // 2

By calling foo(..) with new in front of it, we've constructed a new object and set that new object as the this for the call of foo(..). So new is the final way that a function call's this can be bound. We'll call this new binding.

Everything In Order

So, now we've uncovered the 4 rules for binding this in function calls. All you need to do is find the call-site and inspect it to see which rule applies. But, what if the call-site has multiple eligible rules? There must be an order of precedence to these rules, and so we will next demonstrate what order to ask the rules in.

It should be clear that the default binding is the lowest priority rule of the 4. So we'll just set that one aside.

Which is more precedent, implicit binding or explicit binding? Let's test it:

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

var obj1 = { a: 2, foo: foo };

var obj2 = { a: 3, foo: foo };

obj1.foo(); // 2 obj2.foo(); // 3

obj1.foo.call(obj2); // 3 obj2.foo.call(obj1); // 2

So, explicit binding takes precedence over implicit binding, which means you should ask first if explicit binding applies before checking for implicit binding.

Now, we just need to figure out where new binding fits in the precedence.

function foo(something) {
    this.a = something;
}

var obj1 = { foo: foo };

var obj2 = {};

obj1.foo(2); console.log(obj1.a); // 2

obj1.foo.call(obj2,3); console.log(obj2.a); // 3

var bar = new obj1.foo(4); console.log(obj1.a); // 2 console.log(bar.a); // 4

OK, new binding is more precedent than implicit binding. But do you think new binding is more or less precedent than explicit binding?

Note: It's not grammatically valid to try new foo.call(obj1) to test new binding directly against explicit binding. new and call/apply cannot be used together. But we can still use a hard binding to test the precedence of the two rules.

Before we explore that in a snippet, think back to how hard binding works, which is that Function.prototype.bind(..) creates a new wrapper function that is hard-coded to ignore its own this binding (whatever it may be), and use a manual one we provide. By that reasoning, it would seem obvious to assume that hard binding (which is a form of explicit binding) is more precedent than new binding, and thus cannot be overriden with new.

Let's check:

function foo(something) {
    this.a = something;
}

var obj1 = {};

var bar = foo.bind(obj1); bar(2); console.log(obj1.a); // 2

var baz = new bar(3); console.log(obj1.a); // 2 console.log(baz.a); // 3

Whoa! Look at how new bar(3) did not overwrite obj1.a to be 3 as you might have expected. Instead, the hard bound (to obj1) call to bar(..) is able to be overriden with new, which is how baz.a instead ends up as the value 3.

This is surprising if you go back to our "fake" bind helper:

function bind(fn, obj) {
    return function() {
        fn.apply(obj, arguments);
    };
}

This simple helper clearly cannot provide a way for a new operator call to override the hard-binding to obj.

But the built-in Function.prototype.bind(..) as of ES5 is more sophisticated, quite a bit so in fact. Here is the (slightly reformatted) polyfill provided by the MDN page for bind(..):

if (!Function.prototype.bind) {
    Function.prototype.bind = function(oThis) {
        if (typeof this !== "function") {
            // closest thing possible to the ECMAScript 5
            // internal IsCallable function
            throw new TypeError("Function.prototype.bind - what " +
                "is trying to be bound is not callable"
            );
        }
    <span class="kd">var</span> <span class="nx">aArgs</span> <span class="o">=</span> <span class="nb">Array</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">slice</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="nx">arguments</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span>
        <span class="nx">fToBind</span> <span class="o">=</span> <span class="k">this</span><span class="p">,</span>
        <span class="nx">fNOP</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(){},</span>
        <span class="nx">fBound</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(){</span>
            <span class="k">return</span> <span class="nx">fToBind</span><span class="p">.</span><span class="nx">apply</span><span class="p">(</span>
                <span class="p">(</span>
                    <span class="k">this</span> <span class="k">instanceof</span> <span class="nx">fNOP</span> <span class="o">&amp;&amp;</span>
                    <span class="nx">oThis</span> <span class="o">?</span> <span class="k">this</span> <span class="o">:</span> <span class="nx">oThis</span>
                <span class="p">),</span>
                <span class="nx">aArgs</span><span class="p">.</span><span class="nx">concat</span><span class="p">(</span><span class="nb">Array</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">slice</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="nx">arguments</span><span class="p">))</span>
            <span class="p">);</span>
        <span class="p">}</span>
    <span class="p">;</span>

    <span class="nx">fNOP</span><span class="p">.</span><span class="nx">prototype</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">prototype</span><span class="p">;</span>
    <span class="nx">fBound</span><span class="p">.</span><span class="nx">prototype</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">fNOP</span><span class="p">();</span>

    <span class="k">return</span> <span class="nx">fBound</span><span class="p">;</span>
<span class="p">};</span>

}

The part that's doing the task (allowing new overriding) is:

this instanceof fNOP &&
oThis ? this : oThis

// ... and:

fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();

We won't actually dive into explaining how this trickery works (it's complicated and beyond our scope here), but essentially the snippet determines whether or not the bound function has been called with new (resulting in a newly constructed object being its this), and if so, it uses that new this rather than the previously specified hard binding for this.

Why is new being able to override hard binding useful? Imagine a function that you are given (might be hard-bound, might not, you don't know), and you want to use that function but not cause any side-effects on any object it may have been hard-bound to. Call it with new, and you always get the newly constructed object back, regardless of if the function was previously hard-bound.

While that distinction may not be a terribly common need everyday coding, it's important to fully understand how all these parts of the mechanism interact, so we have an accurate set of rules to analyze and predict its outcome.

Determining this

Now, we can summarize the rules for determining this from a function call's call-site, in their order of precedence. Ask these questions in this order, and stop when the first rule applies.

  1. Is the function called with new (new binding)? If so, this is the newly constructed object.

    var bar = new foo()

  2. Is the function called with call or apply (explicit binding), even hidden inside hard binding? If so, this is the explicitly specified object.

    var bar = foo.call(obj2)

  3. Is the function called with a context (implicit binding), otherwise known as an owning or containing object? If so, this is that context object.

    var bar = obj1.foo()

  4. Otherwise, default the this. If in Strict Mode, pick undefined, otherwise pick the global object.

    var bar = foo()

That's it. That's all it takes to understand the traditional rules of this binding for function calls.

Lexical this

Normal functions abide by the 4 rules we just covered. But ES6 introduces a special kind of function that does not use these rules: arrow-function.

Arrow-functions are signified not by the function keyword, but by the => so called "fat arrow" operator. Instead of using the four standard this rules, arrow-functions inherit (lexically speaking) the this binding from the enclosing (function or global) scope.

Let's illustrate arrow-function lexical scope:

function foo() {
    // return an arrow function
    return (a) => {
        // `this` here is lexically inherited from `foo()`
        console.log(this.a);
    };
}

var obj1 = { a: 2 };

var obj2 = { a: 3 };

var bar = foo.call(obj1); bar.call(obj2);

The arrow-function created in foo() lexically captures whatever foo()s this is, and that's what is used, regardless of trying to subsequently override the this.

The most common use-case will likely be in the use of callbacks, such as event handlers or timers:

function foo() {
    setTimeout(() => {
        // `this` here is lexically inherited from `foo()`
        console.log(this.a);
    },100);
}

var obj = { a: 2 };

foo.call(obj); // 2

While arrow-functions provide an alternative to using bind(..) on a function to ensure its this, which can seem attractive, it's important to note that they essentially are disabling the traditional this mechanism in favor of the more widely-understood lexical scoping. Pre ES-6, we already have a fairly common pattern for doing so, which is basically almost indistinguishable from the spirit of ES6 arrow-functions:

function foo() {
    var self = this; // lexical capture of `this`
    setTimeout(function(){
        console.log(self.a);
    },100);
}

var obj = { a: 2 };

foo.call(obj); // 2

While self = this and arrow-functions both seem like good "solutions" to not wanting to use bind(..), they are essentially fleeing from this instead of understanding and embracing it.

If you find yourself writing this-style code, but most or all the time, you defeat the this mechanism with lexical self = this or arrow-function "tricks", perhaps you should either:

  1. Use only lexical scope and forget the false pretense of this-style code.

  2. Embrace this-style mechanisms completely, including using bind(..) where necessary, and try to avoid self = this and arrow-function "lexical this" tricks.

A program can effectively use both styles of code (lexical and this), but inside of the same function, and indeed for the same sorts of look-ups, mixing the two mechanisms is usually asking for harder-to-maintain code, and probably working too hard to be too clever.

Review (TL;DR)

Determining the this binding for an executing function requires finding the direct call-site of that function. Once examined, four rules can be applied to the call-site, in this order of precedence:

  1. Called with new? Use the newly constructed object.

  2. Called with call or apply? Use the specified object.

  3. Called with a context object owning the call? Use that context object.

  4. Default: undefined in Strict Mode, global object otherwise.

Instead of these four rules, ES6 arrow-functions use lexical scoping for this binding, which means they inherit the this binding (whatever it is) from its enclosing function. They are essentially a syntactic embrace of self = this in pre-ES6 code patterns.

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.02577s from github-fe124-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