18

Can someone please explain why defining a prototype function with lambda expression doesn't work? I thought this must be asked before but couldn't find it.

function Book(title, year) {
    this.title = title;
    this.year = year;

    // define a function within the object, which works fine
    this.printYear = () => console.log("instance function of an object: " + this.year);
}

this doesn't work

Book.prototype.printTitle2 = () => {
        console.log(this.title);
    }

and this is fine of course:

Book.prototype.printTitle = function() {
         console.log(this);
         console.log(this.title);
    }
dragonfly02
  • 3,403
  • 32
  • 55
  • 2
    fat arrows don't have `this` – georg Mar 26 '16 at 10:44
  • @georg I think it does but just not what I thought it was. – dragonfly02 Mar 26 '16 at 11:05
  • 1
    @stt106: georg's comment is correct other than that it's missing two words: "a" and "binding": "arrows don't have a `this` binding". A "binding" is the mechanism the JavaScript spec uses to talk about resolving things like `this`, variable names, and constant names. For instance, a function declaring a variable called "a" has a *binding* for that variable (indirectly through an env record). Normal functions have a *binding* for `this` which is set to a value when they're called, but arrow functions don't, which is why they close over the `this` binding of the context in which they're created. – T.J. Crowder Mar 26 '16 at 11:22

3 Answers3

24

One of the chief features of arrow functions is that they close over the this from the context in which they're created; they don't get it based on how they're called like other functions do. So...

// ...whatever `this` is *here*
Book.prototype.printTitle2 = () => {
    // ...is what `this` will be *here*
    console.log(this.title);
};

But your function relies on this varying depending on how it's called.

This just isn't a use-case for arrow functions. Use a normal function:

Book.prototype.printTitle2 = function() {
    console.log(this.title);
};

Or better yet, use the new class syntax:

class Book {
    constructor(title, year) {
        this.title = title;
        this.year = year;
    }

   printTitle2() {
        console.log(this.title);
    }
}
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
10

The Arrow function would resolve the context this belongs to the scope where the function was defined. I believe you have defined that function in window scope. So the this will point to window in your function.

You can use normal anonymous function here. And we have to be careful while using arrow functions.

Rajaprabhu Aravindasamy
  • 66,513
  • 17
  • 101
  • 130
0

In addition to @t-j-crowder's answer, I wanted to leave a test case (mocha assert) which you can use to visualize which is not working.

Also you can read more about the scope of arrow functions here: You Don't Know JS by Kyle Simpson, who explains this in detail.

Basically, an arrow function's this points to the surrounding context of the current context, which comes in handy if you have enclosing functions. What it does is basically doing the var self = this; thing.

Or as Kyle says:

[...] Lexical this in the arrow function callback in the previous snippet now points to the same value as in the enclosing makeRequest(..) function. In other words, => is a syntactic stand-in for var self = this.

In cases where var self = this (or, alternatively, a function .bind(this) call) would normally be helpful, => arrow functions are a nicer alternative operating on the same principle. [...]

You can test it yourself with my gist: https://gist.github.com/jay-bricksoft/96738dd8a48ceb9f517e914b834cc1ee

In my test case this was the output:

Lambda
    function(){}
      √ should equal function´s type
      1) should have equal context as function
      2) should be able to be used as constructor
  1 passing (39ms)
  2 failing
  1) Lambda function(){} should have equal context as function:
      AssertionError: 'undefined' == 'string'
      + expected - actual
      -undefined
      +string

      at Context.<anonymous> (test\index.js:29:14)
  2) Lambda function(){} should be able to be used as constructor:
     TypeError: construct_l is not a constructor
      at Context.<anonymous> (test\index.js:34:20)

EDIT: added example / reference to Kyle Simpson's "You Don't Know ES6" https://github.com/getify/You-Dont-Know-JS/tree/master/es6%20%26%20beyond

mgthomas99
  • 5,402
  • 3
  • 19
  • 21
Yuna
  • 122
  • 9