What is the `Arguments` object?
| 5 minute read
At my day job we have been slowly linting a legacy project. One of the rules that ESLint enforces is prefer-rest-params.
This is a pretty reasonable rule. But we ran into some issues in some of the legacy code which resulted in a little research on my part. Here's what I found.
Arguments
object?
The arguments
is an iterable object provided inside all (non-arrow) function. It is a catch-all for anything that was passed to your function. It has a length
, but doesn't have the usual Array.prototype
methods you might expect it to have.
What this means in practice is you will have to find other ways to interact with arguments
.
Prior to ES6 and ...rest
parameters you might have used arguments
in a situation where you don't know what was going to be sent to the function.
Aside: There are some cases where I might consider this a code smell.. If you don't know what is going to be passed to your function you might want to look into it. On the other hand, sometimes the API you are working with is out of your control. 🤷♂️
For example:
const argumentsFn = function() {
console.log(arguments);
console.log(arguments.length);
console.log(arguments[1]);
console.log(arguments[2]);
// ... how many more?
}
argumentsFn('hello', 'there');
// The arguments object:
=> Arguments(2) ["hello", "there", callee: ƒ, Symbol(Symbol.iterator): ƒ]
// The length:
=> 2
// The first two arguments that were passed to the fn:
=> hello
=> there
If you wanted to then loop over the arguments
you would have to do something like this:
const argumentsFn = function() {
for(i = 0, i < arguments.length, i += 1) {
console.log(arguments[i]);
}
}
argumentsFn('hello', 'there');
=> hello
=> there
Now, what if you wanted to use filter
, reduce
, findIndex
or any of the other iteration methods Array.prototype
provides? You'll have to change arguments
into an Array
first:
const argumentsFn = function() {
Object.keys(arguments)
.forEach(key => console.log(arguments[key]));
}
argumentsFn('hello', 'there');
=> hello
=> there
...rest
to the rescue
With ...rest
parameters we can avoid the custom transformation (and appease ESLint!).
const argumentsFn = function(...args) {
console.log(args);
}
argumentsFn('hello', 'there');
// Our arguments in Array form:
=> ["hello", "there"]
But you can also name the first argument and catch everything else in a ...rest
Array:
const argumentsFn = function(greeting, ...args) {
console.log(greeting);
console.log(args);
}
argumentsFn('hello', 'there', 'human');
// The first named argument:
=> hello
// Everything that follows the first argument:
=> ["there", "human"]
Arrow Functions
If you needed another reason to start using ...rest
params over arguments
, arguments
is not available inside an arrow function.
const argumentsFn = (greeting, ...args) => {
console.log(arguments);
}
argumentsFn('hello', 'there');
=> Uncaught ReferenceError: arguments is not defined
Bonus Round
This is more of a fun fact than a useful tip. There is literally nothing stopping you from passing a millions of arguments to a function.
I'm not sure where you might want to use this knowledge in a real-world situation, but ...rest
params can handle it for you!
const argumentsFn = (...args) => {
args.forEach(arg => console.log(arg));
}
argumentsFn('a', 'l', 'l', 't', 'h', 'e', 't', 'h', 'i', 'n', 'g', 's');
=> a
=> l
=> l
=> t
=> h
=> e
=> t
=> h
=> i
=> n
=> g
=> s