Joshua T Kalis he/him/his

I build great teams and excellent software.

What Is this?


Let's talk about this.


Warning, there might be a few bad puns.

Hopefully, this will not be too much of a problem.


Terminology

Context

With regard to JavaScript, the 'context' of a function is the value of the this keyword within a function body.

Function vs. Method*

A function is something created in a scope that is not attached to an object, whereas a method is a function that is a member of an object.

* This is my terminology for the purposes of this talk and when I am trying to speak about the difference between the two concepts. It is my attempt to be more clear about what I am talking about.


The Goal

Understand function context in JavaScript

You should be able to, with good confidence, reason about what the value of the this keyword is going to be, in a function during its' execution.


First Rule Of Thumb

The context of function execution is the object that called the function; if no scope is explicitly set, assume 'global'.


Simple Global Scope Execution

function testScope() {
  console.log(this);
}

// the context of this function
// execution is the global object
testScope();

Object Methods

var myObj = {
  method: function () {
    console.log(this);
  }
};

// the context of this method
// execution is the `myObj` object
myObj.method();

With 'Classes'

function OurObject() {
  this.method = function () {
    console.log(this);
  };
}

// using 'new' below changes the context
// the function executes in so that it
// can be use for object creation
var myObj = new OurObject();

// the context of this method
// execution is an empty object
myObj.method();

Now, With Prototypes

function OurObject() {}

OurObject.prototype.method = function () {
  console.log(this);
};

// using 'new' below changes the context
// the function executes in so that it
// can be use for object creation
var myObj = new OurObject();

// the context of this method
// execution is an empty object
myObj.method();

That's Great

But what about modifying context after function definition?


Business Requirement

Create a function that can accept any number of arguments and will return true if the number 9 was passed in and false otherwise.

includesNumber9();        // false
includesNumber9(1);       // false
includesNumber9(2, 3);    // false
includesNumber9(9);       // true
includesNumber9(7, 8, 9); // true
includesNumber9(19);      // false

Iterating Arguments

function includesNumber9() {
  var counter = 0,
      length = arguments.length
      found = false;

  while (!found && counter < length) {
    if (arguments[counter] === 9) {
      found = true;
    }

    counter += 1;
  }

  return found;
}

console.log(includesNumber9(1, 2, 3, 4, 9));

Is there a better way?

Possibly with less code.


Using Array's indexOf

Noticing that the arguments object is very similar to an array we could make the computer do our work for us.

function includesNumber9() {

  // 'TypeError: undefined is not a function'
  // the 'arguments' object is not an array; it's
  // an object and doesn't have an indexOf method
  return -1 !== arguments.indexOf(9);
}

console.log(includesNumber9(1, 2, 3, 4, 9));

Function Methods

Change function execution context


Bend JavaScript to your will


The ABCs of Context Manipulation

Function.prototype methods allow you to take control - to some extent - what context an function/method executes with.


Function.prototype.apply

"The apply() method calls a function with a given this value and arguments provided as an array (or an array-like object)." ~ MDN

function testFn() {
  console.log(this);
}

testFn();               // logs global object
testFn.apply(9);        // logs Number object
testFn.apply('Hello');  // logs String object

For now, we will ignore the additional argument that apply accpets.


Function.prototype.call

"The call() method calls a function with a given this value and arguments provided individually." ~ MDN

function testFn() {
  console.log(this);
}

testFn();               // logs global object
testFn.call(9);         // logs Number object
testFn.call('Hello');   // logs String object

For now, we will ignore the additional arguments that call accpets.


Over-simplification

Ignoring argument(s) after the first, apply and call are identical.

They invoke a function/method.


Function.prototype.bind

"The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called." ~ MDN

function testFn() {
  console.log(this);
}

var bound9 = testFn.bind(9);
var boundHello = testFn.bind('Hello');

testFn();             // global object
bound9(9);            // Number object
boundHello('Hello');  // String object

For now, we will ignore the additional arguments that bind accpets.


When would any of these be needed?


Array Methods On arguments

function includesNumber9() {
  var args;

  args = Array.prototype
    .slice
    .call(arguments, 0);

  return -1 !== args.indexOf(9);
}

console.log(includesNumber9(8, 9)); // true
console.log(includesNumber9(4, 5)); // false

Array Methods On arguments (continued)

function includesNumber9() {

  return -1 !== [].indexOf.call(arguments, 9);
}

console.log(includesNumber9(8, 9)); // true
console.log(includesNumber9(4, 5)); // false

Gauranteed Context

var customObj,
    url = 'awesome/url';

customObj = {
  statement: 'Yay!'
};

function callback() {
  console.log(this.statement);
}

mythicalAjax(url, callback); // undefined
mythicalAjax(url, callback.bind(customObj)); // 'Yay!';

Once Bound

Be careful however. Once a function has context bound using bind that context is forever.

var bound;

function Foo() {
  console.log(this);
}

bound = Foo.bind('Bar');

bound.call('Foo'); // 'Bar'

Was this Good/Helpful?


Any Questions About this?