In JavaScript, how can I create a function with an optional parameter? - javascript

Question: What is the proper way to define a function in JavaScript that takes optional parameters?
For example:
function myFunc(optionVar1) {
if(optionVar1 == undefined) {
...
} else {
...
}
}
myFunc('10'); // valid function call
myFunc(); // also a valid function call
Is it proper to use a ? mark like Ruby in the function declaration like so to denote optional parameters:
function myFunc(optionVar1?) {...} // <--- notice the ? mark

There is no syntax in Javascript that specifies that a parameter is optional (or required). All parameters are optional. If they aren't specified they're undefined so you need to check for that. For example, this function will in effect create a default value of 10 for the parameter:
function myfunc(someParam) {
if (someParam === undefined) {
someParam = 10;
}
...
}
Also you can access the parameters programmatically using the arguments property.
Lastly, if you have more than about 3-4 parameters it's generally advisable to use an anonymous object instead.

Actually, all parameters are optional in JS functions. There is no warning or error if you omit a parameter.
You can set defaults like
function throw_cat(dist){
dist = typeof dist=='undefined' ? 20 : dist;
//OR
dist = dist || 20; //this will assign it to 20 if you pass 0 or another 'falsy' value, though. May be good if you expect a string. String '0' stays, '' or null assigns the default
//etc...
}

You can use annotation such as {Object=} or {number=} in the comment section when using a doclet:
/**
* #param {object=}xyz
*/
Modern IDE knows to recognize annotations for JavaScript and shows you indications about potential problems in your code.
Example:
/**
*
* #param query
* #param callback
* #param {number=} ttl optional time-to-leave
*/
loadByJSONP:function loadByJSONP(query, callback, ttl) {
...do some work
}
In this example 'ttl' is optional. the annotation {number=} indicate to IDE that this parameter is optional. Accordingly when you call this function with two parameters only, you will not get warning.
Annotations can also be used for designating the expected type. this makes your code better and less prone to bugs. Here is the link for annotations:
https://developers.google.com/closure/compiler/docs/js-for-compiler

