ES6 functions, arrow functions and 'this' in an ES6 class [duplicate] - javascript

This question already has answers here:
Should I write methods as arrow functions in Angular's class
(3 answers)
Arrow vs classic method in ES6 class
(1 answer)
Closed 5 years ago.
class App extends Component {
constructor(props) {
...
}
onChange = (e) => this.setState({term: e.target.value})
onSubmit(e){
e.preventDefault();
const api_key = "C1hha1quJAQZf2JUlK";
const url = `http://api.giphy.com/v1/gifs/search?q=${this.state.term}&api_key=${api_key}`;
}
render() {
return (
<div>
<form onSubmit={this.onSubmit}>
<input value={this.state.term} onChange={this.onChange}/>
<button>Search!</button>
</form>
</div>
);
}
}
What is the difference between the two type of functions declared in the class (onChange and onSubmit). I get an error on referencing this.sate in const url if I declare it as an ES6 class method but changing it to arrow function fixes it.
I want to know how exactly 'this' is handled in both the cases
Also, how do I do it the other way? Say, if I want to use the same onSubmit function (ES6 class method) but want to handle this when I call it (in the form element), how do I do it ?
Using this.onSubmit.bind(this) ?

It's important to know that this syntax:
class A {
method = () => {}
}
is just syntactic sugar for creating an instance method in the class constructor:
class A {
constructor() {
this.method = () => {}
}
}
Note: This syntax is not an official part of the JavaScript language yet (currently in stage 3) so you must use a transpiler like Babel to handle it.
The value of this within method is the class A because that is what this points to in the constructor (since arrow functions inherit the context from the scope they are defined in):
class A {
constructor() {
this.method = () => this;
}
}
const instance = new A();
console.log(instance.method() === instance); // true
Defining a regular (non-arrow function) method on the class creates a method on the class prototype (not instance) but sets no rules on what this will be (since this is dynamic in JS and depends on how a function is called, not how it's defined).
class A {
method() {}
}
console.log(new A().method === A.prototype.method); // true
If methods defined in either of these ways are called on the class instance (via the .), as per the rule of how this is bound when a function is called as a method of an object, this will point to the class instance in both cases:
class A {
constructor() {
this.methodOnInstance = () => this;
}
methodOnPrototype() { return this; }
}
const instance = new A();
console.log(
instance.methodOnInstance() === instance.methodOnPrototype(), // true
instance.methodOnPrototype() === instance // true
);
One major difference between the two method declarations above is that the instance method has this always fixed to the class instance while the class (prototype) method does not (we can change it by using Function.prototype.apply or Function.prototype.call)
class A {
constructor() {
this.methodOnInstance = () => this;
}
methodOnPrototype() { return this; }
}
const instance = new A();
console.log(
instance.methodOnInstance() === instance.methodOnPrototype(), // true
instance.methodOnPrototype.call('new this') === 'new this' // true
);
A common occurrence where the this changes is within an event handler, where the event handler calls the function passed into it and binds the context to the element on which the event happened (so overrides the value of this to be the element that was clicked or whatever the event was)
This happens in React as well for all (synthetic) DOM event handlers.
Therefore, if we want our method's context to always point to the instance of the React component, we can use the instance method.
Another way of restricting the context but not using the special instance method syntax that requires Babel is to directly create an instance method ourselves by creating a new function from the class (prototype) method with a bound context (using Function.prototype.bind):
class A {
constructor() {
this.methodOnInstance = this.methodOnPrototype.bind(this);
}
methodOnPrototype() { return this; }
}
const instance = new A();
console.log(
instance.methodOnInstance() === instance.methodOnPrototype(), // true
instance.methodOnPrototype() === instance // true
);
This allows us to arrive to the same result as using the special instance method syntax but with the currently available tools (ES2017 and under).
If for some reason we want a method that is always bound to something that is not an instance of the class, we can do that as well:
class A {
constructor() {
this.method = this.method.bind(console);
}
method() { return this; }
}
const instance = new A();
console.log(
instance.method() === console // true
);

An arrow function expression has a shorter syntax than a function
expression and does not have its own this, arguments, super, or
new.target. These function expressions are best suited for non-method
functions, and they cannot be used as constructors.
Arrow Functions lexically bind their context so this actually refers to the originating context.
In ES3/4 functions declaration you can use this by storing in some other variable.
const that = this;
onSubmit(e){
e.preventDefault();
const api_key = "***************";
const url = `http://api.giphy.com/v1/gifs/search?q=${that.state.term}&api_key=${api_key}`;
}

Also, how do I do it the other way? Say, if I want to use the same onSubmit function (ES6 class method) but want to handle this when I call it (in the form element), how do I do it ?
Using this.onSubmit.bind(this) ?
Yes you must bind the method to the component in the constructor. It's because the arrow functions get automatically binded to the class therefore the scope of this is set in the method. While onSubmit is a regular function that is not yet binded therefore the this inside the method will reference the function and not the component.

You need to use bind in your class's constructor with the ES6 class method. In essence, arrow functions do this for you automatically.
constructor(props) {
super(props);
this.onSubmit = this.onSubmit.bind(this);
}
The more important thing to note here is that I believe the arrow function here will be created on each instance of the class, where the ES6 class method will be made part of the class's prototype and shared amongst all instances.

The key difference is that in ES5 we don't have auto binding which means you have to bind your event handler function manually in order to play with state or props inside the function in react. But in ES6 it does auto binding. That's the key difference
ES5: you have to bind onSubmit preferably in constructor
//is valid
this.onSubmit = this.onSubmit.bind(this);
onSubmit(e){
e.preventDefault();
const api_key = "C1hha1quJAQZf2JUlK";
const url = `http://api.giphy.com/v1/gifs/search?q=${this.state.term}&api_key=${api_key}`;
}
ES6:
The below is valid because it does auto binding.
onChange = (e) => this.setState({term: e.target.value})

Related

Should we add methods only to the prototypes of our objects?

I'm currently studying Javascript and saw that we can define members of an object in its prototype instead of in the object's constructor since it makes the objects lighter because they don't carry the method code in every instance.
I can see how variables can work badly if all objects of this constructor point to the same variable field, but methods shouldn't change too much or at all, so its not a problem to share them.
Is there a reason not to define methods on the prototype rather than the object's constructor?
One reason why one might prefer putting methods on the instance itself would be to ensure the correct this context while adding a method concisely. Getting this to work properly is a very common problem in JavaScript.
For example:
class Foo {
i = 0;
clickHandler = () => console.log(this.i++);
}
const f = new Foo();
window.onclick = f.clickHandler;
This is a common pattern with class components in React. If one put the method on the prototype instead, it could sometimes get a bit uglier:
class Foo {
i = 0;
clickHandler() {
console.log(this.i++);
}
}
const f = new Foo();
window.onclick = () => f.clickHandler();
Another common way of dealing with this is to move a bound method from the prototype to the instance in the constructor:
class Foo {
i = 0;
constructor() {
this.clickHandler = this.clickHandler.bind(this);
}
clickHandler() {
console.log(this.i++);
}
}
const f = new Foo();
window.onclick = () => f.clickHandler();
Is that reason to prefer one over the other? That's up to you, but its one arguable advantage to keep in mind.
Just an idea from me - there is a way of dealing with the "proper" execution context of e.g. click handlers on prototype level as well. Consider the following example:
class AbstractClickHandler {
// the "proper" this is ensured via closure over the actual implementation
clickHandler() {
return (...args) => {
this.handleClick.apply(this, args)
}
}
// no-op in base class
handleClick() {
}
}
class ClickHandler extends AbstractClickHandler {
constructor(data) {
super()
this.data = data
}
// actual implementation of the handler logic
handleClick(arg1, arg2) {
console.log(`Click handled with data ${this.data} and arguments ${arg1}, ${arg2}`)
}
}
const instance = new ClickHandler(42)
const handler = instance.clickHandler()
handler.call(null, 'one', 'two')
This way, there is no need to define a bound handler in every concerned constructor.

ReactJs Events Binds [duplicate]

In new React ES6 classes this needs to be binded as stated here: http://facebook.github.io/react/blog/2015/01/27/react-v0.13.0-beta-1.html#autobinding
for eg:
class Counter extends React.Component {
constructor() {
super();
this.tick = this.tick.bind(this);
}
tick() {
...
}
...
}
The explanation for this is because it's the default behaviour, however if I make an ES6 class and then I make a new instance of it this will be binded
import React from 'React'
class Test extends React.Component {
constructor() {
super()
}
foo () {
console.log('bar')
}
hello() {
this.foo()
}
}
var test = new Test()
test.hello()
// > bar
Why binding is needed in React then?
You need set this to methods in case, for example, if you need pass function reference to event handlers, however you don't need set this for every method.,
class Counter extends React.Component {
constructor() {
super();
this.tick = this.tick.bind(this);
}
tick() {
// this refers to Counter
}
fn() {
// this refers to Counter
}
withoutBind() {
// this will be undefined or window it depends if you use "strict mode" or not
}
render() {
this.fn(); // this inside this method refers to Counter
// but if you will do like this
const fn = this.fn;
fn(); // this will refer to global scope
return <div>
<button onClick={this.tick}>1</button>
<button onClick={this.withoutBind}>2</button>
</div>;
}
}
Example
Classes in ES6 are functions. When you instantiate a class, you get an object. For all the methods in your class, if you used this inside them, it refers to the object that owns the method.
But it is confusing when you extract the method because the context changes. Examples:
let foo = Counter()
foo.tick()
If you call foo.tick(), this belongs to the object foo. Cool.
But watch this:
tickFunc = foo.tick()
tickFunc()
You detached the function from the original object, now the function invocation happens where this inside tickFunc() refers to the global object.
So why do you need to bind your methods in React? You do it because most of the times we are passing the method references either to the event handlers or as props to components. When you pass references of your methods, they become detached functions and their context changes. To solve that, you use bind() in the constructor.

What is a good way to automatically bind JS class methods?

I get tired of writing code like this:
class Something {
constructor() {
this.method = this.method.bind(this);
this.anotherOne = this.anotherOne.bind(this);
// ...
}
}
It's time consuming and it's easy to forget to bind a method. I aware of the class fields proposal, but it's still Stage 3 and seems to come with some issues.
My current solution (based on this answer) looks like this:
class Something {
constructor() {
// Get all defined class methods
const methods = Object.getOwnPropertyNames(Object.getPrototypeOf(this));
// Bind all methods
methods
.filter(method => (method !== 'constructor'))
.forEach((method) => { this[method] = this[method].bind(this); });
}
}
This seems to work, but I'm wondering if there is a better way, or if this method has issues that I'm not aware of.
Update: Why Do This?
The problem I have run into is that if I don't bind my class functions in the constructor, I have to remember to call them "properly" later. For example:
const classInstance = new Something();
// This fails for a non-obvious reason
someAction()
.then(classInstance.method);
// This works of course but looks like we created a useless function if you don't know better
someAction()
.then(result => classInstance.method(result));
Use fat arrow function in ES6 (generally called as arrow function)
anotherOne = ()=> {
...
}
Call like this onClick={this.anotherOne}; no need to bind in constuctor
From the ECMA spec
Any reference to arguments, super, this, or new.target within an
ArrowFunction must resolve to a binding in a lexically enclosing
environment. Typically this will be the Function Environment of an
immediately enclosing function.
It seems a bit late to answer this question but there is no accepted answer so I will try my best for people come here after me.
To automatically bind this for all methods, you can use "arrow" function:
class Something {
constructor() {
// Don't need to bind `this`
}
doSomething = () => {
console.log(this); // `this` will be pointed to the instance
}
}
const s = new Something();
s.doSomething(); // => `this` is pointed to `s`
Note: just make sure the classes extending this class Something will not use doSomething method in its constructor (eg: super.doSomething()), otherwise you will get an error.
To answer the updated question: if you don't manually bind this using .call() or .apply(), the value of this depends on the way that method is called
For example:
class Something {
constructor() {
// didn't bind `this` here
}
doSomething() {
console.log(this);
}
}
const s = new Something();
const funcA = s.doSomething;
const object = {
funcB: s.doSomething,
};
// these ways of calling `.doSomething()` result in different values of `this`:
funcA(); // => `this` is pointed to the global variable (`window` in browser environment)
window.funcA(); // => same as `funcA()`
s.doSomething(); // => `this` is pointed to `s`
object.funcB(); // =? `this` is pointed to `object`
Beside that, the implementation of .then() method would be similar like this:
class Promise {
// ...
then(callback) {
// code...
callback(); // notice how the `callback` is called - not as a method of an object
// code...
}
}
With your code example, the way you pass the callback into .then() method will affect the value of this inside the callback:
const classInstance = new Something();
// `this` inside `classInstance.method` is pointed to `this` inside the
// `.then` method, not the `classInstance`, as `classInstance.method()`
// will be called as `callback()`
someAction()
.then(classInstance.method);
// `this` inside `classInstance.method` is pointed to `classInstance`,
// as the way the anonymous "arrow" function is called does not affect the way
// `classInstance.method` is called, so `this`'s value is controlled by the way
// you call it inside the callback (anonymous "arrow" function) of `.then()`
// method.
someAction()
.then(result => classInstance.method(result));
You can also use auto-bind like this
From usage example:
const autoBind = require('auto-bind');
class Unicorn {
constructor(name) {
this.name = name;
autoBind(this);
}
message() {
return `${this.name} is awesome!`;
}
}
const unicorn = new Unicorn('Rainbow');
// Grab the method off the class instance
const message = unicorn.message;
// Still bound to the class instance
message();
//=> 'Rainbow is awesome!'
// Without `autoBind(this)`, the above would have resulted in
message();
//=> Error: Cannot read property 'name' of undefined
You can use an ES6 arrow function:
method = () => {
//Do stuff
}
As stated in the docs:
An arrow function expression is a syntactically compact alternative to a regular function expression, although without its own bindings to the this, arguments, super, or new.target keywords.
Class fields can be used to create functions automatically bound to the current instance.
class X {
fn = () => console.log(this.constructor.name)
}
const x = new X;
x.fn(); // regular call
const fn = x.fn;
fn(); // this is preserved
const fn2 = x.fn.bind(null);
fn2(); // cannot rebind this

In a React Component, what's the difference between foo(){} and bar = () => {} and when should I use which?

Babel is doing its magic, which makes me very confused about what's going on.
What's the difference between foo and bar in this react Component? And when should I use which?
class MyComponent extends Component {
foo() {
//...
}
bar = () => {
//...
}
}
(My own guess is foo is in the prototype, and bar is in the constructor? Anyways, I don't know what I'm talking about)
My own guess is foo is in the prototype, and bar is in the constructor?
That's exactly right.
foo() {}
in this context is a method declaration and the value gets assigned to the prototype. It's equivalent to
MyComponent.prototype.foo = function() {};
bar = ... ;
on the other hand is a class field. This is a shorthand notation for assigning properties to the instance in the constructor:
constructor() {
this.bar = ... ;
}
And because of how arrow functions work, using class fields with arrow functions basically lets you create "bound" methods.
More on arrow functions: Arrow function vs function declaration / expressions: Are they equivalent / exchangeable?
And when should I use which?
The tl;dr is: Use class field + arrow function when you need a bound function.
When do you need a bound function? Whenever you want this inside the function to refer to a specific value but you don't control how the function is called.
That's mostly the case for event handlers (i.e. functions that are passed to other functions / components) that need to access the component instance (e.g. call this.setState or access this.props/this.state).
You don't have to use it though, you could also bind the method when you need to. However, binding a method only once in the constructor is ideal for React components so that, if the method is passed as event handler, always the same function object is passed.
As noted in another answer, this is not related to React at all. Class fields are likely officially integrated into the language this year.
In the second example bar is an arrow function.
Until arrow functions, every new function defined its own this value.
For instance, this can be a new object in the case of a constructor.
function Person(age){
this.age=age;
console.log(this);
}
let person=new Person(22);
Or this can points to the base object if the function created can be accessed like obj.getAge().
let obj={
getAge:function(){
console.log(this);
return 22;
}
}
console.log(obj.getAge());
An arrow function does not create its own this, it's just used the this value of the enclosing execution context. In the other hand, arrow function uses this of parent scope.
foo is an instance method of class MyComponent. While bar is an instance property of class MyComponent (that happens to be assigned an anonymous function).
It might make more sense to see it used in the traditional sense...
class MyComponent {
// instance property
someProperty = 42;
// instance method
someMethod() {}
}
So why use an instance property instead of an instance method?
Instance methods in javascript must be called within the class scope to inherit the class context (this)...
class MyComponent {
// instance method
someMethod() {
const value = this.state.value // ERROR: `this` is not defined
}
render() {
return <div onClick={this.someMethod} />
}
}
However, since arrow functions inherit their scope, this will be available if you use an arrow function instead
class MyComponent {
// instance method
someProperty = () => {
const value = this.state.value // this works!
}
render() {
return <div onClick={this.someProperty} />
}
}
Essentially, there is no difference.
This has nothing to do with React, though.
Arrow functions
Arrow functions are a relatively new feature added to Javascript that give you a way of defining functions in a more concise way.
function foo (param1, param2, etc) {
// do something
}
// becomes
var foo = (param1, param2, etc) => {
// do something
}
If you have only 1 param, you don't need the parantheses:
function foo (param) {
// do something
}
// becomes
var foo = param => {
// do something
}
If there is only 1 expression, and the result is returned, you can omit the {} too!
function foo (param) {
returns param * 2
}
// becomes
var foo = param1 => param * 2
this is not not updated
In an arrow function, you don't get a new this - it is the same as in the parent block. This can be useful in some cases (when using setTimeout, for example)
JS Classes
In ES6 we can also use the class keyword to define "classes" in Javascript. They still use prototype, though!
function Something (foo) {
this.bar = foo;
}
Something.prototype.baz = function () {
return baz * 2
}
// becomes
class Something {
constructor(foo) {
this.bar = foo;
}
baz () {
return baz * 2
}
}
So the constructor is done in constructor() and all the following methods are added to the Prototype. It's nothing but syntactic sugar (but kinda does some of the Prototype heavy lifting for you)
You can use extends!
class SomethingElse extends Something {
constructor(foo, lol) {
this.lol = lol
// Use super() to call the parent's constructor
super(foo)
}
baz () {
return baz * 2
}
}

how to replace `bind(this)` in es6

I read some codes:
class XXXX {
init() {
MyOBJ.on('EVENTNAME', this.myfunction.bind(this)); // Line3, MyOBJ extends EventEmitter
}
}
Just curious how to use arrow function to replace Line3? Thanks
Function.prototype.bind creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.
What this specific example - this.myFunction.bind(this) - achieves, is to be able to pass a reference to a function (that happens to also be referenced by this.myFunction) while making sure that any calls to that function are done in the context of this.
In ES2015+ we can do this:
class XXXX {
init() {
MyOBJ.on('EVENTNAME', (event) => this.myfunction(event));
}
}
With ES2015 arrow functions this will inside the body be the declaration context of the arrow function. So in our case this.myFunction gets called in an arrow function whose context is the context of the call to init(), a.k.a. this within init.
The key difference is that now you actually create a call expression instead of just passing a reference to the function. This time the reference given to MyOBJ.on is the arrow function.
A strict ES5 equivalent to the snippet above would also use a function literal as callback given to MyOBJ.on:
class XXXX {
init() {
MyOBJ.on('EVENTNAME', function(event) {
this.myfunction(event));
}.bind(this));
}
}
Replace this
this.myfunction.bind(this)
to this
() => {}
Your event binding would look as follow:
class XXXX {
someMethod() {}
init() {
MyOBJ.on('EVENTNAME', () => {
this.someMethod(); // The originating context it's actually your class XXXX
});
}
}
Resource
Javascript ES6 — Arrow Functions and Lexical this
One of the most anticipated new features in the ES6 Javascript standard was the Arrow Function Expression. It promises a shorter syntax than it’s predecessor the Function Expression. In addition is how the new Arrow Function binds, or actually DOES NOT bind it’s own this. Arrow Functions lexically bind their context so this actually refers to the originating context.
Depending on how you add your function to your object, you can simply do:
MyOBJ.on('EVENTNAME', this.someMethod);
Using arrow functions like this will bind the methods to the instance of the class.
class XXXX {
constructor() {
this.init();
};
someMethod = () => {
console.log('someMethod() called');
};
init = () => {
MyOBJ.on('EVENTNAME', this.someMethod);
};
}
const x = new XXXX();
const MyOBJ = new EventEmitter();
MyOBJ.emit('EVENTNAME'); // someMethod() called
Note: using mocks to test for this.someMethod being called will likely fail if you use this method because the EventEmitter actually replaces the context of this inside someMethod when it's called. https://github.com/sinonjs/sinon/issues/1536 describes this issue.

Categories

Resources