What is unique about function arguments in JavaScript?

In one of my previous posts while mentioning about the support for default values for the function parameters, I had hinted that JavaScript has a unique approach to function arguments. In this post, let us explore that.

Let us start with the greet function as follows.

function greet(person, message){
    console.log(message + ", " + person);
}

greet("Ravi", "Hello");

We have defined the function greet with two parameters viz person and message. We also invoke the function in the last line of the above code, which produces the following output.

Hello, Ravi

Everything is good until now. Let's try to invoke the function as follows.

greet("Ravi", "Hello", "Good morning");
greet("Ravi");

We are trying to invoke the function with three and one arguments, respectively. Take a moment to think about what the output would be. Once you are ready, look at the following answer.

Hello, Ravi
undefined, Ravi

Interestingly, the JavaScript compiler didn't complain about the mismatch in the number of arguments. The additional arguments were ignored, and the missing arguments were assumed to be undefined as their values. Based on this observation, it is clear that JavaScript uses only the function's name to look up and not the number of arguments.

Let's add an additional log statement to help you understand how JavaScript handles arguments clearly.

function greet(person, message){
    console.log(arguments);
    console.log(message + ", " + person);
}

greet("Ravi", "Hello");
greet("Ravi", "Hello", "Good morning");
greet("Ravi");

This produces the following output.

[Arguments] { '0': 'Ravi', '1': 'Hello' }
Hello, Ravi
[Arguments] { '0': 'Ravi', '1': 'Hello', '2': 'Good morning' }
Hello, Ravi
[Arguments] { '0': 'Ravi' }
undefined, Ravi

It is evident from the above output that JavaScript captures the argument in a variable of type object called arguments. The keys are the positions of the arguments, and the values are the respective arguments passed by the function caller. Also, if you have specified the function parameters explicitly, those variables will receive their respective values during the function invocation based on their positions.

Let us attempt to rewrite the above function without using the named parameters.

function greet(){
    console.log(arguments);
    console.log(arguments[1] + ", " + arguments[0]);
}

greet("Ravi", "Hello");
greet("Ravi", "Hello", "Good morning");
greet("Ravi");

As expected, the output will be as follows.

[Arguments] { '0': 'Ravi', '1': 'Hello' }
Hello, Ravi
[Arguments] { '0': 'Ravi', '1': 'Hello', '2': 'Good morning' }
Hello, Ravi
[Arguments] { '0': 'Ravi' }
undefined, Ravi

Thus, even though JavaScript captures the function arguments as an object, it offers the convenience of using the named parameters for more readable code.

While we have finished discussing the uniqueness of argument passing in JavaScript, I want to touch on another aspect. ES2015 (formerly known as ES6) introduced the support for default values for function parameters. If we wish to assign a default value to the parameter message in the above function, it is possible from JavaScript version ES2015 onwards.

function greet(person, message = 'Hello'){
    console.log(message + ", " + person);
}
greet("Ravi", "Hello");
greet("Ravi", "Hello", "Good morning");
greet("Ravi");

Output:

Hello, Ravi
Hello, Ravi
Hello, Ravi

Transpiling the above ES2015 code to ES5 would produce the following code showing the absence of support for default parameter values.

"use strict";

function greet(person) {
  var message = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'Hello';
  console.log(message + ", " + person);
}

Well, JavaScript never ceases to amaze developers :)

From ES2015 onwards, we can also use the spread(...) operator to accept arguments as an array.

function print(...numbers){
    console.log(numbers);
}
print(1,2);
print(1,2,3);

Several other languages support capturing multiple arguments into a single variable. Unlike JavaScript, here we need to convey that using some special syntax explicitly. Below are some examples.

Python

def display(*numbers):
	print(numbers)

display(1,2,3)

Ruby

def display(*numbers)
	print numbers
end

display 1, 2, 3

Java

void display(int ...numbers){
	System.out.println(numbers);
}

display(1, 2, 3);