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:

  1. function foo() {
  2. console.log( this.a );
  3. }
  4. var a = 2;
  5. 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 applies 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.

If strict mode is in effect, the global object is not eligible for the default binding, so the this is instead set to undefined.

  1. function foo() {
  2. "use strict";
  3. console.log( this.a );
  4. }
  5. var a = 2;
  6. 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 call-site of foo() is irrelevant.

  1. function foo() {
  2. console.log( this.a );
  3. }
  4. var a = 2;
  5. (function(){
  6. "use strict";
  7. foo(); // 2
  8. })();

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

  1. function foo() {
  2. console.log( this.a );
  3. }
  4. var obj = {
  5. a: 2,
  6. foo: foo
  7. };
  8. 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 obj, 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 preceded 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:

  1. function foo() {
  2. console.log( this.a );
  3. }
  4. var obj2 = {
  5. a: 42,
  6. foo: foo
  7. };
  8. var obj1 = {
  9. a: 2,
  10. obj2: obj2
  11. };
  12. 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:

  1. function foo() {
  2. console.log( this.a );
  3. }
  4. var obj = {
  5. a: 2,
  6. foo: foo
  7. };
  8. var bar = obj.foo; // function reference/alias!
  9. var a = "oops, global"; // `a` also property on global object
  10. bar(); // "oops, global"

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 is when we consider passing a callback function:

  1. function foo() {
  2. console.log( this.a );
  3. }
  4. function doFoo(fn) {
  5. // `fn` is just another reference to `foo`
  6. fn(); // <-- call-site!
  7. }
  8. var obj = {
  9. a: 2,
  10. foo: foo
  11. };
  12. var a = "oops, global"; // `a` also property on global object
  13. doFoo( obj.foo ); // "oops, global"

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.

  1. function foo() {
  2. console.log( this.a );
  3. }
  4. var obj = {
  5. a: 2,
  6. foo: foo
  7. };
  8. var a = "oops, global"; // `a` also property on global object
  9. setTimeout( obj.foo, 100 ); // "oops, global"

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

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

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:

  1. function foo() {
  2. console.log( this.a );
  3. }
  4. var obj = {
  5. a: 2
  6. };
  7. foo.call( obj ); // 2

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

If you pass a simple primitive value (of type string, boolean, or number) as the this binding, the primitive value is wrapped in its object-form (new String(..), new Boolean(..), or new Number(..), respectively). This is often referred to as “boxing”.

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:

  1. function foo() {
  2. console.log( this.a );
  3. }
  4. var obj = {
  5. a: 2
  6. };
  7. var bar = function() {
  8. foo.call( obj );
  9. };
  10. bar(); // 2
  11. setTimeout( bar, 100 ); // 2
  12. // `bar` hard binds `foo`'s `this` to `obj`
  13. // so that it cannot be overriden
  14. 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, it 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:

  1. function foo(something) {
  2. console.log( this.a, something );
  3. return this.a + something;
  4. }
  5. var obj = {
  6. a: 2
  7. };
  8. var bar = function() {
  9. return foo.apply( obj, arguments );
  10. };
  11. var b = bar( 3 ); // 2 3
  12. console.log( b ); // 5

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

  1. function foo(something) {
  2. console.log( this.a, something );
  3. return this.a + something;
  4. }
  5. // simple `bind` helper
  6. function bind(fn, obj) {
  7. return function() {
  8. return fn.apply( obj, arguments );
  9. };
  10. }
  11. var obj = {
  12. a: 2
  13. };
  14. var bar = bind( foo, obj );
  15. var b = bar( 3 ); // 2 3
  16. 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:

  1. function foo(something) {
  2. console.log( this.a, something );
  3. return this.a + something;
  4. }
  5. var obj = {
  6. a: 2
  7. };
  8. var bar = foo.bind( obj );
  9. var b = bar( 3 ); // 2 3
  10. 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.

Note: As of ES6, the hard-bound function produced by bind(..) has a .name property that derives from the original target function. For example: bar = foo.bind(..) should have a bar.name value of "bound foo", which is the function call name that should show up in a stack trace.

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:

  1. function foo(el) {
  2. console.log( el, this.id );
  3. }
  4. var obj = {
  5. id: "awesome"
  6. };
  7. // use `obj` as `this` for `foo(..)` calls
  8. [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:

  1. 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.

For example, the Number(..) function acting as a constructor, quoting from the ES5.1 spec:

15.7.2 The Number Constructor

When Number is called as part of a new expression it is a constructor: it initialises the newly created object.

So, pretty much any ol’ function, including the built-in object functions like Number(..) (see Chapter 3) 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:

  1. function foo(a) {
  2. this.a = a;
  3. }
  4. var bar = new foo( 2 );
  5. 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.