Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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) ?

like image 721
d_bhatnagar Avatar asked Feb 22 '18 05:02

d_bhatnagar


People also ask

When should you not use arrow functions in ES6?

Arrow functions in ES6 have at least two limitations: Don't work with new and cannot be used when creating prototype. Fixed this bound to scope at initialisation.

What does => mean in ES6?

It's a new feature that introduced in ES6 and is called arrow function. The left part denotes the input of a function and the right part the output of that function.

Is this arrow function of ES6 same as function of es5?

There is a noticeable difference between the syntax of es5 functions and es6 arrow functions. //es6 arrow functions const printHelloWorld = () => console. log('Hello World'); In the above syntax we can put the curly braces {} after the token=>.

What is arrow function in ES6?

Arrow functions were introduced in ES6. Arrow functions allow us to write shorter function syntax: let myFunction = (a, b) => a * b; Try it Yourself »


2 Answers

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}`;
  }
like image 40
void Avatar answered Oct 30 '22 22:10

void


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
);
like image 170
nem035 Avatar answered Oct 31 '22 00:10

nem035