Skip arguments in a JavaScript function
JavascriptJavascript Problem Overview
I have a function like this:
function foo(a, b, c, d, e, f) {
}
In order to call this function only with an f
argument, I know I should do:
foo(undefined, undefined, undefined, undefined, undefined, theFValue);
Is there a less verbose way to do this?
Solutions:
I selected some proposed solutions (without using helper third functions)
// zero - ideal one, actually not possible(?!)
foo(f: fValue);
// one - asks a "strange" declaration
var _ = undefined;
foo(_, _, _, _, _, fValue);
// two - asks the {} to be used instead of a 'natural' list of args
// - users should be aware about the internal structure of args obj
// so this option is not 'intellisense friendly'
function foo(args){
// do stuff with `args.a`, `args.b`, etc.
}
foo({f: fValue});
Javascript Solutions
Solution 1 - Javascript
Such:
foo(undefined, undefined, undefined, undefined, undefined, arg1, arg2);
.is equal to:
foo(...Array(5), arg1, arg2);
.or:
foo(...[,,,,,], arg1, arg2);
Such:
foo(undefined, arg1, arg2);
.is equal to:
foo(...Array(1), arg1, arg2);
.or:
foo(...[,], arg1, arg2);
Such:
foo(arg1, arg2);
.is equal to:
foo(...Array(0), arg1, arg2);
.or:
foo(...[], arg1, arg2);
Solution 2 - Javascript
You could use apply
:
foo.apply(this, Array(5).concat([theFValue]));
In this case, 5
is the amount of parameters you want to skip.
Wrap that in a function:
function call(fn, skipParams, parameter) {
fn.apply(this, Array(skipParams).concat([parameter]));
}
call(foo, 5, theFValue);
However, in that case the scope of this
is different, so you may need to pass that, too:
function call(fn, skipParams, parameter, thisArg) {
fn.apply(thisArg, Array(skipParams).concat([parameter]));
}
call(foo, 5, theFValue, this);
Then again, this implementation only allows 1 parameter to be passed. Let's improve that:
function call(fn, skipParams, parameters, thisArg) {
fn.apply(thisArg, Array(skipParams).concat(parameters));
}
call(foo, 5, [theFValue, theGValue, theHValue], this);
That's starting to get a "little" verbose. It also doesn't handle missing parameters after the first parameter that well, unless you want to pass undefined
:
call(foo, 5, [theFValue, theGValue, theHValue, undefined, theJValue], this);
Or, something completely different:
var _ = undefined;
foo(_,_,_,_,_, theFValue);
On a more serious note:
Your best option to deal with optional parameters, is to change the way you're handling parameters. Simply pass an object:
function foo(parameters){
// do stuff with `parameters.a`, `parameters.b`, etc.
}
foo({c: 1, g: false});
This approach doesn't suffer from any of the drawbacks in the earlier examples.
Solution 3 - Javascript
A better way to deal with optional arguments is to pass an object whose attributes you look up:
function foo(options) {
var a = options.a,
b = options.b,
c = options.c,
d = options.d,
e = options.e,
f = options.f;
}
foo({ f: 15 });
Solution 4 - Javascript
Skip function:
const skip = (num) => new Array(num);
Skipping beginning params:
foo(...skip(4), f);
Skipping end params:
foo(f, ...skip(4));
Skipping middle params:
foo(f, ...skip(4), f2);
Solution 5 - Javascript
If you will pass an object with a property name f so you can use destructuring assignment with ES6 syntax like this:
function foo({ f }) {
console.log(f);
}
foo({ g: 5, f: 10 });
Solution 6 - Javascript
If this is something you're going to want to do often, then consider a simple wrapper:
function bar(f) {
foo(undefined, undefined, undefined, undefined, undefined, f);
}
If you're only doing this once, or you're wanting a random permutation of the parameters then this approach isn't the best.
Solution 7 - Javascript
Use bind for a partial application:
function foo(a, b, c, d, e, f) { document.write(f); }
function skip(f, n) {
while (n--) {
f = f.bind(null, undefined);
}
return f;
}
skip(foo, 5)('hallo');
Solution 8 - Javascript
I provide some methods that may help you achieve, as below,
- Destructuring assignment (recommend)
- Optional_chaining
Destructuring assignment
Method1:Example1
function Person(name, {id="007", age=-1, info={msg:null, mood:undefined}}) {
return [name, id, age, info.msg, info.mood]
}
// š Test Only
for (const [result, expected] of [
[Person("Carson", {}), // If you don't need any options then must set "" or {}
["Carson", "007", -1, null, undefined]
],
[Person("Aoo", {
age: 29,
info: {
msg: "hello world"
}
}),
["Aoo", "007", 29, "hello world", undefined]
],
[Person("Boo", {
id: "003",
info: {
mood: "Happy"
}
}),
["Boo", "003", -1, null, "Happy"]
]
]) {
console.log(JSON.stringify(result))
console.log(JSON.stringify(result) === JSON.stringify(expected))
}
Example 2
const user = {
id: 42,
displayName: 'jdoe',
fullName: {
firstName: 'John',
lastName: 'Doe'
}
};
function userId({id}) {
return id;
}
function whois({displayName, fullName: {firstName: name}}) {
return `${displayName} is ${name}`;
}
console.log(userId(user)); // 42
console.log(whois(user)); // "jdoe is John"
ļ source code from object_destructuring search Unpacking fields from objects passed as a function parameter
Method2
Use Optional_chaining to set the default value
const val = obj ?? "default value" // if obj is undefined then val = default value
const val = obj?.msg // equal to obj.msg if {msg:...} exists in the obj. Otherwise, undefined
for example
/*
Assume your options is:
{
id:"",
info:{
msg:"",
mood: "",
}
}
*/
function MyFunc(name, options = {}) {
const id = options.id ?? "007"
const msg = options.info?.msg ?? null
const mood = options.info?.mood
// ...
}
Example
function Person(name, options = {}) {
const id = options.id ?? "007"
const msg = options.info?.msg ?? null
const mood = options.info?.mood
return [name, id, msg, mood]
}
for (const [result, expected] of [
[Person("Carson"),
["Carson", "007", null, undefined]
],
[Person("Aoo", {
info: {
msg: "hello world"
}
}),
["Aoo", "007", "hello world", undefined]
],
[Person("Boo", {
id: "003",
info: {
mood: "Happy"
}
}),
["Boo", "003", null, "Happy"]
]
]) {
console.log(JSON.stringify(result) === JSON.stringify(expected))
}
Method 2.extend
If you want the IDE to know what the options is, you may consider using the below method,
function PersonOptions(options={}) {
this.id = options.id ?? "007"
this.msg = options.info?.msg ?? null
this.mood = options.info?.mood
}
function Person2(name, options = new PersonOptions()) {
return [name, options.id, options.msg, options.mood]
}
for (const [result, expected] of [
[Person2("Carson"),
["Carson", "007", null, undefined]
],
[Person2("Aoo", new PersonOptions({
info: {
msg: "hello world"
}
})),
["Aoo", "007", "hello world", undefined]
],
[Person2("Boo", new PersonOptions({
id: "003",
info: {
mood: "Happy"
}
})),
["Boo", "003", null, "Happy"]
]
]) {
console.log(JSON.stringify(result) === JSON.stringify(expected))
}
Solution 9 - Javascript
How about
function multiply(a = 2, b = 1) {
return a * b;
}
console.log(multiply(undefined, 3));
// expected output: 6
If you pass a param undefined, it will use the default value from the definition.