Amount of passed parameters to functions in JavaScript - javascript

JavaScript is a revelation to me. I thought it would be like another sort of classical languages like C#, Java, etc. But it didn't. "Dynamic world" is tough and unpredictable. I was astonished when I read that functions can receive as many parameters as you desire. Without any error! I don't like it at all. I want more "staticness", I want some sort of compile-time errors!
My question is: am I need to worry about that? Is it a good practice to throw an exception if a quantity of passed parameters are more than a particular function expects?
function foo(one, two, three)
{
// Is it good?
if(arguments.length > arguments.callee.length)
throw new Error("Wrong quantity of arguments in " + arguments.callee.name + "()");
/* Stuff */
}
foo(1, 2, 3, 4); // -> Error
foo(1, 2, 3); // -> OK
Should I be concerned about it at all?
Thanks in advance!

You probably should not be concerned. There is no blanket rule on how to handle errors like this. It depends entirely upon the type of error and the type of situation. In some cases, where it's a serious programming error and there is no way to proceed (like insufficient arguments to perform the desired function), it may make sense to throw an exception or return an error from the function. But, in other cases, an extra argument can just be safely ignored and you can continue on your merry way as if that argument was never passed.
As you get used to javascript, you will come to understand that many function arguments can be optional and a single function may be correctly called with zero, one, two or three or even N arguments and the code in the function can adapt appropriately. This actually allows you to do things that are not as easy to do in more "static" languages. It is even possible to adapt to the type of the arguments and do something appropriately based on the type of the argument. While this may sound like heresy to someone that only has experience in hard-typed languages, it can actually be extremely useful.
As you maintain a body of code over time, you will also come to find that it's nice to be able to add an argument to the definition of a function, add code to that function that defaults it to a reasonable value if it isn't passed and NOT have to change any of the prior code that was using that function, yet a few new places that need that new argument can start using it immediately. Rather then grepping through the entire codebase to fix up every caller of that function, you can just make one change in one file and immediately start using a new argument to the function without changing all the other callers. This is enormously useful.
So, in more direct answer to your question, an extra argument passed to a function is never a serious error in javascript. Your code could just ignore it and proceed. If you want to alert the developer who wrote that code that an unexpected argument was passed, you can notify them somehow (perhaps some warning text on the debug console) in the "debug" version of your function/library, but I see no reason why you should stop execution in the "production" version of your function/library when you can proceed without any harm.

You don't need to worry about this. If you pass too many arguments, the function will just ignore it. You should only throw an error if there are too few arguments. In that case, the function might not be able to run.

While I agree that the number of arguments aren't important (and won't cause a problem so long as you type-check the arguments you're getting before you use them), since an unused, uncalled, argument won't do anything, if you're particularly concerned you could just create a subset of the passed-arguments and access that object internally:
function test(arg1, arg2, arg3) {
var slice = Array.prototype.slice,
subset = slice.call(arguments, 0, 3); // depending on how many arguments you want
}
Of course this means that you've now got to recover the parameters from the args object, and since surplus arguments seem to be perfectly safe this seems pointless. But it is still an option.
Albeit unnecessary.

Related

What are the harmful effects of manipulating an object passed in as a parameter? [duplicate]

