Javascript : optional parameters in function [duplicate] - javascript

This question already has answers here:
Is there a better way to do optional function parameters in JavaScript? [duplicate]
(28 answers)
Closed 6 years ago.
Let's say I have this :
function concatenate(a, b, c) {
// Concatenate a, b, and c
}
How do I handle those calls ?
x = concatenate(a)
x = concatenate(a, b)
x = concatenate(a, c)
How can I make my function aware of the parameter I gave to it ?

Any unfilled argument will be undefined.
concatenate(a, c) is equivalent to concatenate(a, b). You cannot pass the third parameter without passing the second; but you can pass undefined (or null, I suppose) explicitly: concatenate(a, undefined, c).
In the function, you can check for undefined and replace with a default value.
Alternately, you can use an object argument to imitate keyword arguments: concatenate({a: a, c: c}).

Just use arguments array-like object:
function concatenate() {
var result = '';
for (var i = 0; i < arguments.length; i++) {
result += arguments[i];
}
return result;
}

from ES6/ES2015 on you can do similar as in php:
function concatenate(a, b = false, c = false) {
// Concatenate a, b, and c
}
for the older versions, you can do:
function concatenate(a, b, c) {
if( typeof b !== 'undefined' && typeof c !== 'undefined') {
// code when a, b and c are defined
} else if ( typeof b !== 'undefined' && typeof c == 'undefined') {
// code when a and b are defined
} else {
// code
}
}
I am sure there is a better approach too, but should work.

Use the ES6 rest parameters syntax to get an array of arguments. Then simply join its items to retrieve the concatenated string.
concatenate(a);
concatenate(a, b);
concatenate(a, c);
function concatenate(...args){
// for old browsers
// `...args` is an equivalent of `[].slice.call(arguments);`
return args.join('');
}

Related

Shorthand way to check for function parameters

In my code, I have a function that creates a new map given a few parameters. I want to make it so that if no parameters are passed, some default values will be used.
Why won't this work:
function create(a,b,c) {
return new Map(a,b,c || 10,1,10); // create new map using a b c as parameters
// or 10, 1, 10 if none entered.
}
create();
Assume that there is a constructor function 'Map' that would accept and process these parameters.
What can i do besides having an if/else type check?
The shortest way i know under some limitations is to use
<var> = <var> || <defaultvalue>;
So
Return new Map((a = a || 10), (b = b || 1), (c = c || 10));
However this way its hardly readable and you might want to consider moving the assignments before the constructor.
The limitation however is that all falsy values lead to the default being assigned which might be a problem for some numeric values.
You can do it this way:
function create(a, b, c) {
a = typeof a !== 'undefined' ? a : 10;
b = typeof b !== 'undefined' ? b : 1;
c = typeof c !== 'undefined' ? c : 10;
return new Map(a, b, c);
}
Javascript does not offer default function value parametization.

A javascript issue for closure

