Reputation: 119837
For the user's ease, I have a function that receives an optional argument first, before the required argument. For example:
ns.myFunction('optional arg', function(){
//the required callback
});
I'm doing this rather than doing the following since the callback body could be long, and the user might forget to override the defaults to the optional arguments:
ns.myFunction(function(){
//the required callback
}, 'optional arg');
Currently I'm doing this to check:
function myFunction(first, second) {
//if second is undefined and first is a function
if (typeof second === 'undefined' && typeof first === 'function') {
second = first;
}
}
Questions
Upvotes: 8
Views: 5878
Reputation: 61
function test(num = 1) {
console.log(typeof num)
}
test() // 'number' (num is set to 1)
test(undefined) // 'number' (num is set to 1 too)
// test with other falsy values:
test('') // 'string' (num is set to '')
test(null) // 'object' (num is set to null)
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters
Upvotes: 0
Reputation: 1996
A simple functional approach:
Idea:
_
/**
* @param {function} fn(a,...b)
* @param {function} argumentTest - a function to test the first parameter
* @return {function} returns a function that passes its first argument into
argumentTest. If argumentTest returns true, fn is called with the first
argument shifted to the last position else fn is called with the arguments
order unchanged
*/
function appendFirstArgumentIf (fn,argumentTest){
return function(a,...b){
return argumentTest(a) ? fn(...b,a) : fn(a,...b)
}
}
usage:
function myFunction(callback, string){
string = string ? string : 'Default Message'
callback(string)
}
var myNewFunction = appendFirstArgumentIf(myFunction,
firstArgument=> typeof firstArgument !== 'function')
myNewFunction('New Message', console.log) // using console.log as callback function
//>>>New Message
myNewFunction(console.log)
//>>>Default Message
also possible with this:
function mySecondFunction(callback, name, string){
string = string ? string : 'Welcome'
callback(string, name)
}
var myLatestFunction = appendFirstArgumentIf(mySecondFunction,
firstArgument=> typeof firstArgument !== 'function')
myLatestFunction('Hello', console.log, 'John')
//>>>Hello John
myLatestFunction(console.log, 'John')
//>>>Welcome John
Upvotes: -1
Reputation: 5660
You can configurate function arguments, depending on their number:
myFunction() {
let first, second;
if (arguments.length === 2) {
[first, second] = arguments;
} else if (arguments.length === 1) {
[second] = arguments;
}
if ( ! first) {
// first is undefined, so only second argument was passed.
}
}
ES6 destructuring makes code clean and suitable for scaling.
Upvotes: 2
Reputation: 340733
This is not the right way because optional parameters are by convention always placed at the end. And you see a reason why: it is much easier to handle them. If the length of anonymous function is your concern, clients of your API should use function references or variables:
function callback1() { //...
var callback2 = function() {//...
myFunction(callbackX, optional);
The problem with escaping this
can be solved with bind()
.
If you really want to go the path of multiple optional parameters and callback at the end, I can think of two ways: arguments
object or wrapping all optional arguments in one options
objects.
With arguments
you can write:
var lastOptionalIndex = arguments.length - 2;
var callback = arguments[lastOptionalIndex + 1]; //required callback is always last
var optionalFirst = lastOptionalIndex >=0? arguments[0] : undefined;
var optionalSecond = lastOptionalIndex >=1? arguments[1] : undefined;
//...
See how ugly it is compared to:
function myFunction(callback, firstOptional, secondOptional //...
With options
wrapper object you always have two arguments:
function myFunction(options, callback);
Where options
is just an object:
{
firstOptional: 1,
secondOptional: 'foo'
//...
}
Upvotes: 4
Reputation: 1566
ArgueJS:
function range(){
arguments = __({start: [Number, 0], stop: Number, step: [Number, 1]});
for(var i = arguments.start; i < arguments.stop; i += arguments.step)
console.log(i);
}
Become even prettier with ES6:
function range(){
{start, stop, step} = __({start: [Number, 0], stop: Number, step: [Number, 1]})
for(var i = start; i < stop; i += step)
console.log(i);
}
Or CoffeeScript:
range = ->
{start, stop, step} = __ {start: [Number, 0], stop: Number, step: [Number, 1]}
console.log i for i in [start...stop] by step
Upvotes: 1
Reputation: 3717
It can easilly be done with ArgueJS:
function myFunction (){
arguments = __({first: [String], second: Function})
// and now on, you can access your arguments by
// arguments.first and arguments.second
}
A very similar example can be found at ArgueJS' first example. Note that only the parameter in the middle is required:
function range(){
arguments = __({start: [Number, 0], stop: Number, step: [Number, 1]})
for(var i = arguments.start; i < arguments.stop; i += arguments.step)
console.log(i);
}
repl:
>>> range(3)
0
1
2
>>> range(3, 5)
3
4
>>> range(0, 5, 2)
0
2
4
Upvotes: 2
Reputation: 18906
It's not necessary to name any of the parameters. you can simply say:
if (arguments.length < 2) // There are no optional parameters
And retrieve each parameter via arguments[i]. The function is found at arguments[arguments.length - 1].
On a minor note, the typeof operator always returns a string, so == can be used instead of ===
Upvotes: 1
Reputation: 1584
If you really want to pass multiple optional parameters first, you could do it like this:
function myFunction() {
alert("optional:");
for(var i=0; i<arguments.length-1) alert(arguments[i]);
alert("function: "+ arguments[arguments.length-1]);
}
But It's rather ugly. I think you should just put the optional stuff at the end.
Upvotes: 0