Reading Between the Lines

To figure out what “this” really is

Nick Bottomley
Hack Reactor June 2014

--

Rehashing the “this” lecture with some extra thoughts on “use strict” and Function.prototype.bind

Global Reference

Whenever this is not enclosed in any function.

<script>
console.log( this ); // no function in sight
// => window object
</script>

Free Function Invocation

Whenever a function is called by itself, rather than as a method of an object. Basically, function name followed by parentheses.

function sayThis(){ console.log( this ) }
sayThis();
// => window object

.call() and .apply()

These are methods exist on Function.prototype, and their purpose is to explicitly set the this value in a function call. The first argument passed to either of these will be the this value.

function sayThis(){ console.log( this ) }
sayThis.call( document.body )
// => body DOM element.

Method Invocation

function sayThis(){ console.log( this ) }
obj = { prop: "val" };
obj.speak = sayThis;
obj.speak();
// => obj

Keep in mind that obj.speak and sayThis are both pointers to the exact same function object. Go ahead and add the above code, then do obj.speak === sayThis in your console to prove it. The difference between them is only apparent at call time.

Constructor Mode

Running a function “in constructor mode” basically means putting new before the function call. This indicates to the interpreter that you want to create a brand new object, and it helpfully sets the this context within the function to the new object you’re creating. Often, certain constructor functions are intended to be run only in constructor mode, due to the fact that internally they expect to be creating a new object (and use this accordingly), rather than performing work on an existing object. Using them not in constructor mode can have unintended consequences, particularly accidentally adding stuff to the global scope.

A function run in constructor mode implicitly returns the newly created object. You can see this in action below. You can explicitly return something else, but generally you probably don’t want to do that.

function Person( name, age, gender ){
this.name = name;
this.age = age;
this.gender = gender;
}
new Person( "Jane", 30, "female" );
// => {name: "Jane", age: 30, gender: "female"}

Let’s see what happens if we fail to use new:

Person("Jane", 30, "female");
// returns nothing... but we just defined 3 globals.
window.name // => "Jane"
window.age // => 30
window.gender // => "female"

For this reason, some advocate the use of “factory functions” to instantiate objects, rather than using constructors. Alternately, throwing a “use strict”; at the top of your constructor will prevent accidentally spamming the global object. Let’s take a quick detour to talk about…

“this” and “use strict”

The default binding of this to the window object is considered by some to be a major language design mistake. Placing “use strict”; at the top of a function will disable implicit this binding within that function. If, for some reason, you wanted to call a function in the context window, you can use, say, .call() to accomplish it. If you’re not familiar with it, you can read up on strict mode at MDN.

“this” in a Callback Function

Being able to pass functions as arguments to other functions is awesomely powerful. But one of the downsides is that it can obscure a function’s eventual execution context. Here’s a function that takes an object and a function, and calls the function on the object.

var callAsMethod = function( obj, fn ) {
var args = Array.prototype.slice.call( arguments, 2 );
return fn.apply( obj, args );
};

var repo = { language: "js", size: 125, author: "nick" };
var pluckSize = function(){ return this.size };

callAsMethod( repo, pluckSize ); // => 125

However, in order to track down the context, we need to go into the body of callAsMethod() and actually take a look at how it handles the callback passed to it. Further, pluckSize doesn’t appear anywhere followed by parentheses, or even used with .apply or .call. Inside callAsMethod, in this specific case, fn refers to pluckSize, and it’s only through that alias that we ever call pluckSize. This can be make it difficult to track down the context, but the call stack can help you greatly.

In certain cases you can’t easily go into a function and inspect the body to check out how it’s calling a function you passed it. This might happen with minified library code, or with native code. Luckily, native functions are very well documented, and you should be able to easily see how the context of a passed in function is set. Hopefully same with libraries; if you’re using one with bad documentation or unexpected behavior, consider switching.

Bound Functions

One last thing about this, that’ll kind of crap all over the nice rules above, is Function.prototype.bind. When you use .call() or .apply() to run a function, you can easily see that it’s being used when you track down the call. Using .bind() introduces some kind of funky behavior. .bind() returns a function with a fixed this value that can’t be changed. Here’s some demonstration code for you to pick apart.

var repo1 = { language: "js", size: 125, author: "nick" }; 
var repo2 = { language: "ruby", size: 200, author: "ian" };
var pluckSize = function(){ return this.size };

var getSize1 = pluckSize.bind( repo1 );
getSize1(); // => 125
getSize1.call( repo2 ); // => 125 (did NOT execute on repo2)

var repo3 = { size: 300, getSize: getSize1 };
repo3.getSize(); // => 125 (did NOT execute on repo3)

That’s pretty much it. Using these strategies should let you track down the this value in almost every case you run into.

Thanks for reading. Find me on Twitter @nickhbottomley with questions or comments.

--

--