I ran into a small but instructive bug in a Vue component recently. A method had been written as an arrow function, and instead of the expected name it logged undefined:

export default {
    data() {
        return {
        name: 'Hello World'
      }
    },
    methods: {
        getName6: () => {
            console.log(this.name); //=>undefined
        }
    }
}

At first nothing looked off. A colleague pointed at the obvious: arrow functions don't get a this of their own. That sent me back through the ways this gets set, and these are the notes from that detour.

Roughly, what MDN says

Arrow function expressions are shorter than function expressions, and they have no this, arguments, super or new.target of their own. That makes them handy wherever you'd otherwise pass an anonymous function, but it also means they can't be used as constructors.

Plain calls and global code

It helps to start from zero. In global code, and inside an ordinary function call, this is the window object:

console.log(this); //=> global code, prints the window object

function hello() {
    console.log(this);
}
hello(); //=> function call, prints the window object

(Everything here assumes non-strict mode, where an unset this falls back to the global object.)

Called as an object method

var obj = {
    name:'Hello'
  };

  function foo(){
    console.log(this);
  }
  obj.newFoo = foo;
  obj.newFoo(); //=> {name: "test",newFoo: ƒ foo()}

Now this means the object the method was called on — whatever sits to the left of the dot. In the console you'd be looking at:

{
    arguments: null,
    caller: null,
    length: 0,
    name: "foo"
}

Called with new

function hello(){
    this.name = 'test';
    this.age = 18;
    console.log(this);
}
var h = new hello(); //=>hello {name: "test", age: 18}
console.log(c); //=> hello {name: "test", age: 18}
var w = hello(); //=> prints the window object

With new, this is a fresh object being assembled. Call the same function without new and this drops back to window — precisely the kind of inconsistency that earned JavaScript so much complaining.

That is why the old defensive habits existed: stash a reference in a variable like var self = this, or pin the value with bind(). Arrow functions take a different route. They never receive a this of their own at all, so they simply read the one belonging to the scope in which they were written.

An arrow function decides this once

var C = function(){
  this.name = 'test';
  this.greet = () => {
    console.log('Hello,I am '+ this.name + '!');
  };
  this.oldGreet = function() {
    console.log('Hello,I am '+ this.name + '!');
  }
}
var obj = new C();
obj.greet();                 //=> prints Hello,I am test!
setTimeout(obj.greet, 1000); //=> prints Hello,I am test!

setTimeout fires a second later, long after the call to obj.greet() has finished, and the name still comes out right. The arrow function was created inside the constructor while this was the new instance, and it held onto that reference from then on.

Where the two really diverge

The key point isn't just that arrow functions skip rebinding — it's that their scope cannot be rebound at all. call, apply and bind won't move their this. The value is settled at the place the function is written, so it makes no difference what happens at the place it's called.

Fixing the component

The method only needed to be a regular function, so that this would be the component instance; a nested arrow function then inherits it:

export default {
    data() {
        return {
        name: 'Hello World'
      }
    },
    methods: {
        getName6: function() {
            () => {
                console.log(this); //=>Hello World
            }
        }
    }
}

Arrow functions are shorter and read nicely, but that isn't a reason to reach for them everywhere. If the this a piece of code will see isn't obvious to you, write a normal function and keep the question closed.

The one line worth remembering: an arrow function's this always refers to the object in scope where it was defined, never the object it happens to run against.