ES6 arrow functions and arguments
With the recent advancement in JavaScript, which is ES6 or ECMA Standard Level 6, there are few changes introduced to the JavaScript language and one of it is the Fat Arrow Functions. These are a more concise syntax for writing function expressions. They utilize a new token, =>
, that looks like a fat arrow. Arrow functions are anonymous and change the way this
binds in functions.
Contents
What are fat arrow functions?
Arrow functions make our code more concise, and simplify function scoping and the this keyword. They are one-line mini functions which work much like Lambdas in other languages like C# or Python. (See also lambdas in JavaScript). By using arrow functions, we avoid having to type the function
keyword, return
keyword (it’s implicit in arrow functions), and curly brackets.
Difference with the traditional functions?
We can see a great difference between the traditional functions that use the function
keyword vs. the new ones. Let's consider the following code uses the traditional style.
var add = function (a, b) {
return (a + b);
};
Now the whole content of say about 50 characters is compressed into the new ones, which is just 25ish characters:
let add = (a, b) => (a + b);
This is about 50% reduction in size, which is great, but thinking about the cleanliness it boasts, it has some dangers too:
- How do you send the parameters here?
- What about the context of
this
? - What about scoping inside the function?
Specification?
From the official ECMA 262 specification page, it has described in the Runtime Semantics: Evaluation:
ArrowFunction: ArrowParameters =>
ConciseBody
- If the function code for this ArrowFunction is strict mode code (10.2.1), let strict be
true
. Otherwise let strict befalse
. - Let
scope
be the LexicalEnvironment of the running execution context. - Let
parameters
beCoveredFormalsList
of ArrowParameters. - Let closure be FunctionCreate(
Arrow
,parameters
,ConciseBody
,scope
,strict
). - Return closure.
An ArrowFunction does not define local bindings for
arguments
,super
,this
, ornew.target
. Any reference toarguments
,super
,this
, ornew.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. Even though an ArrowFunction may contain references tosuper
, the function object created in step 4 is not made into a method by performing MakeMethod. An ArrowFunction that referencessuper
is always contained within a non-ArrowFunction and the necessary state to implementsuper
is accessible via the scope that is captured by the function object of the ArrowFunction.
Users' Choice
Arrow functions were introduced with ES6 as a new syntax for writing JavaScript functions. They save developers time and simplify function scope. Surveys show they’re the most popular ES6 feature:
Problem
The problem now is that there seems to be no way you can send any arguments to the functions defined this way. This means, at the prototype level, the arguments
is not defined in the function.
(() => console.log(arguments))(1, 2, 3);
On Google Chrome, when the above is executed on Developer Tools JavaScript Console, we get this.
That's what the recent versions of Chrome, Firefox, and Babel says. The above syntax is "invalid" as arrow functions should be using their parent scope for arguments. The only info I've been able to find that contradicts this is a single comment saying this was rejected by TC39, but I can't find anything to back this up in the official docs or specification.
Solution
Arrow function expressions evaluate to functions that have their [[ThisMode]] set to lexical
, and when such are called the declaration instantiation does not create an arguments
object. There is even a specifc note (18 a) stating that "Arrow functions never have an arguments objects.".
However, if we do want to capture the args
for your arrow function, we can simply use a rest parameter (Babel • MDN).
The same above code can be restructured this way:
((...arguments) => console.log(arguments))(1, 2, 3);
And here we go!
We have a clear winner now! Also, a few notes regarding the above use. Rest parameters can be combined with other positional parameters, but must always be included as the last parameter. Another example showing both positional and rest parameters can be seen here.
((a, b, c, ...rest) => console.log (a, b, c, rest))("Apple", "Ball", "Cat", 1, 2, 3);
With Google Chrome's Dev Tools, we get this:
But at the same time, the good part of JavaScript interpreters are the way they give the error messages. Say, you put the Rest parameters in the middle or at the front, this is what the code looks like.
((...rest, a, b, c) => console.log (a, b, c, rest))(1, 2, 3, "Apple", "Ball", "Cat");
And with our all good Chrome Console, we get an Uncaught SyntaxError
:
Summary
Now we now know what could be the best way to add the arguments to fat arrow functions. We also know now how to go around the official specs (I mean, thinking out of box) and achieve what we are expecting. I really hope this has helped someone who's struggling with this concept.