Reputation: 816970
Arrow functions in ES2015 provide a more concise syntax.
Examples:
Constructor function
function User(name) {
this.name = name;
}
// vs
const User = name => {
this.name = name;
};
Prototype methods
User.prototype.getName = function() {
return this.name;
};
// vs
User.prototype.getName = () => this.name;
Object (literal) methods
const obj = {
getName: function() {
// ...
}
};
// vs
const obj = {
getName: () => {
// ...
}
};
Callbacks
setTimeout(function() {
// ...
}, 500);
// vs
setTimeout(() => {
// ...
}, 500);
Variadic functions
function sum() {
let args = [].slice.call(arguments);
// ...
}
// vs
const sum = (...args) => {
// ...
};
Upvotes: 837
Views: 266123
Reputation: 1095
Arrow functions => best ES6 feature so far. They are a tremendously powerful addition to ES6, that I use constantly.
Wait, you can't use arrow function everywhere in your code, its not going to work in all cases like this
where arrow functions are not usable. Without a doubt, the arrow function is a great addition it brings code simplicity.
But you can’t use an arrow function when a dynamic context is required: defining methods, create objects with constructors, get the target from this when handling events.
They do not have this
It uses “lexical scoping” to figure out what the value of “this
”
should be. In simple word lexical scoping it uses “this
” from the
inside the function’s body.
They do not have arguments
Arrow functions don’t have an arguments
object. But the same
functionality can be achieved using rest parameters.
let sum = (...args) => args.reduce((x, y) => x + y, 0);
sum(3, 3, 1) // output: 7
They cannot be used with new
Arrow functions can't be constructors because they do not have a prototype property.
map
, reduce
, or forEach
.Upvotes: 52
Reputation: 623
They are not always equivalent. Here's a case where you cannot simply use arrow functions instead of regular functions.
Arrow functions CANNOT be used as constructors
TLDR:
This is because of how Arrow Functions use the this keyword. JS will simply throw an error if it sees an arrow function being invoked as a "constructor". Use regular functions to fix the error.
Longer explanation:
This is because objects "constructors" rely on the this keyword to be able to be modified.
Generally, the this keyword always references the global object. (In the browser it is the window object).
BUT, when you do something like:
function personCreator(name) {
this.name = name;
}
const person1 = new personCreator('John');
The new keyword do some of its magic and makes the this keyword that is inside of personCreator to be initially an empty object instead of referencing the global object. After that, a new property called name is created inside that empty this object, and its value will be 'John'. At the end, the this object is returned.
As we see, the new keyword changed the value of this from referencing the global object to now be an empty object {}.
Arrow functions do not allow their this object to be modified. Their this object is always the one from the scope where they were statically created. This is called Static Lexical Scope. That is why you cannot do operations like bind, apply, or call with arrow functions. Simply, their this is locked to the value of the this of the scope were they were created. This is by design.
And because of this :D, arrow functions cannot be used as "constructors".
Side Note:
A lexical scope is just the area where a function is created. For example:
function personCreator(name) {
this.name = name;
const foo = () => {
const bar = () => {
console.log(this); // Output: { name: 'John' }
}
console.log(this); // Output: { name: 'John' }
bar();
}
foo();
}
const person1 = new personCreator('John');
The lexical scope of bar is everything that is within foo. So, the this value of bar is the one that foo has, which is the one of personCreator.
Upvotes: 3
Reputation: 816970
tl;dr: No! Arrow functions and function declarations / expressions are not equivalent and cannot be replaced blindly.
If the function you want to replace does not use this
, arguments
and is not called with new
, then yes.
As so often: it depends. Arrow functions have different behavior than function declarations / expressions, so let's have a look at the differences first:
1. Lexical this
and arguments
Arrow functions don't have their own this
or arguments
binding. Instead, those identifiers are resolved in the lexical scope like any other variable. That means that inside an arrow function, this
and arguments
refer to the values of this
and arguments
in the environment the arrow function is defined in (i.e. "outside" the arrow function):
// Example using a function expression
function createObject() {
console.log('Inside `createObject`:', this.foo);
return {
foo: 42,
bar: function() {
console.log('Inside `bar`:', this.foo);
},
};
}
createObject.call({foo: 21}).bar(); // override `this` inside createObject
// Example using a arrow function
function createObject() {
console.log('Inside `createObject`:', this.foo);
return {
foo: 42,
bar: () => console.log('Inside `bar`:', this.foo),
};
}
createObject.call({foo: 21}).bar(); // override `this` inside createObject
In the function expression case, this
refers to the object that was created inside the createObject
. In the arrow function case, this
refers to this
of createObject
itself.
This makes arrow functions useful if you need to access the this
of the current environment:
// currently common pattern
var that = this;
getData(function(data) {
that.data = data;
});
// better alternative with arrow functions
getData(data => {
this.data = data;
});
Note that this also means that is not possible to set an arrow function's this
with .bind
or .call
.
If you are not very familiar with this
, consider reading
2. Arrow functions cannot be called with new
ES2015 distinguishes between functions that are callable and functions that are constructable. If a function is constructable, it can be called with new
, i.e. new User()
. If a function is callable, it can be called without new
(i.e. normal function call).
Functions created through function declarations / expressions are both constructable and callable.
Arrow functions (and methods) are only callable.
class
constructors are only constructable.
If you are trying to call a non-callable function or to construct a non-constructable function, you will get a runtime error.
Knowing this, we can state the following.
Replaceable:
this
or arguments
..bind(this)
Not replaceable:
this
)arguments
(see below))function*
notationLets have a closer look at this using your examples:
Constructor function
This won't work because arrow functions cannot be called with new
. Keep using a function declaration / expression or use class
.
Prototype methods
Most likely not, because prototype methods usually use this
to access the instance. If they don't use this
, then you can replace it. However, if you primarily care for concise syntax, use class
with its concise method syntax:
class User {
constructor(name) {
this.name = name;
}
getName() {
return this.name;
}
}
Object methods
Similarly for methods in an object literal. If the method wants to reference the object itself via this
, keep using function expressions, or use the new method syntax:
const obj = {
getName() {
// ...
},
};
Callbacks
It depends. You should definitely replace it if you are aliasing the outer this
or are using .bind(this)
:
// old
setTimeout(function() {
// ...
}.bind(this), 500);
// new
setTimeout(() => {
// ...
}, 500);
But: If the code which calls the callback explicitly sets this
to a specific value, as is often the case with event handlers, especially with jQuery, and the callback uses this
(or arguments
), you cannot use an arrow function!
Variadic functions
Since arrow functions don't have their own arguments
, you cannot simply replace them with an arrow function. However, ES2015 introduces an alternative to using arguments
: the rest parameter.
// old
function sum() {
let args = [].slice.call(arguments);
// ...
}
// new
const sum = (...args) => {
// ...
};
Related question:
Further resources:
Upvotes: 1132