Javascript Function Parameters and Arguments Explained
JavaScript Function Signatures
Alright, you’ve probably seen functions before. Something like this maybe?
function doSomething(name, desc, task) {
// did something
}
This function, named doSomething
, takes three parameters
as part of its function signature. These parameters named name
, desc
, and task
are the names of the values being sent into the function.
I’m going to use a sportsball reference here so be warned. You can think of the function as a net that gets various types of objects tossed into the hoop ( )
where it triggers sports points.
let points = 0;
function basket(ball) {
// +3 points to Gryffindor!
points += 3;
}
JavaScript functions may have no parameters, one parameter, or pretty much as many as you want to define on the function.
Using sportsball again, like a basketball hoop that is meant to take just one parameter, or like skee-ball where you just toss in as many objects as you can.
What is a parameter you may or may not be asking yourself?
A parameter is simply the name used to represent the value being passed in. Kind of like a variable that’s defined for you as part of the function itself.
Refactor Rob: If a function takes three or more parameters it is worth evaluating whether the function should be refactored to use an object with expected properties instead. Skee-ball functions bad m’kay.
Calling JavaScript Functions
Now, here is where JavaScript goes off the rails compared to many other languages.
Just because JavaScript functions define parameters in the function signature does not mean that the parameters are required when the function is called.
Wat.
Using the doSomething
example from earlier there were three parameters defined.
All three are required to make the function call, right? Seems reasonable.
Nope, the following function calls are all perfectly valid. Not saying the results would be valid, but the function calls are.
// Eh, one should do it
doSomething('tots valid call');
// Not even pretending to try here
doSomething();
Not only can you pass less than the parameters defined in the function signature, but you can actually pass more values than the function expects too!
let name, desc, task, bogo;
// Three was a crowd, but four is a PARTY ... parameter party!
doSomething(name, desc, task, bogo);
At this point you’re starting to realize something about JavaScript function signatures…
OK, so since JavaScript violates the laws of the function pyhsics (that’s a thing right?) how can we be sure that anything is getting passed to our functions when we need it?
Besides inspecting the parameter values directly there is a neat but mostly unknown object called arguments
.
You know, not to be confused with the actual concept of parameters and arguments, but an actual object available within a function.
We’ll hold off on dealing with our arguments
in a minute. First we need to see how to protect ourselves from this parameter shenanigans.
JavaScript Function Parameter Defences
To lighten the ol’ cogntive load let’s look back at the doSomething
function:
function doSomething(name, desc, task) {
// did something
}
Now that we know that we can’t trust any of these parameters we need to look at how we can do some defensive programming.
I won’t dive too deeply into what parameter types can or cannot be. All parameters values share the following rules in JavaScript.
- not passing a value means the paramter is
undefined
- argument values, when provided, are either
null
or notnull
The simple way to combat this situation is an equality check.
function doSomething(name, desc, task) {
// First parameter check
if (name == null) {
// Who are you?
return;
}
}
Notice, the quality check is not ===
because null
does equal the type undefined
, but null
is NOT the same type as undefined
.
Don’t believe me? Fair. Try this in the console:
console.log(typeof null);
//object
console.log(typeof undefined);
//undefined
Now, what if some dilligent refactorer comes along blindingly passing out extra equality =
to every ==
check without actually verifying if the change breaks something.
Refactor Rob: Yes, this does happen. A “friend” of mine, who will remain anonoymous, told me he’s done this before, and he’s very sorry. He hopes you forgive
mehim.
So, if we do a name == null
equality check this will tell us two things.
- if the value wasn’t provided
- if the value is
null
But what if the value of name
was NaN
or an empty string ''
? We’d have to check if the value was null
, undefined
, NaN
, or an empty string too.
Here is another thing about Javascript. JavaScript logic checks are like a politician’s statement. They may have different definitions of truth in them.
In JavaScript you have either truthy
or falsy
values.
:smh:
Right, so, JavaScript variables may or may NOT be what we think they are.
To help us figure out the quantum conundrum of JavaScript values there is an even simpler approach to defending against this madness.
JavaScript Truthy and Falsy Evaluations
Behold, the magic of variable truthy
or falsy
checks!
function doSomething(name, desc, task) {
// Is name a thing or not?
if (!name) {
// You're not what I was expecting.
return;
}
}
What magic is this?
This, my internet friends, is the magic of evaluating the truthy
or falsy
-ness of a JavaScript variable’s value.
What this did under the covers was to check if the name
parameter’s value was any of the following falsy values:
- null
- undefined
- ''
- NaN
- false
- 0
If the name
parameter’s value was equal to any of these values the name
value would be considered falsy
.
What !name
does is inverts the logic and checks if name is NOT a truthy value.
The way to think about this is like this:
if (!name) {
// name is a falsy value
}
if (name) {
// name is a truthy value
}
At this point we now know how to defend against missing parameter values, or at least how to evaluate them enough to know if they’re missing when we require them.
But what if in the future we refactor doSomething
from requiring three parameters down to two?
Before:
function doSomething(name, desc, task) {
}
After:
function doSomething(name, task) {
}
We know that JavaScript doesn’t care how many values you provide in the function call, right? Function signatures are just a guideline remember?
Don’t worry, yes, there is a way to handle this if you cannot refactor ALL the function calls in your application to downsize the parameters.
This, my confused friends, is where the JavaScript function arguments
comes in.
JavaScript arguments
Object
If you haven’t read anything up to this point, we’ve just had an intrepid developer refactor our doSomething
function, reducing the required parameters from 3 to 2.
function doSomething(name, task) {
}
Me: “Great job! All the references have been updated right?”
Me2: “Well, it’s possible not all the references were found. It is JavaScript after all…”
Me: “Ugh, OK, let’s patch the function to support the previous signature. Also, you’re fired.”
Me2: “OK… I deserved that.”
OK, now we need to be backwards compatible with the previous function signature that took three parameters.
To do that, JavaScript has a nifty object that is scoped to the function called arguments
.
Check Number of Parameters Passed to Function
To support backwards compability (or simply to satify curiosity), we can quickly check how many parameter values were sent in a function call. Let’s take a look at an example.
function lights(one, two, three) {
const count = arguments.length;
console.log(count);
}
lights(1, 2, 3, 4);
What would the console output?
Picard knows. Let’s ask him.
Exactly Picard. Our function only expected three parameters, but when called it had four values sent to it. The arguments
object contains references to the values that were sent to the function in the order they were sent.
This is incredibly useful.
Let’s take a look at what the arguments
object looks like when we call our lights
. Engage.
function lights(one, two, three) {
console.log(JSON.stringify(arguments));
}
lights(1, 2, 3, 4);
// {"0":1,"1":2,"2":3,"3":4}
What happens if I change the value of one
within the function? Does it change the arguments[0]
value?
Yes, yes it does.
Changing arguments[0]
would also change the value of the one
parameter. The arguments
holds references to the values not a copy of the data.
function lights(one, two, three) {
one = 8;
console.log(JSON.stringify(arguments));
}
//{"0":8,"1":2,"2":3,"3":4}
function lights(one, two, three) {
arguments[0] = 8;
console.log(JSON.stringify(one));
}
// 8
I think you get the point.
With this new found programmatic alchemy how would we fix Mr. Refactors backwards compability issue with the doSomething
function?
function doSomething(name, desc, task) {
}
Well, one option would be to use the arguments
, check the number of values sent, and if we received 3 values it’s likely the old function signature being used.
function doSomething(name, task) {
// old function signature detected - most likely
if (arguments.length === 3) {
// we didn't need a description to do something
task = arguments[2]
}
// keep calm, and carry on with doing something
}
// Missed reference call somewhere in the code...
doSomething('chores', 'Things I should be doing.', () => {});
Now, there is a danger in relying on arguments
. Especially with Captain Refactor roaming free.
Our mystery JavaScript refactorer just recently finished reading about arrow functions, and now the world points towards refactoring ALL the functions.
We come back from lunch to find that doSomething
has been updated recently to an arrow function variable.
const doSomething = (name, task) => {
// old function signature detected - most likely
if (arguments.length === 3) {
// guess we didn't need a description anyway
task = arguments[2]
}
// carry on with doing something
}
doSomething('Chores', 'Things I should be doing.', () => {});
Still works, right? No. No, it does not…
The arguments
object does not exist with arrow functions.
Refactor Rob: Before refactoring make sure to create unit tests that will verify existing behavior. This is especially important because any change could lead to breaking existing behavioral contracts.
On that note. I hope you enjoyed my post on JavaScript functions and arguments explained.
Later topics will touch on this
, that, and other programming related topics.