Splats...
Sometimes when developing a function, we are not sure just how many arguments we are going to need. Sometimes we might get one argument; other times we might get a hundred. To help us easily solve this problem, CoffeeScript gives us the option of using splats when defining the argument list for a function. Splatted arguments are denoted by placing an ellipsis (...) after the method definition.
When would you use splats? Splats can be used whenever your function will be taking in a variable number of arguments. Before we take a look at a detailed example, let’s look quickly at a simple function that takes a splatted argument:
Example: (source: splats.coffee)
splatter = (etc...) -> console.log "Length: #{etc.length}, Values: #{etc.join(', ')}" splatter() splatter("a", "b", "c")
Example: (source: splats.js)
(function() { var splatter, __slice = Array.prototype.slice; splatter = function() { var etc; etc = 1 <= arguments.length ? __slice.call(arguments, 0) : []; return console.log("Length: " + etc.length + ", Values: " + (etc.join(', '))); }; splatter(); splatter("a", "b", "c"); }).call(this);
Output: (source: splats.coffee)
Length: 0, Values: Length: 3, Values: a, b, c
As you can see, whatever arguments we pass into our function automatically get put into an array, and should we not send any arguments we get an empty array.
Unlike other languages that support a similar construct, CoffeeScript does not force you to only use splats as the last argument in the argument list. In fact, splatted arguments can appear anywhere in your argument list. A small caveat is that you can have only one splatted argument in the argument list.
To help illustrate how splats can be used in any part of the argument list, let’s write a method that will take some arguments and spit out a string. When building this string, we make sure that the first and last arguments are uppercased; any other arguments will be lowercased. Then we’ll concatenate the string using forward slashes.
Example: (source: splats_arg_join.coffee)
joinArgs = (first, middles..., last) -> parts = [] if first? parts.push first.toUpperCase() for middle in middles parts.push middle.toLowerCase() if last? parts.push last.toUpperCase() parts.join('/') console.log joinArgs("a") console.log joinArgs("a", "b") console.log joinArgs("a", "B", "C", "d")
Example: (source: splats_arg_join.js)
(function() { var joinArgs, __slice = Array.prototype.slice; joinArgs = function() { var first, last, middle, middles, parts, _i, _j, _len; first = arguments[0], middles = 3 <= arguments.length ? __slice.call(arguments, 1, _i = arguments.length - 1) : (_i = 1, []), last = arguments[_i++]; parts = []; if (first != null) parts.push(first.toUpperCase()); for (_j = 0, _len = middles.length; _j < _len; _j++) { middle = middles[_j]; parts.push(middle.toLowerCase()); } if (last != null) parts.push(last.toUpperCase()); return parts.join('/'); }; console.log(joinArgs("a")); console.log(joinArgs("a", "b")); console.log(joinArgs("a", "B", "C", "d")); }).call(this);
Output: (source: splats_arg_join.coffee)
A A/B A/b/c/D
I admit that is a bit of a heavy example, but it illustrates how splats work. When we call the joinArgs function, the first argument we pass into the function call gets assigned to the first variable, the last argument we pass in gets assigned to the last variable, and if any other arguments are passed in between the first and the last arguments, those are put into an array and assigned to the middles variable.
Finally, when dealing with splats, you might have an array that you want passed in as individual arguments. That is possible.
Let’s take a quick look at an example:
Example: (source: splats_array.coffee)
splatter = (etc...) -> console.log "Length: #{etc.length}, Values: #{etc.join(', ')}" a = ["a", "b", "c"] splatter(a) splatter(a...)
Example: (source: splats_array.js)
(function() { var a, splatter, __slice = Array.prototype.slice; splatter = function() { var etc; etc = 1 <= arguments.length ? __slice.call(arguments, 0) : []; return console.log("Length: " + etc.length + ", Values: " + (etc.join(', '))); }; a = ["a", "b", "c"]; splatter(a); splatter.apply(null, a); }).call(this);
Output: (source: splats_array.coffee)
Length: 1, Values: a,b,c Length: 3, Values: a, b, c
Using our earlier splatter example, we can try first passing in an array, but as you can see, the splatter function sees the array as a single argument, because that is what it is. However, if we append ... to the array as we pass it into our function call, the CoffeeScript will split up the array into separate arguments and pass them into the function.