Skip to content

Instantly share code, notes, and snippets.

@dlukes
Last active January 25, 2017 12:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dlukes/460034d970cc787106b67d28e3e8f5f8 to your computer and use it in GitHub Desktop.
Save dlukes/460034d970cc787106b67d28e3e8f5f8 to your computer and use it in GitHub Desktop.
A review of `this` in regular vs. arrow functions in JavaScript.
/* With regular functions, `this` is bound purely depending on how the function is called. If it's
* bound to an object (either explicitly or via the `instance.funcName()` sugar), `this` refers to
* that object. Otherwise, it's `undefined` (or `window`, depending on your exact environment and
* whether `"use strict";` is in effect).
*
* In particular, it doesn't matter whether the function is attached to the object itself as a
* property, i.e. each instance has its own version of the function created during initialization,
* (see `Class2`), or whether it exists on the object's `prototype` as a method and all instances
* share it (see `Class1`). `this` is always resolved dynamically, i.e. depending on the call site.
* Neither of these is well suited for yanking out of the context of the original object and using
* as a callback which relies on `this`, because `this` will be `undefined` when the function is
* called standalone.
*
* A way to alleviate this in the property approach (`Class2`) is to rebind `this` to a custom
* variable and refer to that instead inside the function (see `Class2b`).
*/
console.log("Function method:");
function Class1(data) {
this.data = data;
};
Class1.prototype.callback = function(spec) {
console.log(`${spec}: ${this.data}`);
};
let c = new Class1("some data");
c.callback("Called via instance"); // Called via instance: some data
let callback = c.callback;
callback("Called standalone"); // Called standalone: undefined
// -------------------------
console.log("Function property:");
function Class2(data) {
this.data = data;
this.callback = function(spec) {
console.log(`${spec}: ${this.data}`);
};
};
c = new Class2("some data");
c.callback("Called via instance"); // Called via instance: some data
callback = c.callback;
callback("Called standalone"); // Called standalone: undefined
// -------------------------
console.log("Function property revisited:");
function Class2b(data) {
this.data = data;
// rebind `this` to a custom variable...
let _this = this;
this.callback = function(spec) {
// ... and use that instead
console.log(`${spec}: ${_this.data}`);
};
};
c = new Class2b("some data");
c.callback("Called via instance"); // Called via instance: some data
callback = c.callback;
callback("Called standalone"); // Called standalone: some data
/* By contrast, `this` in arrow functions is resolved lexically -- if it's defined in the
* surrounding lexical scope of the arrow function, then it takes that value inside it as well,
* irrespective of how the function is called. If it isn't defined in the surrounding lexical scope,
* then it's `undefined`.
*
* This means that using an arrow function as a method (defined on the object's prototype and shared
* by all instances) is not possible if you need to use `this` (see `Class3` -- there's no `this` at
* the toplevel). But when used as instance properties, arrow functions can easily refer to `this`
* without jumping through any additional hoops, because it gets bound to the value of `this`
* provided by the surrounding lexical scope, which is that of the constructor function (i.e. `this`
* will be the instance object, see `Class4`), irrespective of how the function is called.
*/
console.log("Arrow method:");
function Class3(data) {
this.data = data;
};
Class3.prototype.callback = (spec) => {
console.log(`${spec}: ${this.data}`);
};
c = new Class3("some data");
c.callback("Called via instance"); // Called via instance: undefined
callback = c.callback;
callback("Called standalone"); // Called standalone: undefined
// -------------------------
console.log("Arrow property:");
function Class4(data) {
this.data = data;
this.callback = (spec) => {
console.log(`${spec}: ${this.data}`);
};
};
c = new Class4("some data");
c.callback("Called via instance"); // Called via instance: some data
callback = c.callback;
callback("Called standalone"); // Called standalone: some data
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment