More Useful JavaScript Function Patterns - Multiple Arguments
The Mastering the Arcane Art of JavaScript-mancy series are my humble attempt at bringing my love for JavaScript to all other C# developers that haven’t yet discovered how awesome this language and its whole ecosystem are. These articles are excerpts of the super duper awesome JavaScript-Mancy book a compendium of all things JavaScript for C# developers.
Time for some more useful function patterns in JavaScript!
In this article I’ll show you how to handle multiple parameters - the equivalent to params
in C# - in the current version on JavaScript (ES5) and in the upcoming ECMAScript 2015 that brings great support for multiple parameters with the rest parameter syntax.
A Pattern For Using Multiple Arguments In Functions Today
You can experiment with all these examples in examples in JsFiddle.
JavaScript gives you a lot of freedom when it comes to handling function parameters and arguments. For instance, you can declare a function with a specific number of parameters and call it without arguments:
function heal(person, inchantation, modifier) {
var healedHp
modifier = modifier || 1
healedHp = modifier * 100
console.log(
'you heal ' +
person +
' with ' +
inchantation +
' spell (+' +
healedHp +
'hp)'
)
person.hp += healedHp
}
try {
heal()
} catch (e) {
console.log(e)
}
// => you heal undefined with undefined spell (+100hp)
// => typeError: cannot read property hp of undefined
Or with as many arguments you want:
// or with as many arguments as you want
var JonSnow = {
name: 'Jon Snow',
hp: 5,
toString: function() {
return this.name
},
}
heal(JonSnow)
// => you heal Jon Snow with undefined spell (+100hp)
heal(JonSnow, 'cure')
// => you heal Jon Snow with cure spell (+100hp)
heal(JonSnow, 'super heal', /* modifier */ 2)
// => you heal Jon Snow with super heal spell (+200hp)
Or even with more arguments than the parameters defined in the function:
heal(JonSnow, 'heal', 1, 3, 2, 3, 'cucumber', { a: 1, b: 2 }, 'many arguments')
// => you heal Jon Snow with heal spell (+100hp)
It is up to you to implement the body of a function and handle each case as you see fit. But what happens when you want to write a function that takes an arbitrary number of arguments? In C# whenever we want to write such a function we use the params
keyword. In JavaScript on the other hand, and as of ES5, there’s no keyword nor operator that allows us to declare that a function will take an arbitrary number of arguments and handle it within the function body in a seamless manner.
The only approach possible is to use the arguments
object that is present within every function in JavaScript and which provides access to the arguments used to call a function:
// Taking a variable number of parameters in JavaScript:
// In C# we use params
// In JavaScript we take advantage of the arguments object
// available within every function
function obliterate() {
// Unfortunately arguments is not an array :O
// so we need to convert it ourselves
var victims = Array.prototype.slice.call(arguments, /* startFromIndex */ 0)
victims.forEach(function(victim) {
console.log(victim + ' wiped out of the face of the earth')
})
console.log(
'*Everything* has been obliterated, oh great master of evil and deceit!'
)
}
obliterate('John Doe', getPuppy(), 1, new Minion('Willy', 'troll'))
/*
John Doe wiped out of the face of the earth
cute puppy wiped out of the face of the earth
1 wiped out of the face of the earth
Willy the troll wiped out of the face of the earth
*Everything* has been obliterated, oh great master of evil and deceit!
*/
function getPuppy() {
return {
cuteWoof: function() {
console.log('wiii')
},
toString: function() {
return 'cute puppy'
},
}
}
function Minion(name, type) {
this.name = name
this.type = type
this.toString = function() {
return name + ' the ' + type
}
}
There are a couple of specially interesting things to point out in this example.
The first one is the fact that we are sending arguments of different types to the function and they are all treated seamlessly. That’s an example of duck typing in action where an object is defined by what it can do and not by what the object type is. As long as the values that we pass as arguments support the interface the function implementation expects - in this case the toString
method - everything will work just fine.
The second thing to point out is the use of the Array.prototype.slice
function to convert the arguments
variable into a real array victims
. While you could think that arguments
is an array, it is not (this is another of JavaScript quirks right here), it is an array-like object that you can index, enumerate, and that has a length
property:
function inspectArguments() {
console.log('the first argument is ', arguments[0])
console.log('the second argument is ', arguments[1])
console.log('there are ' + arguments.length + ' arguments')
// the arguments are enumerable like
// any common array or object, and thus you could use
// the for/in loop
for (var idx in arguments) {
console.log('item at position ' + idx + ' is ' + arguments[idx])
}
}
inspectArguments('cucumber', 'dried meat', 'dagger', 'rock')
// => the first argument is cucumber
// => the second argument is dried meat
// => there are 4 arguments
// => item at position 1 is cucumber
// => etc...
But that does not have any of the array functions that you would expect:
function inspectArgumentsFunctions() {
console.log('arguments.foreach is ', arguments.foreach)
console.log('arguments.map is ', arguments.map)
console.log('arguments.some is', arguments.some)
}
inspectArgumentsFunctions()
// => arguments.foreach is undefined
// => arguments.map is undefined
// => arguments.some is undefined
Using the slice
function of Array.prototype
allows you to convert arguments
to an array and take advantage of all the nice array functions. This is what we did with the obliterate
function within the first example:
function obliterate() {
//...
var victims = Array.prototype.slice.call(arguments, /* startFromIndex */ 0)
victims.forEach(function(victim) {
console.log(victim + ' wiped out of the face of the earth')
})
//...
}
Native Multiple Arguments with ECMASCript 2015 With Rest Parameters
ES2015 (ES6), the upcoming new version of JavaScript, comes with a native way to handle multiple arguments: rest parameters.
All the complexities of using the arguments
object in the previous examples can be substitued by rest parameters and handled in a much seamless fashion:
function obliterate(...victims) {
victims.forEach(function(victim) {
console.log(victim + ' wiped out of the face of the earth')
})
console.log(
'*Everything* has been obliterated, oh great master of evil and deceit!'
)
}
When using the rest parameters syntax, the victims
parameter becomes an array so there’s no need to perform additional conversions.
You can test the example above on this jsFiddle snippet.
Concluding
In this article you learned about the flexibility of handling parameters and arguments in JavaScript, how to implement functions that can handle an arbitrary number of arguments in JavaScript today (in a similar way to how params
works in C#). You also learned that in the upcoming version of JavaScript - ES 2015 - you will be able to use the rest parameters syntax that works exactly like params
in C#.
Interested in Learning More JavaScript? Buy the Book!
Are you a C# Developer interested in learning JavaScript? Then take a look at the JavaScript-mancy book, a complete compendium of JavaScript for C# developers.
Have a great week ahead!! :)
More Articles In This Series
- Chapter 0: The Basic Ingredients of JavaScriptMancy
- Chapter 1: The Many a One JavaScript Quirks
- Functions
- Object Oriented Programming
- Basics
- ES6
- OOP
- Introduction to OOP in JavaScript for C# Developers
- Summoning Fundamentals: Introduction to OOP In JavaScript – Encapsulation
- Summoning Fundamentals: Introduction to OOP In JavaScript – Inheritance
- Summoning Fundamentals: Introduction to OOP In JavaScript – Polymorphism
- White Tower Summoning: Mimicking C# Classical Inheritance in Javascript
- White Tower Summoning Enhanced: The Marvels of ES6 Classes
- Black Tower Summoning: Object Composition with Mixins
- Safer JavaScript Composition With Traits and Traits.js
- JavaScript Ultra Flexible Object Oriented Programming with Stamps
- Object Oriented JavaScript for C# Programmers
- Tooling
- Data Structures
- Functional Programming
- And more stuff to be shaped out:
- Functional Programming with JavaScript
- Modules
- Asynchronous Programming
- The JavaScript Ecosystem
References
A Sidenote on Using Array.prototype.slice On Arguments
While I was writing this article I found out that there’s a drawback when using Array.prototype.slice
to convert arguments
in a array: It prevents the browser JavaScript engine from performing optimizations.
The recommended approach is to use Array.slice
generic method - Array generic methods are specially implemented to be used on other types - but it is not a part of the ECMAScript specification (not even of ES6/2015) and is only implemented in certain browsers (try testing Array.slice
in the web developer console on Chrome, Firefox and Internet Explorer and you’ll see that it only exists in Firefox).
You have two options:
- use a polyfill that falls back to
Array.prototype.slice
when there are no generic functions available - ignore the recommendation based on the fact that functions with an arbitrary of arguments are uncommon :)
This problem will take care of itself when we start using the rest parameter syntax.
Written by Jaime González García , dad, husband, software engineer, ux designer, amateur pixel artist, tinkerer and master of the arcane arts. You can also find him on Twitter jabbering about random stuff.