What advantage is there for using the JavaScript arrow syntax for a method in a constructor?
TL;DR
The main advantage of using an arrow function as a method inside a constructor is that the value of this
gets set at the time of the function creation and can't change after that. When the constructor is used to create a new object, this
will always refer to that object.
For example, let's say we have a Person
constructor that takes a first name as an argument has two methods to console.log()
that name, one as a regular function and one as an arrow function:
const Person = function (name) {this.name = name;this.sayName1 = function () {console.log(this.name);};this.sayName2 = () => {console.log(this.name);};};const john = new Person('John');const dave = new Person('Dave');john.sayName1(); // Johnjohn.sayName2(); // John// The regular function can have its `this` value changed, but the arrow function cannotjohn.sayName1.call(dave); // Dave (because `this` is now the dave object)john.sayName2.call(dave); // Johnjohn.sayName1.apply(dave); // Dave (because `this` is now the dave object)john.sayName2.apply(dave); // Johnjohn.sayName1.bind(dave)(); // Dave (because `this` is now the dave object)john.sayName2.bind(dave)(); // Johnconst sayNameFromWindow1 = john.sayName1;sayNameFromWindow1(); // undefined (because `this` is now the window object)const sayNameFromWindow2 = john.sayName2;sayNameFromWindow2(); // John
The main takeaway here is that this
can be changed for a normal function, but this
always stays the same for an arrow function. So even if you are passing around your arrow function to different parts of your application, you wouldn't have to worry about the value of this
changing.
Arrow functions
Arrow functions are introduced in ES2015 and it provides a concise way to write functions in Javascript. One of the key features of arrow function is that it lexically bind the this
value, which means that it takes the this
value from enclosing scope.
Syntax
Arrow functions use the =>
syntax instead of the function keyword. The basic syntax is:
const myFunction = (arg1, arg2, ...argN) => {// function body};
If the function body has only one expression, you can omit the curly braces and the return keyword:
const myFunction = (arg1, arg2, ...argN) => expression;
Examples
// Arrow function with parametersconst multiply = (x, y) => x * y;// Arrow function with no parametersconst sayHello = () => 'Hello, World!';
Advantages
- Concise: Arrow functions provide a more concise syntax, especially for short functions.
- Implicit return: They have an implicit return for single-line functions.
- Value of
this
is predictable: Arrow functions lexically bind thethis
value, inheriting it from the enclosing scope.
Limitations
Arrow functions cannot be used as constructors and will throw an error when used with the new
keyword.
const Foo = () => {};const foo = new Foo(); // TypeError: Foo is not a constructor
They also do not have arguments
keyword; the arguments have to be obtained from using the rest operator (...
) in the arguments.
const arrowFunction = (...args) => {console.log(arguments); // Throws a TypeErrorconsole.log(args); // [1, 2, 3]};arrowFunction(1, 2, 3);
Since arrow functions do not have their own this
, they are not suitable for defining methods in an object. Traditional function expressions or function declarations should be used instead.
const obj = {value: 42,getValue: () => this.value, // `this` does not refer to `obj`};console.log(obj.getValue()); // undefined
Why arrow functions are useful
One of the most notable features of arrow functions is their behavior with this
. Unlike regular functions, arrow functions do not have their own this
. Instead, they inherit this
from the parent scope at the time they are defined. This makes arrow functions particularly useful for scenarios like event handlers, callbacks, and methods in classes.
Arrow functions inside function constructors
const Person = function (name) {this.name = name;this.sayName1 = function () {console.log(this.name);};this.sayName2 = () => {console.log(this.name);};};const john = new Person('John');const dave = new Person('Dave');john.sayName1(); // Johnjohn.sayName2(); // John// The regular function can have its `this` value changed, but the arrow function cannotjohn.sayName1.call(dave); // Dave (because `this` is now the dave object)john.sayName2.call(dave); // Johnjohn.sayName1.apply(dave); // Dave (because `this` is now the dave object)john.sayName2.apply(dave); // Johnjohn.sayName1.bind(dave)(); // Dave (because `this` is now the dave object)john.sayName2.bind(dave)(); // Johnconst sayNameFromWindow1 = john.sayName1;sayNameFromWindow1(); // undefined (because `this` is now the window object)const sayNameFromWindow2 = john.sayName2;sayNameFromWindow2(); // John
Arrow functions in event handlers
const button = document.getElementById('myButton');button.addEventListener('click', function () {console.log(this); // Output: Buttonconsole.log(this === button); // Output: true});button.addEventListener('click', () => {console.log(this); // Output: Windowconsole.log(this === window); // Output: true});
This can be particularly helpful in React class components. If you define a class method for something such as a click handler using a normal function, and then you pass that click handler down into a child component as a prop, you will need to also bind this
in the constructor of the parent component. If you instead use an arrow function, there is no need to bind this
, as the method will automatically get its this
value from its enclosing lexical context. See this article for an excellent demonstration and sample code.