First, everyone who writes JavaScript, do yourself a favor and go through Learning Advanced JavaScript from John Resig.
function myFunc(arg1, arg2 /* varargs... */) {
var optional = Array.prototype.slice.call( arguments, 2 );
Every parameter to a function is "optional", even those you declare in the parameter list of the function declaration.
Just consider documenting them well.

Some sources will tell you to skip parameters altogether and instead make use of the arguments array. This lets you take any input that's provided to your function, and you can decide how to respond to the arguments passed to your function as you see fit.
You can read more about it here:
http://www.devguru.org/technologies/ecmascript/quickref/arguments.html

cletus and Damovisa have made good suggestions. I would also add that some (like myself) might prefer a notation like this:
function foo(/*bar*/) {
if (arguments.length == 1) {
var bar = arguments[0];
...
}
}
This serves to document to developers of your code base that the argument is optional and also documents the name, but it also prevents the argument from showing up in the function name in a debugger (the example would show up as foo() rather than foo(optional_argument). Otherwise, developers who are consuming the API might assume that it was required.
Edit: This is especially useful for optional arguments that are intended to be used internally.

function connect(hostname, port, method) {
hostname = hostname || "localhost";
port = port || 80;
method = method || "GET";
}
Important URL Optional Parameters in Javascript

The first google response I discovered:
http://www.tipstrs.com/tip/354/Using-optional-parameters-in-Javascript-functions
I haven't seen any instances where a question mark is used to alert a caller to an optional parameter. While it's done in other languages, I don't think it's necessary in javascript.
In fact, it seems that you can't use a question mark in the variable name. Only letters, numbers, $ and _.

Just don't define the function with any parameters and access the special arguments array from within the function where you want the optional parameters to be. Example below.
<HTML>
<HEAD>
<SCRIPT Language="JavaScript">
function foo() {
var argv = foo.arguments;
var argc = argv.length;
for (var i = 0; i < argc; i++) {
alert("Argument " + i + " = " + argv[i]);
}
}
</SCRIPT>
</HEAD>
<BODY onload="foo('hello', 'world');">
</BODY>
</HTML>

Is it proper to use a ? mark like Ruby in the function declaration
No, there's no language-based way denote an optional arg. I definitely think it's worth indicating in a comment, though:
function myFunc(var1, var2 /* optional */, var3 /* optional */) {
if (var2===undefined) ...
(Note the triple-equals for exact equality testing. Otherwise a passed null value will also match. You generally want === for most comparisons in JS, as the double-equals is undesirably weakly-typed.)
For when you've got an indeterminate number of optional arguments, you generally omit them from the function statement, and again a comment is polite:
function myFunc(var1 /* , optional var2..varN */) {
for (var i= 1; i<arguments.length; i++) ...

// 1. best way
function sum1(x = 1, y = 2) {
console.log(x + y)
}
sum1() // x=1,y=2
sum1(4, 5) // x=4,y=5
sum1(8) // x=8,y=2
// 2. Ternary way
function sum2(x) {
x = x !== undefined ? x : 1; // if x define x=x, if undefine x=1
console.log(x)
}
sum2(1)
sum2(5)
sum2('foo')
sum2()
// logical way
function sum3(x) {
x = x || 1; // x = either x if x is true or 1 if x is not false
console.log(x)
}
sum3(1)
sum3(5)
sum3('foo')
sum3()

For history sake: You can loop in your arguments with a != null check. For instance :
function container(param1, param2, param3, param4){
var param1, param2, param3, param4 = 0 // or other data types.
for (var i = 0; i < arguments.length; i++){
if(i != null) {
console.log(i); // or put default values to your parameters if you need to.
}
}
Now, all arguments become optional.

Related

JavaScript: Pass conditional condition as value

I have a function that finds elements using jQuery. I then take that element, get a value from it and assign it to a variable. However, sometimes an element will not exist on the document and the function will return undefined. This is expected and if the element returned is undefined then the variable that gets assigned should also be undefined. Since I am using this function to assign variables many times I wanted to make it neat and on one line. I thought using conditionals might be a solution, however, doing this leads to really messy long lines and needing to call the function twice:
let html = $(response.body);
let language = getRowElementFromText('Language', html) ? getRowElementFromText('Language', html).find('a').text() : undefined;
let pages = getRowElementFromText('Page', html) ? parseInt(getRowElementFromText('Page', html).text()) : undefined;
Is it possible to resolve those issues and somehow pass the condition of the conditional to be used as the value? For example, in this pesudo code this would be the value of the conditional:
let html = $(response.body);
let language = getRowElementFromText('Language', html) ? this.find('a').text() : undefined;
let pages = getRowElementFromText('Page', html) ? parseInt(this.text()) : undefined;
If this not possible is there another more readable way I can accomplish this on one line?
It is/will be with the optional chaining operator that's new in ES2020 (available via transpilation today). the code would look like this (it only helps so much with the parseInt cas):
let language = getRowElementFromText('Language', html)?.find('a').text();
// −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−^
let pageText = getRowElementFromText('Page', html)?.text();
// −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−^
let pages = pageText ? parseInt(pageText) : undefined;
language would be undefined if the element wasn't found.
If you can't use it (because your target environments don't have it yet and you're not transpiling), you could pass in a function that getRowElementFromText will call if the element is found:
let html = $(response.body);
let language = getRowElementFromText('Language', html, el => el.find('a').text());
let pages = getRowElementFromText('Page', html, el => parseInt(el.text()));
getRowElementFromText would look like this:
function getRowElementFromText(text, html, callback) {
const result = /*...existing logic...*/;
return result === undefined ? undefined : callback(result);
}
Or perhaps like this (callback is optional):
function getRowElementFromText(text, html, callback) {
const result = /*...existing logic...*/;
return !callback || result === undefined ? result : callback(result);
}
Note: In the above I'm assuming getRowElementFromText does actually return undefined in some cases ("not found"), and returns a jQuery object in other cases (found). I flag this up because if it always returns a jQuery object (which may be empty), jQuery objects are never falsy (not even empty ones).
Add the following function to jQuery prototype. It receives a fallback string as an argument and when element is not found, returns fallback string:
$.prototype.getText = function(fallback = undefined) {
return this.length > 0 ? this.text() : fallback
}
console.log($("#foo").getText())
console.log($("#bar").getText("If the element does not exists, returns given fallback string"))
console.log(`Fallback string default value is: ${$("#baz").getText()}`)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<label id="foo">If the element exists, return its inner text</label>
Usage:
$(selector).getText(fallbackString);

Creating an Arithmetic Task Runner

I have been tasked to create an Arithmetic Task Runner as part of my assignment.
Up until today I've never used NodeJS or even the terminal to execute a script.
I have been working on this for the past 5 hours and still no luck. I have avoided coming here and asking as I'd like to figure it out for myself, however, I have succumbed to desperately needing help.
This is the code I have so far:
class ArithmeticTaskRunner {
static set taskCount(counter) {
throw new('This is a readOnly accessor, the value is ${value}');
}
add(y) {
this.y = y || 0
console.log(y)
}
minus(x) {
this.x = Math.abs(this.y) * -1;
console.log(this.x);
};
multiply(z) {
this.z = z * this.x;
console.log(this.z)
}
execute(startValue) {
this.startValue = startValue + this.y
this.y = this.startValue
console.log(this.startValue)
this.startValue = this.minus
console.log(this.startValue)
this.startValue = this.multiply(this.startValue)
console.log(this.startValue)
}
}
tasks = [
function() { minus()},
function() { multiply(z)},
function() { add(x)},
function() { execute(x)}
]
This is nowhere near perfect, but it's 80%-90% near completion.
This is the task I have been given:
You should implement a class called ArithmeticTaskRunner with the following:
- An instance variable named tasks which is initialised to an empty array upon
creation.
- A method named addNegationTask which adds an anonymous function to the
tasks array. This anonymous function should take one argument, x, and return the
negation, -x.
- A method named addAdditionTask which takes a single argument y, and adds
an anonymous function to the tasks array. This anonymous function should take
one argument, x, and return the result x + y.
- A method named addMultiplicationTask which takes a single argument y,
and adds an anonymous function to the tasks array. This anonymous function
should take one argument, x, and return the result x * y.
- A read-only accessor named taskCount which returns the number of queued tasks.
- A method named execute, which takes a single argument named startValue.
If omitted, startValue defaults to zero. Starting at startValue, this method should iterate over the tasks array executing each function on the current value. It then returns the resulting number after all arithmetic operations have been executed.
I'd be grateful for any help I could get.
The issues I have are the following: The execute method (trying to make the startValue, after the addition, a negative), the multiplication method and the fact I cannot call the addition method twice without overriding the value. The examples of the program fully working have shown I should allow for a method to be called multiple times without overriding the previous value.
I know there's a rule where it's one question per issue, I concede that. But if anyone can help me out with any of the issues I will truly be grateful and I will compensate people for their efforts.
Thank you.
Edit - This is an example of both the expected inputs/outputs
> let taskRunner = new ArithmeticTaskRunner()
undefined
> taskRunner.addAdditionTask(2)
undefined
> taskRunner.addMultiplicationTask(4)
undefined
> taskRunner.addAdditionTask(10)
undefined
> taskRunner.execute(2)
26
> taskRunner.execute(-2)
10
I don't want to provide the whole answer because this is an assignment for you, but here's some code that might help you out. This starts with 5 then calls doubleIt and then calls addOne to arrive at 11.
It does this by creating an array of functions, each one performs a simple task and returns the result of its input modified in some way.
Then it creates a function called execute that uses Array.reduce to call the first function in the array with a given initial value, then repeatedly calls each function in the array on the result. Check the documentation for Array.reduce if you're confused about how it works.
doubleIt = x => x * 2;
addOne = x => x + 1;
tasks = [doubleIt, addOne];
execute = (initial) => tasks.reduce((x,fn) => fn(x), initial)
document.write(execute(5))
Hint #2
class ArithmeticTaskRunner {
constructor() {
this.tasks = [];
}
addAdditionTask(arg) {
this.tasks.push(x => x + arg);
}
addMultiplicationTask(arg) {
this.tasks.push(x => x * arg);
}
execute(startValue) {
return this.tasks.reduce((x, fn) => fn(x), startValue);
}
}
let taskRunner = new ArithmeticTaskRunner()
taskRunner.addAdditionTask(2)
taskRunner.addMultiplicationTask(4)
taskRunner.addAdditionTask(10)
document.write(taskRunner.execute(2));
document.write(', ');
document.write(taskRunner.execute(-2));

Does it ever make sense to include a value in the first next call to generator iterator?

As the question states, does it ever make sense to pass a value to the first iterator.next() call or will it always be ignored? As an example:
function* foo(x) {
var y = 2 * (yield(x + 1));
var z = yield(y / 3);
return (x + y + z);
}
var it = foo(5);
// note: not sending anything into `next()` here
console.log(it.next()); // { value:6, done:false }
console.log(it.next(12)); // { value:8, done:false }
console.log(it.next(13)); // { value:42, done:true }
Notice there is not a value being passed to the first next(). However, in the article I'm trying to learn from, it goes on to say:
But if we did pass in a value to that first next(..) call, nothing bad would happen. It would just be a tossed-away value
Okay. That makes sense. But now I'm curious to know if there is a use case (or if it's even possible to utilize) where passing in a value has a benefit.
Article: https://davidwalsh.name/es6-generators
Will it always be ignored?
Yes.
Is a use case where passing in a value has a benefit?
When it simplifies the rest of your code. In your example, that could be using a loop instead of writing it out:
const it = foo(5);
for (let i=11; i<=13; i++)
console.log(it.next(i));
Now we're passing 11 into the first call to .next even if there's no use for the value anywhere.

jquery/javascript missing ) after formal parameters

Hi there I have this code:
function getFullnameDetails(mainHeight,upperstyle,type=''){
setTimeout(function(){fullnameCenter(mainHeight,upperstyle,type='')},2000);
}
function fullnameCenter(mainHeight,upperstyle,type=''){
var distOfMainAndUpper = mainHeight - upperstyle;
var mainHalfHeight = mainHeight/2;
var imageHeight = jQuery("img[rel='fullname']").height(); //there is a delay
var imageHalfHeight = imageHeight/2;
var fromImageTopToMainHalf = mainHalfHeight - imageHeight;
var position = imageHalfHeight+fromImageTopToMainHalf-distOfMainAndUpper;
if(type == 'big'){ jQuery("#temp .test1").css("bottom",position+"px"); }
else { jQuery(".test1").css("bottom",position+"px"); }
}
It says that I'm missing ) after formal parameters.
This happens on this line:
function getFullnameDetails(mainHeight,upperstyle,type=''){ //IT HAPPENS HERE! :)
setTimeout(function(){fullnameCenter(mainHeight,upperstyle,type='')},2000);
}
What am I doing wrong here.
Thanks in advance for the help :)
Javascript does not support default function parameter values.
You can do things like this (but be wary of unintended 'falsey' values):
function getFullnameDetails(mainHeight,upperstyle,type){
type = type || ''; // if type is 'falsey' (null, undefined, empty string) assign ''
//...
}
As other answers have pointed out, JavaScript doesn't support default values in the function parameters. However, as also pointed out in the other solutions, using an inline || won't work when falsey values are passed (eg null, 0, '', NaN, false). A better solution is to check to see if the argument is undefined:
type = typeof(type) !== 'undefined' ? type : '';
or, more generally:
argument_name = typeof(argument_name) !== 'undefined' ? argument_name : default_value;
Of course, this means that passing undefined to a function will mean that the default value is used instead - but that should be pretty uncommon.
Are you trying to use like a default parameter or something?
type=''
That is not valid -- you can't have an equal sign in the parameter list of a function like that.

How to turn this JavaScript string "myArray[0].myPrice" in to a reference to myPrice?

If I have a string of "myArray[0].myPrice", how do I turn this in to a reference to myPrice?
This is the code context:
binding.value = convert(binding.data[binding.field],
binding.converter, binding.data, elem);
binding.field is what contains "myArray[0].myPrice".
binding.data has a reference to the hierarchical object which has an Array property of myArray, and the first element is an object with a property of myPrice.
EDIT: based on the answers, this worked:
binding.value = convert(eval('(binding.data.' + binding.field + ')'),
binding.converter, binding.data, elem);
Is it good to use eval like this? I thought eval was going away in newer JavaScript?
You can use eval, but here is a better solution:
// The following code results in the same as that scary eval(...)
var data = binding.data,
chain = binding.field.split(/[\.\[\]]+/);
for (var i = 0; data[chain[i]]; i++) {
data = data[chain[i]];
}
// Embrace JavaScript Awesomeness!
A breakdown of what I'm doing here:
In JS any property can be called as object[propertyName].
That includes arrays, i.e. a[3] is the same as a['3'].
Therefore, we split the string using one of the characters: ., [, ]. The + is there because without it if you have a[3].b[3] the ]. will give you an empty
string.
We might get an empty string in the end, but that's not a problem since "" is like false in JS.
You could go further and filter out all the invalid variable names, which in javascript is a name that does not conform to [a-zA-Z_$][0-9a-zA-Z_$]*. But I am not quite sure as to why one would do that...
Well, it ain't exactly pretty, but it doesn't use eval:
var str = 'myArray[0].myPrice';
var parts = str.split(/[\[\].]+/);
doSomething(window[parts[0]][parts[1]][parts[2]]);
where window[parts[0]][parts[1]][parts[2]] is the actual reference. Not sure why you'd have a string version and one loaded into memory, though.
Depending on the current context, this might work:
eval("myArray[0].myPrice")
try
var a = eval("myArray[0].myPrice".split('.')[1]);
However this will probably not work in the context you run it in since myPrice will be referring to window.myPrice which I doubt would be defined as anything.
Your best bet here is to define myArray in a scope that can be accessed from anywhere (such as window.AppNS.Data.myArray) and then use the eval() function to access this.
var a = eval("window.AppNS.Data." + "myArray[0].myPrice")
alert(a) // the contents of myPrice are revealed
Here is something similar to the logic we use in the template engine pure.js
var obj = {
myArr:[
{price:15}
],
anObj:{
prop:'ABC'
}
};
function getRef(o, path){
var props = path.split(/\./),
prop,
isArr = /([^\[]+)(\[(\d+)\])*/,
i = 0, ii = props.length;
while(i < ii){
prop = props[i].match(isArr);
o = o[prop[1]];
if(prop[2]){
o = o[+prop[3]];
}
i++;
}
return o;
}
console.log(getRef(obj, 'myArr[0].price')); // --> 15
console.log(getRef(obj, 'anObj.prop')); // --> ABC

Categories