Differences in Defining ES6 Class Methods

in #steemdev6 years ago

In ES6 there are two common ways to define class methods, but they behave completely different.

The first way is to define them as a standard class function.

class A {
    foo() {
        console.log('foo from A')
    }
}

But you can also define them as an instance specific function:

class A {
    foo = () => {
        console.log('foo from A')
    }
}

The first difference is pretty obvious if you know ES6's fat arrow functions:
It binds the this variable inside the function call to the this variable of the scope of where the function was defined.
So in our case, this will be bound to the current class instance.

We can achieve the same by avoiding arrow functions and use a standard class method in addition to binding it in the constructor:

class A { 
    constructor() {
        this.foo = this.foo.bind(this)
    }

    foo() {
        console.log('foo from A')
    }
}

You might now think that the following class definitions have the same behavior:

These are not the same

However, they behave in a completely different way when we use JavaScript's class inheritance.

Class Inheritance yields different results

Let's run the following code:

class A { 
    constructor() {
        this.foo = this.foo.bind(this)
    }

    foo() {
        console.log('foo from A')
    }
}

class B extends A {
    foo() {
        super.foo();
        console.log('foo from B')
    }
}
new B().foo()

The expected result is logged:

foo from A
foo from B

Let's try the same with fat-arrow functions now:

class A { 
    foo = () => {
        console.log('foo from A')
    }
}

class B extends A {
    foo = () => {
        super.foo();
        console.log('foo from B')
    }
}
new B().foo()

We get an error:

Cannot read property 'call' of undefined

Cannot read property 'call' of undefined

What's happening here?

Whenever you call super.someFunction in JavaScript, a lookup for a key someFunction is done in __proto__ - that's the way JavaScript's class inheritance is implemented.
The reason why we get this error is because foo is not in __proto__ of the object instance of new B().

So, what this tells us is that fat arrow class functions cannot be used from subclasses.
Fat arrow functions can always only be defined through class instance properties. So what our class with transform-class-properties actually does, is attaching the function first inside the constructor when a concrete class instance is created:

class A {
    constructor() {
        this.foo = () => {
            console.log('foo from A')
        }
    }
}

Of course, doing it this way, foo does never exist in __proto__ and makes it unusable in extended classes.
If you need a method to be callable from a derived class (and you want to use the ES6 class syntax), you need to define that method as a normal class method using shorthand notation:

class A { 
    constructor() {
        this.foo = this.foo.bind(this)
    }

    foo() {
        console.log('foo from A')
    }
}

class B extends A {
    foo() {
        super.foo();
        console.log('foo from B')
    }
}
new B().foo()

Originally published at https://cmichel.io

Sort:  

Neet. Never thought about using fat arrow function when defining a class. Turns out there is a good reason not to do it :)

I still use fat arrows quite often for React components, to be honest. You just need to be aware that you'll get problems when extending that class.

Oh, I use fat arrow functions all the time! Just not in conjunction with class properties. Inside methods, for callbacks and in promises for example they are great.

This is a good observation that I had never considered, thanks. One of the problems with fat arrows (and even with the class keyword in general) is that it effectively covers up the way that Javascript works fundamentally and makes it look like something different.

It's super important to understand the basics relating to these features (i.e. Object prototypes and the behaviour of this). I strongly recommend to anyone who hasn't already to check out Kyle Simpson's 'You don't know JS`book series which covers both of these topics and many more in careful detail. You're guaranteed to learn a thing or two, and it's free!

Yes, I only came across it because my code didn't work and I had to investigate :D Most people just don't really use OOP in JavaScript / React where binding is needed for the event handlers

Congratulations @cmichel! You have completed some achievement on Steemit and have been rewarded with new badge(s) :

Award for the number of upvotes

Click on any badge to view your own Board of Honor on SteemitBoard.
For more information about SteemitBoard, click here

If you no longer want to receive notifications, reply to this comment with the word STOP

By upvoting this notification, you can help all Steemit users. Learn how here!

Damnn nice coding lesson. I'm still a newby with the coding and this is really hard for me the understand. Gonna read it another 10 times and it might makes sense then! haha

U definetly got another follower mate!

Beyoutiful post you are on fire.
Keep it up god will bless you.

Coin Marketplace

STEEM 0.20
TRX 0.13
JST 0.030
BTC 66631.72
ETH 3487.54
USDT 1.00
SBD 2.71