Taking
JavaScript
Out Of
Context

this

$('.clickme').on('click', function() {
  this.innerHTML = 'Wait 1 sec...';
  setTimeout(function() {
    this.innerHTML = 'Done!';
  }, 1000);

  // But it doesn't work?

});

What?

var sayHi = person.sayHi;

sayHi();

'Hi, my name is undefined'

...Really?

el.addEventListener('click', person.sayHi);

el.click();

'Hi, my name is undefined'

F*CKING JAVASCRIPT

Mental model

Let's fix this

The short version

Every function shadows the value of this

When the function is called,
Look to the left of the dot.

person.sayHi();

// 'this' is person
sayHi();
// no dot, so 'this' is window
// (or undefined in strict mode)
new Person();
// 'this' is the new instance object

What's really going on?

Hint:  Schrödinger's cat

Question:
How do we define constructors?

With functions

var Person = function(name) {
  this.name = name;
};

Question:
How do we define methods?

Once again...
With functions.

var person = {

  name: 'Mark',
  
  sayHi: function() {
    return "Hi, I'm " + this.name;
  }
  
};

If everything is a function...

var sayHi = function(){
  return "Hi, I'm " + this.name;
};

var person = {
  sayHi: sayHi
};
// function:
sayHi();

// constructor:
new sayHi();

// method:
person.sayHi();

Same function
called in three different ways

“Current functions playing triple duty is difficult to understand.”

Dr. Axel Rauschmayer
ECMAScript.next: arrow functions and method definitions

Constructor, method, or
plain old function?

The calling code decides

Schrödinger's function

Passing a callback to someone else?

You're not the calling code

var runAsCallback = function(fn) {
  fn();
  // No dot,
  // so 'this' is window
};

// Even if you pass it a method
runAsCallback(person.sayHi);

Can we explicitly set this?

// When calling a function

sayHi.call(person, arg1, arg2);
// or...
sayHi.apply(person, [arg1, arg2]);
// When passing a method as a callback

runAsCallback(
  person.sayHi.bind(person)
);
// When trying to maintain the context

setTimeout(function() {

  // etc...
  
}.bind(this), 1000);

Is this worth it?

Another option...
Only use functions,
as functions

function makePerson(name) {
  var sayHi = function() {
    return "Hi, I'm " + name;
  };
  
  return {
    sayHi: sayHi
  };
};

For example,
Bespoke.js

bespoke.from = function(el, plugins) {
  var listeners = [],
      on = function(e, fn) { },
      next = function() { },
      prev = function() { }, // etc...
      
  return {
    on: on,
    next: next,
    prev: prev, // etc...
  };
};

Need private state? Simple.
Don't expose it.

No methods or constructors

Functions return objects,
powered by closures

“This is everything you need to make objects, and it all works because of functions

Douglas Crockford
The World's Most Misunderstood Programming Language

Functions:
The Good Parts

ES6 makes this pattern easy

Arrow
functions

let makePerson = (name) => {

  let sayHi = () => `Hi, I'm ${ name }`;
  
  return { sayHi };
  
};
$('.clickme').on('click', function() {
  this.innerHTML = 'Wait 1 sec...';
  
  setTimeout(() => {
    this.innerHTML = 'Done!';
  }, 1000);
  
});

You don't need this, but you need to understand it

And if you only remember one thing...

Look to the left of the dot.

Thank you!

bit.ly/gettingcontext

@markdalgleish