DEV Community

loading...
Cover image for Understanding `this` in Javascript

Understanding `this` in Javascript

BlueBlazin
Deep learning, programming languages, and web dev.
・4 min read

How did this all begin?

The one thing that seems to unify beginner javascript programmers more than anything else is their shared confusion about the concept of this.

Perhaps that's because this or self in other languages behaves differently than in javascript.

Look, the language was created in ten days. Some less than ideal decisions were probably made. It is what it is.

This exists

I mean it. You can access this anywhere in a javascript program. At the outermost level? Sure!

console.log(this);
Enter fullscreen mode Exit fullscreen mode

Inside a function? Also, yes.

function foo() {
  console.log(this);
}
Enter fullscreen mode Exit fullscreen mode

What about constructors? Of course!

function Bar(x, y) {
  this.x = x;
  this.y = y;
}
Enter fullscreen mode Exit fullscreen mode

But see, here lies the confusion. It certainly feels sensible to talk about this as a property on functions, constructors, and methods. But that's wrong.

This exists on its own! It's a property on function scopes!

What's this scope thing?

You can think of function scopes (or Function Environment Records to be proper) as containers for variables. Each scope will contain a bunch of names of variables (and associated values).

From within any function scope you can:

  1. access variables defined in that scope
  2. access variables defined in any ancestor function scope

scopes

At the outermost level is the global scope on which live such famous builtins as: Math, and console, and Number among others.

Notice how they are labeled foo() scope or bar() scope in the diagram and not foo scope, bar scope, etc.

That's because a scope is associated with function calls, not functions themselves. A new function scope is created for each function call. That's why you could do:

function foo(x) {
  let bar = x;
}

foo(7);
foo(42);
Enter fullscreen mode Exit fullscreen mode


and bar will be created two different times with two different values assigned to it.

Now look at the image again. You'll see this exists on each function scope. You don't need to declare it, it's added to the scope automatically.

This once more

Here's a recap of what I just said:

calls-scopes-this

Calls create function scopes. Those scopes create this. Ergo, by transitivity, this is associated with function calls.

Not functions. Not constructors. Calls!

The rules of this language

In javascript, there are just two types of calls. The value of this depends on the type of call you make.

1. Function calls

Just plain old vanilla function calls.

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

foo(); // Window
Enter fullscreen mode Exit fullscreen mode

This gets set to the global Window object for these.

2. Method calls

Method calls are nothing special, just calls which have the form <object>.<attribute>(). For example:

const foo = { 
  bar: function () {
    console.log(this);
  }
};
foo.bar();
Enter fullscreen mode Exit fullscreen mode


For method calls, this gets set to the object from which the method was called. Again, functions don't matter* for this, just the calls.

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

let x = { bar: foo };

foo(); // Window
x.bar(); // x
let baz = x.bar;
baz(); // Window
Enter fullscreen mode Exit fullscreen mode


Even baz will print Window. It's not a method call, it doesn't follow the method call format!

That's pretty much all there is to it.........

........or is it?!

I apologize for this

Remember how I told you this is all about function calls, not the functions themselves? Well, I lied.

Ok look, let me remind you once again: They made javascript in 10 days!

The this rules we've discussed above, they are a bit limiting. So there's three* ways you can override these rules.

* don't you dare even mention apply

1. call

The special call method on functions allows you to pass your own custom value of this to a function call (or the call's scope I should say).

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

foo.call({ a: 42 }); // { a: 42 }
Enter fullscreen mode Exit fullscreen mode

2. bind

bind is another builtin method on functions. Much like call it too allows you to pass a custom value for this to the function call. Except unlike call, bind doesn't immediately call the function. It instead returns a special 'bound' functions.

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

let bar = foo.bind({ a: 42 });
foo(); // Window
bar(); // { a: 42 }
Enter fullscreen mode Exit fullscreen mode

3. Arrow functions

Arrow functions are the third way to override the call rules for this described previously. Arrow functions capture the this from the function scope in which they are created.

arrow-functions

function foo() {
  const bar = () => {
    console.log(this);
  };

  return bar;
}

let bar = foo.call({ a: 42 });
bar(); // { a: 42 }
Enter fullscreen mode Exit fullscreen mode

So they're essentially the same as defining a normal function but then also binding it.

// achieves the same effect
function foo() {
  const bar = (function () {
    console.log(this);
  }).bind(this);

  return bar;
}

let bar = foo.call({ a: 42 });
bar(); // { a: 42 }
Enter fullscreen mode Exit fullscreen mode

In summary

Yes, no pun in the heading this time (oops). The key takeaways are this:

In JS this is associated with the current function scope, and since function scopes are associated with functions calls -- this is associated with calls. Those are the rules but they can be overridden.

That is the reason why people are often confused when passing functions referencing this to callbacks. It's also why you were told to use arrow functions if you need to pass them to callbacks.

I too was confused about this for a long time. Instead of taking the more sensible approach of reading an article such as this one, I instead decided to implement my own javascript.

I wrote a subset of javascript. In that subset of javascript. If you want to go down that rabbit hole, then check out the repo:
https://github.com/BlueBlazin/thislang

If you want more posts on other javascript or computing related topics let me know on twitter:
https://twitter.com/suicuneblue

Discussion (0)