Given the following code:
String.method('deentityify', function () {
var entity = {
quot: '"',
lt: '<',
gt: '>'
};
return function () {
return this.replace(/&([^&;]);/g,
function (a, b) {
var r = entity[b];
return typeof r === 'string' ? r : a;
}
);
};
}());
document.write('deentityify: ' + '<">'.deentityify() + '<br>');
Regarding the
function (a, b) {
var r = entity[b];
return typeof r === 'string' ? r : a;
}
How come the anonymous function get the parameter value a, b? Of course I have tried, the output is right. Can anyone can help me?
The function is actually an argument to the 'replace' call. The regex matches are passed into the function as parameters.
To write the code another way, it would look:
function match(a, b) {
var r = entity[b];
return typeof r === 'string' ? r : a;
}
var result = this.replace(/&([^&;]);/g, match)
The names of the parameters (a & b) are inconsequential and could be anything you like.
The first parameter will be the matched value and the subsequent parameters will be the values of the matched groups. So for clarity the function could be written as:
function matchFn(match, group1, group2, group3) {
var r = entity[group1];
return typeof r === 'string' ? r : match;
}
To quote MDN
A function to be invoked to create the new substring (to put
in place of the substring received from parameter #1). The arguments
supplied to this function are described in the "Specifying a function
as a parameter" section below.
You can handover a function as second parameter to replace(). This function acts as some kind of callback function and receives it's parameters from the calling replace in a fixed order like stated in the Docs.
a and b are just arbitrary names - a is the matched substring and b the capture group ([^&;]).

Set undefined parameters via arguments

I'm trying to write a function that corrects the arguments of a function based on previously specified optional parameters. I've come to a problem though. It seems that I can't set variables via the arguments array unless they have been defined in any way before. The code below shows an example of the problem I'm facing.
function foo(a, b, c) {
arguments[0] = "lorem";
arguments[1] = "ipsum";
arguments[2] = "dolor";
console.log([a, b, c]);
}
foo(null); // ["lorem", undefined, undefined]
foo(null, null); // ["lorem", "ipsum", undefined]
foo(null, null, null); // ["lorem", "ipsum", "dolor"]
When logging arguments the result is always ["lorem", "ipsum", "dolor"] though.
Is there any way to solve this problem ?
I can't set a, b and c directly because a function called in foo wouldn't have access to these names.
My goal would look like something like this:
function foo(a, b, c) {
var rules = [];
// Rule for optional parameter 1 (b)
// If it equals true the value of b is shifted to the next parameter (c)
rules[1] = function(val) { return val !== "ipsum"; };
optionalize(rules);
console.log([a, b, c]);
}
foo("lorem", "dolor"); // ["lorem", undefined, "dolor"];
The arguments array isn't really an array but an "array-like" object. You can't change its length.
What you try to do is usually done using
a = a || "lorem";
or, if you don't want to replace any "falsy" argument, using
if (typeof a === "undefined") a = "lorem";
It is a bit peculiar, but is this what you had in mind?
function optionalize(fn, options) {
var i, key, rule;
for(i = 0; i < options.length; i += 1) {
key = fn.placement[i];
rule = fn.ruleset[key];
// If the rule exists and returns true, shift value to the right.
if(rule && rule(options[i])) {
options[i+1] = options[i];
options[i] = undefined;
}
// Assign the numeric index to an alphabet key.
// Depending on your use case, you may want to substitute a plain Object here and return that, instead of adding non-numeric properties to an Array.
options[key] = options[i];
}
}
// Test function
function foo(a, opts) {
opts = opts || [];
optionalize(foo, opts);
console.log([a, opts.b, opts.c]);
}
// Optional argument names, in the order that they would be received.
foo.placement = ['b', 'c'];
// Optionalize rules
foo.ruleset = {
b: function (val) { return val !== "ipsum"; }
};
// Demonstration
foo('lorem');
foo('lorem', []);
foo('lorem', ['dolor']);
foo('lorem', ['ipsum', 'dolor']);
As dystroy's answer has already indicated, the arguments variable isn't a real Array, and changing it may not be a good idea. I have provided a solution which does not rely on arguments and fulfills the criteria as far as could be possible using simple JavaScript.
The function foo is specified with a required argument a, followed by an Array of optional arguments named opts. An optionalize specification is set onto foo, through the placement and ruleset properties. The optionalize function takes this information and transforms the array's indices into usable name keys, applying the rules as necessary.
I'm not sure what you're trying to do but something like arguments[0] = a ? a : "lorem" and so on ?
You can convert arguments to an array
function foo () {
var arguments = Array.prototype.slice.call(arguments);
arguments.push(4);
console.log(arguments);
}
foo(1,2,3);
​

How can I declare optional function parameters in JavaScript? [duplicate]

This question already has answers here:
Is there a better way to do optional function parameters in JavaScript? [duplicate]
(28 answers)
Closed 3 years ago.
Can I declare default parameter like
function myFunc( a, b=0)
{
// b is my optional parameter
}
in JavaScript?
With ES6: This is now part of the language:
function myFunc(a, b = 0) {
// function body
}
Please keep in mind that ES6 checks the values against undefined and not against truthy-ness (so only real undefined values get the default value - falsy values like null will not default).
With ES5:
function myFunc(a,b) {
b = b || 0;
// b will be set either to b or to 0.
}
This works as long as all values you explicitly pass in are truthy.
Values that are not truthy as per MiniGod's comment: null, undefined, 0, false, ''
It's pretty common to see JavaScript libraries to do a bunch of checks on optional inputs before the function actually starts.
Update
With ES6, this is possible in exactly the manner you have described; a detailed description can be found in the documentation.
Old answer
Default parameters in JavaScript can be implemented in mainly two ways:
function myfunc(a, b)
{
// use this if you specifically want to know if b was passed
if (b === undefined) {
// b was not passed
}
// use this if you know that a truthy value comparison will be enough
if (b) {
// b was passed and has truthy value
} else {
// b was not passed or has falsy value
}
// use this to set b to a default value (using truthy comparison)
b = b || "default value";
}
The expression b || "default value" evaluates the value AND existence of b and returns the value of "default value" if b either doesn't exist or is falsy.
Alternative declaration:
function myfunc(a)
{
var b;
// use this to determine whether b was passed or not
if (arguments.length == 1) {
// b was not passed
} else {
b = arguments[1]; // take second argument
}
}
The special "array" arguments is available inside the function; it contains all the arguments, starting from index 0 to N - 1 (where N is the number of arguments passed).
This is typically used to support an unknown number of optional parameters (of the same type); however, stating the expected arguments is preferred!
Further considerations
Although undefined is not writable since ES5, some browsers are known to not enforce this. There are two alternatives you could use if you're worried about this:
b === void 0;
typeof b === 'undefined'; // also works for undeclared variables

how do I compare 2 functions in javascript

How do I compare 2 functions in javascript?
I am not talking about internal reference. Say
var a = function(){return 1;};
var b = function(){return 1;};
Is it possible to compare a and b ?
var a = b = function( c ){ return c; };
//here, you can use a === b because they're pointing to the same memory and they're the same type
var a = function( c ){ return c; },
b = function( c ){ return c; };
//here you can use that byte-saver Andy E used (which is implicitly converting the function to it's body's text as a String),
''+a == ''+b.
//this is the gist of what is happening behind the scences:
a.toString( ) == b.toString( )
Closures mean that you need to be very careful what you mean when you say "compare". For example:
function closure( v ) { return function(){return v} };
a = closure('a'); b = closure('b');
[a(), b()]; // ["a", "b"]
// Now, are a and b the same function?
// In one sense they're the same:
a.toString() === b.toString(); // true, the same code
// In another sense they're different:
a() === b(); // false, different answers
// In a third sense even that's not enough:
a2 = closure('a');
a() === a2(); // true
a === a2; // false, not the same object
The ability to reach outside the function means that in a general sense, comparing functions is impossible.
However, in a practical sense you can get a very long way with Javascript parsing libraries like Esprima or Acorn. These let you build up an "Abstract Syntax Tree" (AST), which is a JSON description of your program. For example, the ast your return 1 functions looks like this
ast = acorn.parse('return 1', {allowReturnOutsideFunction:true});
console.log( JSON.stringify(ast), null, 2)
{
"body": [
{
"argument": {
"value": 1, // <- the 1 in 'return 1'
"raw": "1",
"type": "Literal"
},
"type": "ReturnStatement" // <- the 'return' in 'return 1'
}
],
"type": "Program"
}
// Elided for clarity - you don't care about source positions
The AST has all the information you need to make comparisons - it is the Javascript function in data form. You could normalize variable names, check for closures, ignore dates and so on depending on your needs.
There are a bunch of tools and libraries to help simplify the process but even so, it's likely to be a lot of work and probably not practical, but it is mostly possible.
You can compare two variables that might contain function references to see if they refer to the exact same function, but you cannot really compare two separate functions to see if they do the same thing.
For example, you can do this:
function foo() {
return 1;
}
var a = foo;
var b = foo;
a == b; // true
But, you can't reliably do this:
function foo1() {
return 1;
}
function foo2() {
return 1;
}
var a = foo1;
var b = foo2;
a == b; // false
You can see this second one here: http://jsfiddle.net/jfriend00/SdKsu/
There are some circumstances where you can use the .toString() operator on functions, but that's comparing a literal string conversion of your function to one another which, if even off by a teeny bit that is inconsequential to what it actually produces, will not work. I can think of no situation where I would recommend this as a reliable comparison mechanism. If you were seriously thinking about doing it this way, I'd ask why? What are you really trying to accomplish and try to find a more robust way of solving the problem.
toString() on a function returns the exact declaration. You can modify jfriend00's code to test it out.
This means you can test to see if your functions are exactly the same, including what spaces and newlines you put in it.
But first you have to eliminate the difference in their names.
function foo1() {
return 1;
}
function foo2() {
return 1;
}
//Get a string of the function declaration exactly as it was written.
var a = foo1.toString();
var b = foo2.toString();
//Cut out everything before the curly brace.
a = a.substring(a.indexOf("{"));
b = b.substring(b.indexOf("{"));
//a and b are now this string:
//"{
// return 1;
//}"
alert(a == b); //true.
As the others said, this is unreliable because a single whitespace of difference makes the comparison false.
But what if you're employing it as a protective measure? ("Has someone altered my function since I created it?") You may actually desire this kind of strict comparison then.
ES6+ clean solution using template literals:
const fn1 = () => {}
const fn2 = () => {}
console.log(`${fn1}` === `${fn2}`) // true
Basically means:
console.log(fn1.toString() === fn2.toString()) // true
Convert function to string, then, replace line-break and space before comparing:
let a = function () {
return 1
};
let b = function () {
return 1
};
a = a.toString().replace(/\n/g, '').replace(/\s{2}/g, ' ');
b = b.toString().replace(/\n/g, '').replace(/\s{2}/g, ' ');
console.log(a); // 'function () { return 1}'
console.log(b); // 'function () { return 1}'
console.log(a === b); // true
b = function () {
return 2
};
b = b.toString().replace(/\n/g, '').replace(/\s{2}/g, ' ');
console.log(b); // 'function () { return 2}'
console.log(a === b); // false
b = () => 3;
b = b.toString().replace(/\n/g, '').replace(/\s{2}/g, ' ');
console.log(b); // '() => 3'
console.log(a === b); // false
p/s: If you are using ES6, try to use let instead of var.

Categories

Resources