Eclipse has an option to warn on assignment to a method's parameter (inside the method), as in:
public void doFoo(int a){
if (a<0){
a=0; // this will generate a warning
}
// do stuff
}
Normally I try to activate (and heed) almost all available compiler warnings, but in this case I'm not really sure whether it's worth it.
I see legitimate cases for changing a parameter in a method (e.g.: Allowing a parameter to be "unset" (e.g. null) and automatically substituting a default value), but few situations where it would cause problems, except that it might be a bit confusing to reassign a parameter in the middle of the method.
Do you use such warnings? Why / why not?
Note:
Avoiding this warning is of course equivalent to making the method parameter final (only then it's a compiler error :-)). So this question Why should I use the keyword "final" on a method parameter in Java? might be related.
The confusing-part is the reason for the warning. If you reassign a parameter a new value in the method (probably conditional), then it is not clear, what a is. That's why it is seen as good style, to leave method-params unchanged.
For me, as long as you do it early and clearly, it's fine. As you say, doing it buried deep in four conditionals half-way into a 30-line function is less than ideal.
You also obviously have to be careful when doing this with object references, since calling methods on the object you were given may change its state and communicate information back to the caller, but of course if you've subbed in your own placeholder, that information is not communicated.
The flip side is that declaring a new variable and assigning the argument (or a default if argument needs defaulting) to it may well be clearer, and will almost certainly not be less efficient -- any decent compiler (whether the primary compiler or a JIT) will optimize it out when feasible.
Assigning a method parameter is not something most people expect to happen in most methods. Since we read the code with the assumption that parameter values are fixed, an assignment is usually considered poor practice, if only by convention and the principle of least astonishment.
There are always alternatives to assigning method parameters: usually a local temporary copy is just fine. But generally, if you find you need to control the logic of your function through parameter reassignment, it could benefit from refactoring into smaller methods.
Reassigning to the method parameter variable is usually a mistake if the parameter is a reference type.
Consider the following code:
MyObject myObject = new myObject();
myObject.Foo = "foo";
doFoo(myObject);
// what's the value of myObject.Foo here?
public void doFoo(MyObject myFoo){
myFoo = new MyObject("Bar");
}
Many people will expect that at after the call to doFoo, myObject.Foo will equal "Bar". Of course, it won't - because Java is not pass by reference, but pass by reference value - that is to say, a copy of the reference is passed to the method. Reassigning to that copy only has an effect in the local scope, and not at the callsite. This is one of the most commonly misunderstood concepts.
Different compiler warnings can be appropriate for different situations. Sure, some are applicable to most or all situations, but this does not seem to be one of them.
I would think of this particular warning as the compiler giving you the option to be warned about a method parameter being reassigned when you need it, rather than a rule that method parameters should not be reassigned. Your example constitutes a perfectly valid case for it.
I sometimes use it in situations like these:
void countdown(int n)
{
for (; n > 0; n--) {
// do something
}
}
to avoid introducing a variable i in the for loop. Typically I only use these kind of 'tricks' in very short functions.
Personally I very much dislike 'correcting' parameters inside a function this way. I prefer to catch these by asserts and make sure that the contract is right.
I usually don't need to assign new values to method parameters.
As to best-practices - the warning also avoids confusion when facing code like:
public void foo() {
int a = 1;
bar(a);
System.out.println(a);
}
public void bar(int a) {
a++;
}
You shoud write code with no side effect : every method shoud be a function that doesn't change . Otherwise it's a command and it can be dangerous.
See definitions for command and function on the DDD website :
Function :
An operation that computes and returns a result without observable side effects.
Command : An operation that effects some change to the system (for
example, setting a variable). An
operation that intentionally creates a
side effect.

How come I can call a function with an argument, when the function is defined with no parameters?

Good day!
I stumbled upon something I've never seen in the realm of JavaScript, but I guess it's very easy to explain for someone who knows the language better. Below I have the following function: (Code taken from the Book: "Secrets of the JavaScript Ninja")
function log() {
try {
console.log.apply(console, arguments);
}
catch(e) {
try {
opera.postError.apply(opera, arguments);
}
catch(e) {
alert(Array.prototype.join.call(arguments, " "));
}
}
}
As you can see, the function is defined with an empty parameter list, but I was completely puzzled when I saw, later in the book, that they actually use said function like this...
var x = 213;
log(x); //Hmmm, I thought this function had an empty parameter list.
Could someone please explain to me, why is that function call allowed/possible? What are the concepts involved in JS that support this functionality? Thanks in advance, I'm very confused.
Best Regards,
You can call functions with the wrong number of parameters as much as you like. Excess parameters will be ignored; missing parameters will be given a default value.
As you can see from the code sample, you can access the arguments that were actually passed with the "arguments" object.
One of the design principles of JavaScript is to be forgiving rather than strict. If there's a way to keep going, JavaScript keeps going. Failing to pass a sufficient number of arguments is not considered fatal in JavaScript the way it would be in a language like C#, where one of the design principles is "bring possible errors to the developer's attention by failing at compile time".
Javascript functions have an implicit arguments parameter which is an array-like object with a length property.
for your log method you could do.
function log(){
alert(arguments[0]);
alert(arguments[1]);
}
And you can call log.
log("first","second");
JavaScript functions are variadic, i.e. they can take an arbitrary and infinite amount of arguments. You can access them via the arguments object, and pass in a variable number of values via apply. The formal parameter list just declares some variable names that are pointers to the arguments, and if there are less arguments given than parameter names defined they will default to undefined.
JavaScript allows you to call functions with any number of parameters, but if you'd like something more rigorous, there is TypeScript.
It prevents incorrect method calls and a lot of other JavaScript silliness.

Multiple arguments vs. options object

When creating a JavaScript function with multiple arguments, I am always confronted with this choice: pass a list of arguments vs. pass an options object.
For example I am writing a function to map a nodeList to an array:
function map(nodeList, callback, thisObject, fromIndex, toIndex){
...
}
I could instead use this:
function map(options){
...
}
where options is an object:
options={
nodeList:...,
callback:...,
thisObject:...,
fromIndex:...,
toIndex:...
}
Which one is the recommended way? Are there guidelines for when to use one vs. the other?
[Update] There seems to be a consensus in favor of the options object, so I'd like to add a comment: one reason why I was tempted to use the list of arguments in my case was to have a behavior consistent with the JavaScript built in array.map method.
Like many of the others, I often prefer passing an options object to a function instead of passing a long list of parameters, but it really depends on the exact context.
I use code readability as the litmus test.
For instance, if I have this function call:
checkStringLength(inputStr, 10);
I think that code is quite readable the way it is and passing individual parameters is just fine.
On the other hand, there are functions with calls like this:
initiateTransferProtocol("http", false, 150, 90, null, true, 18);
Completely unreadable unless you do some research. On the other hand, this code reads well:
initiateTransferProtocol({
"protocol": "http",
"sync": false,
"delayBetweenRetries": 150,
"randomVarianceBetweenRetries": 90,
"retryCallback": null,
"log": true,
"maxRetries": 18
});
It is more of an art than a science, but if I had to name rules of thumb:
Use an options parameter if:
You have more than four parameters
Any of the parameters are optional
You've ever had to look up the function to figure out what parameters it takes
If someone ever tries to strangle you while screaming "ARRRRRG!"
Multiple arguments are mostly for obligatory parameters. There's nothing wrong with them.
If you have optional parameters, it gets complicated. If one of them relies on the others, so that they have a certain order (e.g. the fourth one needs the third one), you still should use multiple arguments. Nearly all native EcmaScript and DOM-methods work like this. A good example is the open method of XMLHTTPrequests, where the last 3 arguments are optional - the rule is like "no password without a user" (see also MDN docs).
Option objects come in handy in two cases:
You've got so many parameters that it gets confusing: The "naming" will help you, you don't have to worry about the order of them (especially if they may change)
You've got optional parameters. The objects are very flexible, and without any ordering you just pass the things you need and nothing else (or undefineds).
In your case, I'd recommend map(nodeList, callback, options). nodelist and callback are required, the other three arguments come in only occasionally and have reasonable defaults.
Another example is JSON.stringify. You might want to use the space parameter without passing a replacer function - then you have to call …, null, 4). An arguments object might have been better, although its not really reasonable for only 2 parameters.
Using the 'options as an object' approach is going to be best. You don't have to worry about the order of the properties and there's more flexibility in what data gets passed (optional parameters for example)
Creating an object also means the options could be easily used on multiple functions:
options={
nodeList:...,
callback:...,
thisObject:...,
fromIndex:...,
toIndex:...
}
function1(options){
alert(options.nodeList);
}
function2(options){
alert(options.fromIndex);
}
It can be good to use both. If your function has one or two required parameters and a bunch of optional ones, make the first two parameters required and the third an optional options hash.
In your example, I'd do map(nodeList, callback, options). Nodelist and callback are required, it's fairly easy to tell what's happening just by reading a call to it, and it's like existing map functions. Any other options can be passed as an optional third parameter.
I may be a little late to the party with this response, but I was searching for other developers' opinions on this very topic and came across this thread.
I very much disagree with most of the responders, and side with the 'multiple arguments' approach. My main argument being that it discourages other anti-patterns like "mutating and returning the param object", or "passing the same param object on to other functions". I've worked in codebases which have extensively abused this anti-pattern, and debugging code which does this quickly becomes impossible. I think this is a very Javascript-specific rule of thumb, since Javascript is not strongly typed and allows for such arbitrarily structured objects.
My personal opinion is that developers should be explicit when calling functions, avoid passing around redundant data and avoid modify-by-reference. It's not that this patterns precludes writing concise, correct code. I just feel it makes it much easier for your project to fall into bad development practices.
Consider the following terrible code:
function main() {
const x = foo({
param1: "something",
param2: "something else",
param3: "more variables"
});
return x;
}
function foo(params) {
params.param1 = "Something new";
bar(params);
return params;
}
function bar(params) {
params.param2 = "Something else entirely";
const y = baz(params);
return params.param2;
}
function baz(params) {
params.params3 = "Changed my mind";
return params;
}
Not only does this kind of require more explicit documentation to specify intent, but it also leaves room for vague errors.
What if a developer modifies param1 in bar()? How long do you think it would take looking through a codebase of sufficident size to catch this?
Admittedly, this is example is slightly disingenuous because it assumes developers have already committed several anti-patterns by this point. But it shows how passing objects containing parameters allows greater room for error and ambiguity, requiring a greater degree of conscientiousness and observance of const correctness.
Just my two-cents on the issue!
Your comment on the question:
in my example the last three are optional.
So why not do this? (Note: This is fairly raw Javascript. Normally I'd use a default hash and update it with the options passed in by using Object.extend or JQuery.extend or similar..)
function map(nodeList, callback, options) {
options = options || {};
var thisObject = options.thisObject || {};
var fromIndex = options.fromIndex || 0;
var toIndex = options.toIndex || 0;
}
So, now since it's now much more obvious what's optional and what's not, all of these are valid uses of the function:
map(nodeList, callback);
map(nodeList, callback, {});
map(nodeList, callback, null);
map(nodeList, callback, {
thisObject: {some: 'object'},
});
map(nodeList, callback, {
toIndex: 100,
});
map(nodeList, callback, {
thisObject: {some: 'object'},
fromIndex: 0,
toIndex: 100,
});
It depends.
Based on my observation on those popular libraries design, here are the scenarios we should use option object:
The parameter list is long (>4).
Some or all parameters are optional and they don’t rely on a certain
order.
The parameter list might grow in future API update.
The API will be called from other code and the API name is not clear
enough to tell the parameters’ meaning. So it might need strong
parameter name for readability.
And scenarios to use parameter list:
Parameter list is short (<= 4).
Most of or all of the parameters are required.
Optional parameters are in a certain order. (i.e.: $.get )
Easy to tell the parameters meaning by API name.
Object is more preferable, because if you pass an object its easy to extend number of properties in that objects and you don't have to watch for order in which your arguments has been passed.
For a function that usually uses some predefined arguments you would better use option object. The opposite example will be something like a function that is getting infinite number of arguments like: setCSS({height:100},{width:200},{background:"#000"}).
I would look at large javascript projects.
Things like google map you will frequently see that instantiated objects require an object but functions require parameters. I would think this has to do with OPTION argumemnts.
If you need default arguments or optional arguments an object would probably be better because it is more flexible. But if you don't normal functional arguments are more explicit.
Javascript has an arguments object too.
https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Functions_and_function_scope/arguments

Performance penalty for undefined arguments

I quite often have optional arguments in functions, but some testing is showing a huge performance hit for them in firefox and safari (70-95%). Strangely, if I pass in the literal value undefined then there is no penalty. What could be happening here? I wouldn't have thought that it was a scope chain issue as they are inherently local to the function. Am I to start passing undefined into every optional argument?
jsPerf: http://jsperf.com/function-undefined-args/2
For a function like this:
function threeArgs(x, y, z) {
return x + y + z;
}
that's called like this:
threeArgs(1, 2, 3);
the optimizer is free to make the choice to generate no code at all. It's fairly easy for it to determine that there are no side effects, because the function simply references its parameter values and returns the result of a simple expression. Since the return value is ignored, there's no reason for the runtime to do anything at all.
Beyond that, if the code were:
something += threeArgs(1, 2, 3);
the optimizer might decide to generate code roughly equivalent to:
something += 6;
Why? Because the call was made with numeric constants, and it can safely fold those at code generation time. It might be conservative on that, because numbers are weird, but here they're all integers so it could well do this. Even if it didn't, it could safely inline the function:
something += 1 + 2 + 3;
When there's a parameter missing, however, it may be that the optimizers bail out and generate a real function call. For such a simple function, the overhead of the function call could easily account for a large difference in performance.
By using variables instead of constants in a test, and by actually using the return value of the function, you can "confuse" the optimizer and keep it from skipping the call or pre-computing the result, but you can't keep it from inlining. I still think that your result is interesting for that reason: it exposes the fact that (as of today anyway) those optimizers are sensitive to the way that functions are invoked.
I think what could explain the performance difference is the way arguments are passed to a function object: via the arguments object. When not passing any arguments, JS will start by scanning the arguments object for any of the given arguments, when those are undefined, The arguments prototype chain will be scanned, all the way up to Object.prototype. If those all lack the desired property, JS will return undefined. Whereas, passing undefined explicitly, sets it as a property directly on the arguments object:
function foo(arg)
{
console.log(arguments.hasOwnProperty('0'));
}
foo();//false'
foo('bar');//true
foo(undefined);//true
I gather that's the reason why passing undefined explicitly tends to be faster.

How can I ensure that all of my JavaScript functions return a value?

I've had numerous bugs happening just because of a missing return in a function. You see, when most of the code you write is in Ruby, it's easy to forget about explicit returns.
So I'd like to use something similar to JSlint (which I already use) to check that all functions return something. Yes, I think it's better to explicitly return something when it's not required than to hunt down missing returns.
So, are there any tools that would check for returns? Or maybe I can assert it in runtime in a simple manner?
Please don't suggest Coffeescript, I'm aware of its existence.
JSUnit example:
<script language="javascript" src="jsUnitCore.js"></script>
<script language="javascript">
function testWithValidArgs() {
assertEquals("someFunction should return something", "Expected REturn Value", someFunction(2, 3));
}
</script>
Just add return consistently. But to be honest, JSlint is a VERY strict checking tool. You will never get errors if you're not returning values unless you're trying to define a variable using the response of a function, but in that case it's more than logic that you add a return statement.
However, if you're still dedicated to have a return statement in every function, you should add them from the start. There is no tool that adds them.
I'm not aware of any tools that will do this out of the box. But it would not be hard to write one.
Start by using UglifyJS to parse your code into a syntax tree. Write a recursive function that examines all code, looking for function definitions. For every function you find, look at the last statement. If that one is not a return-statement, then print a warning.
(Too long for comment.)
My problem with returning something when a function has no (meaningful) return value is that it's misleading, unless it returns undefined, which defeats the purpose.
If I see a return, I have to reason about the code both in the method and at the call site.
In the function I have to determine if it ever returns anything else, why it returns the value it does, etc. The only real way around this is to return a constant that makes it obvious the it's not really returning anything, it's just to satisfy a desire to return something.
At the call site, if a return value is ignored, I need to understand why, and if it's okay to do so. If I know every function returns something, I then have to check the function to see if it's returning that special value, or go through the above process.
I'd almost rather namespace my functions into "function" and "method" namespaces at that point as a differentiater. This would allow automated testing of each namespace to make sure that all functions return something useful, all methods specifically don't, and would provide a source-level clue as to which the caller should expect.

Categories

Resources