Javascript skip arguments in function call - javascript

javascript beginner here.
Let's say I'm having a javascript function that takes 3 arguments:
function f(arg1, arg2, arg3) { // do stuff }
I know that I can call f(value1, value2); and in that case inside the function scope arg1 will be value1, arg2 will be value2 and arg3 will be null.
Everything ok with this. However if I want to call the function giving values only to arg1 and arg3 I need to do something like this: f(value1, null, value2);
Is there a way I can specify which arguments to have which values in a more C#-esque manner (without specifying not given arguments as null)? Something like this: for calling f with values only for arg1 and arg3 I would write f(value1, arg3 = value2);
Any ideas?
Cheers!

there is a way i have seen for this:
for example
function person(name,surname,age)
{
...
}
person('Xavier',null,30);
you can do this:
function person(paramObj)
{
var name = paramObj.name;
var surname = paramObj.surname;
var age = paramObj.age;
}
calling like this:
person({name:'Xavier',age:30});
I think this is the closest you'll be able to do it like in c# have in mind that JS is not compilled so you can't predict the arguments of a function.
EDIT:
For better syntax you can use ES6 object destructuring, like this:
function person({name, surname, age})
{
...
}
https://javascript.info/destructuring-assignment

Yes you can. It can be written as:
f(arg1, undefined, arg3);
In this call, arguments 1 and 3 will pass, and argument 2 will not be sent.

If you were going to do (let's say it was valid)
f(value1, arg3 = value2)
Then argument 2 would be undefined, so just pass that:
f(value1, undefined, value2)

The only way you would be able to do this with JS is to pass one array containing all of the parameters.
Your default values would have to be set within the function - you can't define default values for arguments in JavaScript.
function foo( args ){
var arg1 = args[ 0 ] || "default_value";
var arg2 = args[ 1 ] || 0;
///etc...
}
Even better, instead of an array you could pass a simple object which would allow you to access the arguments by their key in the object:
function foo( params ){
var arg1 = params[ "arg1" ] || "default_value";
var arg2 = params[ "arg2" ] || 0;
///etc...
}

Hey I had a similar problem but i don't know if it would apply in any context, and if it's the same for ES5, but in ES6 and within my context i was able to just pass undefined as the argument i want to skip.
What i mean by context, is that in my case I am assigning a default value to that argument in the function with ES6 format:
const exampleFunc = (arg1 = "defaultValue",arg2) => {
console.log(arg1,arg2)
}
exampleFunc(undefined,"hello!");
//this would log to the console "defaultValue","hello!"
I'm not sure if this would work if you don't have a default value assigned in the function with ES6 format, but it worked for me! Hope this helped

With ECMAScript 6 (ECMAScript 2015) and the introduction of Default Parameters, it can be as easy as setting default values to the parameters and passing undefined to skip a parameter:
function paramsTest(p1 = "p1 def", p2 = "p2 def", p3 = "p3 def") {
console.log([p1, p2, p3]);
}
paramsTest(); // [ "p1 def", "p2 def", "p3 def"]
paramsTest("p#1", "p#2"); // [ "p#1", "p#2", "p3 def"]
paramsTest("p#1", undefined, null); // [ "p#1", "p2 def", null]

Add this code to your function (for default values)
function f(a, b, c)
{
a = typeof a !== 'undefined' ? a : 42;
b = typeof b !== 'undefined' ? b : 'default_b';
a = typeof c !== 'undefined' ? c : 43;
}
call the function like this
f(arg1 , undefined, arg3)

Another interesting approach to this would be to use the Function.prototype.call() method. All we have to do is assign the arguments with default call object values, and then you're free to call the method with only the arguments you want to use:
function f(arg1 = this.arg1, arg2 = this.arg2 || false, arg3 = this.arg3) {
console.log([arg1, arg2, arg3]);
}
f.call({ arg1: 'value for arg1', arg3: 'value for arg3'});
f('value for arg1', undefined, 'value for arg3');
Notice that I set arg2 to this.arg2 || false - this syntax allows a fallback to false instead of undefined when no f.call instance object arg2 property exists.

in ES6, if your parameters are optional because they have default values and you are okay passing an object, you can use spread syntax:
function f(opts) {
const args = {
arg1: 'a',
arg2: 'b',
arg3: 'c',
...opts
}
const {arg1, arg2, arg3} = args
return arg1.concat(arg2, arg3)
}
f() // "abc"
f({arg1: '1', arg3: '3'}) // "1b3"
f({arg2: ' whatever '}) // "a whatever c"

Related

Mapping arbtrary object values to function inputs in Javascript

I have an object of arbitrary functions and a function that takes a method name and an object or an array with parameters and then selects and calls the function accordingly. The problem is that functions have a variable number of inputs and some of them contain optional fields with default values and I can't find a general way to map inputs of the parameters to inputs of the function.
One way to solve the problem when the arguments come in the form of the array is just simply called the function with the ... operator: func(...args), but that still leaves me with a problem, because I can't use these methods on objects. Is there any way to map object values to function inputs by using object keys?
Abstract example of the situation:
const funcs = {
func1: (arg1, arg2, arg3 = 'something') => .....does something
func2: () => ....does something
func3: (anotherArg1) => ...does something
}
function callFunction(method: string, args: unknown[]| object) {
if (Array.isArray(args)) {
return funcs[method](...args)
}
else (if args instanceof Object) {
//... Here I need to parse the args and call the function in "funcs" object.
}
}
Just spread the second argument of callFunction(method, ...args)
const funcs = {
func1: (arg1, arg2, arg3 = 'something') => {
[arg1, arg2, arg3].forEach((a, i) => console.log(`arg${i+1}:`, JSON.stringify(a)));
}
}
function callFunction(method, ...args) {
return funcs[method](...args)
}
const method = 'func1';
callFunction(method, [1, 2], {foo: 'bar'})

JavaScript: Passing different count of parameters to callback function [duplicate]

Can I call a function with array of arguments in a convenient way in JavaScript?
Example:
var fn = function() {
console.log(arguments);
}
var args = [1,2,3];
fn(args);
I need arguments to be [1,2,3], just like my array.
Since the introduction of ES6, you can sue the spread syntax in the function call:
const args = [1,2,3];
fn(...args);
function fn() {
console.log(arguments);
}
Before ES6, you needed to use apply.
var args = [1,2,3];
fn.apply(null, args);
function fn() {
console.log(arguments);
}
Both will produce the equivalent function call:
fn(1,2,3);
Notice that I used null as the first argument of the apply example, which will set the this keyword to the global object (window) inside fn or undefined under strict mode.
Also, you should know that the arguments object is not an array, it's an array-like object, that contains numeric indexes corresponding to the arguments that were used to call your function, a length property that gives you the number of arguments used.
In ES6, if you want to access a variable number of arguments as an array, you can also use the rest syntax in the function parameter list:
function fn(...args) {
args.forEach(arg => console.log(arg))
}
fn(1,2,3)
Before ES6, if you wanted to make an array from your arguments object, you commonly used the Array.prototype.slice method.
function fn() {
var args = Array.prototype.slice.call(arguments);
console.log(args);
}
fn(1,2,3);
Edit: In response to your comment, yes, you could use the shift method and set its returned value as the context (the this keyword) on your function:
fn.apply(args.shift(), args);
But remember that shift will remove the first element from the original array, and your function will be called without that first argument.
If you still need to call your function with all your other arguments, you can:
fn.apply(args[0], args);
And if you don't want to change the context, you could extract the first argument inside your function:
function fn(firstArg, ...args) {
console.log(args, firstArg);
}
fn(1, 2, 3, 4)
In ES5, that would be a little more verbose.
function fn() {
var args = Array.prototype.slice.call(arguments),
firstArg = args.shift();
console.log(args, firstArg);
}
fn(1, 2, 3, 4);
In ECMAScript 6, you can use spread syntax (...) for that purpose. It's way simpler and easier to understand than Function.prototype.apply().
Code example:
const fn = function() {
console.log(arguments);
}
const args = [1,2,3];
fn(...args);

Can't use Function.prototype.call directly

function f(a) { return a}
f(1) // => 1
f.call(null, 1) // => 1
Function.prototype.call(f, null, 1) // => undefined
Why the last line return undefined, I thought they are the same.
These will be the same:
function f(a) { return a}
console.log(f(1)); // => 1
console.log(f.call(null, 1)); // => 1
console.log(Function.prototype.call.call(f, null, 1)); // => 1
Notice the additional .call in the last statement.
And here's the explanation:
Function.prototype.call
According to the spec, Function.prototype.call returns an abstract operation Call(func, thisArg, argList).
Therefore, f.call(null, 1) will return the abstract operation Call(f, null, 1) where f is the function being called, null is the context from which it is called, and 1 is the argument passed to f. This will give you the desired output.
Based on that, Function.prototype.call(f, null, 1) will result in the abstract operation Call(Function.prototype, f, null, 1) where Function.prototype is the function being called, f is the context, and null and 1 are the arguments passed to Function.prototype. Of course this will not work as intended.
Function.prototype.call.call
However, Function.prototype.call.call(f, null, 1) will return the abstract call operation Call(Function.prototype.call, f, null, 1), where Function.prototype.call is the function to be called, f is the context from which it is called, and null and 1 are passed as arguments. So what would that look like? Well, since f is the context and call is the function being invoked with (null,1), the end result is identical to: f.call(null, 1).
Let's start with this:
function fn() { console.log(this); }
fn.a = function(){console.log(this)}
fn.a() // function fn() { console.log(this); }
So let's dig deeper and try to implement a fake call function:
Function.prototype.fakeCall = function () {
// console.log(this)
// taking the arguments after the first one
let arr = Array.prototype.slice.call(arguments, 1);
// on the first run this will be Function.fakeCall but on the second pass it will be the first argument (the a function)
this.apply(arguments[0], arr);
}
function a(ar){ console.log(ar + this.name) };
let obj = {name : "thierry"};
// a.fakeCall( obj, 'hi ')
Function.fakeCall.fakeCall(a, obj, 'hi ');
Thus when we do this: Function.prototype.fakeCall.fakeCall(a, obj, 'hi ')
what happens is, on the first run we have:
arr = [ obj, 'hi ']
this = Function.fakeCall
so we end up with Function.fakeCall.apply(a, [ obj, 'hi ']);
Then on the second run we have
arr = ['hi']
this = a
so we end up with a.apply(obj, ['hi']) which is the same as a.call(obj, 'hi');
If however we did Function.fakeCall(a, obj, 'hi ');
On the first run we would have this = Function and that won't work. It will throw an error in this case, in your case it just returns undefined. That is easily implementable with a try-catch.
Function.prototype.fakeCall = function () {
let arr = Array.prototype.slice.call(arguments, 1);
try{
return this.apply(arguments[0], arr);
}catch(e){}
}
function a(ar){ return ar + this.name };
let obj = {name : "thierry"};
console.log(Function.fakeCall(a, obj, 'hi ')); // undefined
console.log(Function.fakeCall.fakeCall(a, obj, 'hi ')); // hi thierry

Default value in function - Javascript

In PHP we can define the argument value for the functions if it's not set(called), so for example:
<?php
function blah($arg = false){
var_dump($arg);
}
?>
In the above example if we call the function like:
<?php
blah();
// ==> out put will be: false;
blah(true);
// ==> out put will be: true;
?>
So we can define a value for the arguments if they are not settled while we call the function, how this could be achieved in javascript functions?
I have it exactly like PHP:
<script>
function blah(arg = false){
//...
}
</script>
The above code works just fine in Mozilla Firefox, but in Chrome, the function is not working and gets fixed when I remove = false in the parenthesis, Chrome developer tools says:
Uncaught Syntax Error: Unexpected token =
This is not possible in Javascript.
Try this Conditional Operator statement instead:
<script>
function blah(arg){
arg = typeof arg !== 'undefined' ? arg : 'someValue';
}
</script>
Where 'someValue' is the default value that the arg variable will get when there are no arguments passed to the blah() function.
This is the cleanest pattern for implementing default arguments in javascript IMO.
function (arg) {
arg = arg || 'defaultVale';
}
However this can fail if you expect the value of arg to be some falsy value, e.g 0, false, NaN, null using it is not really recommended.
This version protects against this case by explicitly comparing with undefined.
function (arg) {
arg = arg === undefined ? 'defaultVale' : arg;
// As T.J Crowder pointer out if not in strict mode or if the code will run
// in an IFrame use typeof arg === "undefined" instead of directly
// comparing with undefined
}
Another nice pattern is using objects for arguments instead. This has two benefits
Order of arguments is not important
It's easy to implement default arguments
Code
var defaults = {
arg1: 10,
arg2: 20
};
var f = function (args) {
args = jQuery.extend(true, args, defaults); //jQuery
args = _.defaults(args, defaults); // Underscore
};
f({
a: 25 //Use a non default value
});
In JavaScript there is no default parameter.
Just write the code like:
function test(arg) {
if(arg) {
// do something as arg is defined.
} else {
// do something as arg is undefined.
}
}
test(true); // arg is defined
test(); // arg is undefined
Simple variation
function defaulter(p1) {
p1 = p1 || "default";
}
In Js you can't have default values for parameters. You can check if the data is of a known type with typeof operator:
function blah(arg)
{
if (typeof arg === 'undefined')
{
arg = false;
}
}
or setting his value in a short-circuit way
function blah(arg)
{
arg = arg || false;
}
For example in coffeescript you can set it by using
blah = (arg = "mydefaultvalue") ->
that is translated into
blah = function(arg)
{
if (arg == null)
{
arg = "mydefaultvalue";
}
}

Trying to understand underscore.js source - call and apply used in library

In Jeremy Ashkenas's awesome Underscore.js library, I tried to understand one thing about the source file. I do not understand this:
var slice = Array.prototype.slice;
args = slice.call(arguments, 2);
So that:
args = Array.prototype.slice.call(arguments, 2);
.call or .apply are the methods of the functions. But here, which functions do .call refer to? The first parameter should be the context, but arguments is context? The second parameter should be the params to pass in the functions. Here they are number 2. What does this mean? Sometimes in the library, it uses 1 or 0. Are they the number of the params to pass in the functions?
_.bind = function bind(func, context) {
var bound, args;
if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
if (!_.isFunction(func)) throw new TypeError;
args = slice.call(arguments, 2);
return bound = function() {
if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
ctor.prototype = func.prototype;
var self = new ctor;
var result = func.apply(self, args.concat(slice.call(arguments)));
if (Object(result) === result) return result;
return self;
};
};
Question 2:
I do not quite understand the logic of this function. Need help to understand. An example should be very helpful.
// Invoke a method (with arguments) on every item in a collection.
_.invoke = function(obj, method) {
var args = slice.call(arguments, 2);
return _.map(obj, function(value) {
return (method.call ? method || value : value[method]).apply(value, args);
});
};
Thank you for help.
The "slice" function on the Array prototype expects that this will refer to the array on which it's supposed to operate. In other words, if you have a real array:
var myArray = [1, 2, 3];
and you call slice():
var sliced = myArray.slice(1);
Then in that call to slice(), this refers to the array "myArray". As Raynos notes in a comment:
myArray.slice(1)
is the same as
myArray.slice.call(myArray, 1);
Thus when you use call() to invoke the function, and pass it arguments as the context object, the slice() code operates on arguments. The other parameters passed via .call() are simply the parameter or parameters for slice() itself. In my example above, note that I passed 1 to the function.
Now as to your second question, that .invoke() function first isolates the arguments passed in after the first two. That means that when you use _.invoke() you pass it two or more arguments: the first is the list to operate on, the second is the method, and the (optional) subsequent arguments are passed to the method for each element of the list.
That call to _.map() is complicated (and in fact I think it's got a little nonsense in it). What it's doing is iterating over the list, calling a function for each value in the list. What that function does to first determine whether the "method" parameter really is a function. If it is, then it calls that function via .apply() with the element of the list as the context. If "method" is not a function, then it assumes it's the name of a property of each list element, and that the properties are functions.
So for example, with a simple list it's pretty simple:
var myList = [1, 2, 3];
var result = _.invoke(myList, function(n) { return this * n; }, 2);
That will give the result [2, 4, 6] because the function I passed multiplies its context object (this) by the parameter passed, and I passed 2 in the call to _.invoke().
With a more complicated list, I can use the second flavor of _.invoke() and call a method on each object in the list:
var getName = function(prefix) { return prefix + " " + this.name; };
var list = [
{ name: "Bob", getName: getName },
{ name: "Sam", getName: getName },
{ name: "Lou", getName: getName }
];
var result = _.invoke(list, "getName", "Congressman");
That will call the "getName" function on each object in the list and return a list made from the results. The effect will be the list ["Congressman Bob", "Congressman Sam", "Congressman Lou"].
Now about that nonsense. In the code for _.invoke():
return _.map(obj, function(value) {
return (method.call ? method || value : value[method]).apply(value, args);
});
That subexpresion method || value will always return the value of "method", or at least almost always barring some exotic trick. If method.call is truthy, then a reference to method must also be truthy. Also, if it were my code, I'd inspect method outside the _.map() callback so that the decision doesn't have to be made over and over again. Maybe something like:
return _.map(obj, method.call ?
function(value) { method.apply(value, args); } :
function(value) { value[method].apply(value, args); }
);

Categories

Resources