What are the differences (if any) between ES6 arrow functions and functions bound with Function.prototype.bind?
Solution 1:
There are no (significant) differences.
Well, okay, that's a little premature. There are three tiny differences unique to arrow functions.
-
Arrow functions cannot be used with
new
.This means, of course, that they do not have a
prototype
property and cannot be used to create an object with the classically-inspired syntax.new (() => {}) // TypeError: () => {} is not a constructor
This is probably for the best, though—the way
new
works would not make much sense with bound functions. -
Arrow functions do not have access to the special
arguments
object that ordinary JavaScript functions have access to.(() => arguments)(1, 2, 3) // ReferenceError: arguments is not defined
This one is probably a little bit more of a gotcha. Presumably this is to remove one of JavaScript's other oddities. The
arguments
object is its own special beast, and it has strange behavior, so it's not surprising that it was tossed.Instead, ES6 has splats that can accomplish the same thing without any magic hidden variables:
((...args) => args)(1, 2, 3) // [1, 2, 3]
-
Arrow functions do not have their own
new.target
property, they use thenew.target
of their enclosing function, if it exists.This is consistent with the other changes to remove "magically" introduced values for arrow functions. This particular change is especially obvious, considering arrow functions can't be used with
new
anyway, as mentioned above.
Otherwise, arrows are just like bound functions, semantically. It's possible for arrows to be more performant, since they don't have to carry around the extra baggage and since they don't need to be converted from ordinary functions first, but they're behaviorally exactly the same.
Solution 2:
There are a few differences:
-
Arrow functions cannot be constructed. While both arrow functions and bound functions both don't have a
.prototype
property, the former do throw an exception when called withnew
while the latter just ignore the bound value and call their target function as a constructor (with the partially applied bound arguments, though) on the new instance.function F() {} var f = () => {}, boundF = F.bind({}); console.log(new boundF(), new boundF instanceof F) // {}, true console.log(new f) // TypeError
Arrow functions do have lexical
arguments
,new.target
andsuper
as well (not only lexicalthis
). A call to an arrow function does not initialise any of those, they are just inherited from the function the arrow function was defined in. In a bound function, they just refer to the respective values of the target function.-
Arrow functions don't actually bind a
this
value. Rather, they don't have one, and when you usethis
it is looked up like a variable name in the lexical scope. This does allow you to lazily define an arrow function whilethis
is not yet available:class X extends Object { constructor() { var f = () => this, // works boundF = function(){ return this; }.bind(this); // ^^^^ ReferenceError super(); // initialises `this` console.log(f(), f() == this); // {}, true } } new X;
Arrow functions cannot be generator functions (though they can return generators). You can use
.bind()
on a generator function, yet there is no way to express this using an arrow function.
Solution 3:
Here is one more subtle difference:
Arrow functions can return a value without using the 'return' keyword, by omitting the {} braces following the => immediately.
var f=x=>x; console.log(f(3)); // 3
var g=x=>{x}; console.log(g(3)); // undefined
var h=function(x){x}; console.log(h(3)); // undefined
var i=x=>{a:1}; console.log(i(3)); // undefined
var j=x=>({a:1}); console.log(j(3)); // {a:1}