Is there a general good practice convention regarding sending parameters as individual variables, or to send an array of parameters to a function / method?
Eg.
param1, param2, param3 vs array data
How do you determine which of the two to use, or a combination of both?
My rule of thumb is that as soon as you have more than two parameters, you should switch to passing an aggregate of some sort (Array, Hash, Object, Record, whatever) instead. If it's a case of one or two primary parameters and several options, then put just the options into an aggregate and keep the primaries in their own parameters.
When you ask about an array of arguments, I'm assuming you're talking about arguments that are all of the same type (or similar type). It really depends upon the situation and it's a bit of a compromise between convenience for the caller and convenience for the function implementation. That means a lot of it depends upon what you most want to optimize for. There are no hard and fast rules, but you can use this sort of thinking as guidance:
Use separate arguments if:
The number of arguments is relatively small and usually fixed
The code in the receiving function is much cleaner by having named arguments
The typical way the function call is made is not by building argument lists programatically
Use an array if:
The number of arguments is relatively large or usually variable (caller probably wants to build an array and pass that)
The receiving function is cleaner by processing a variable list of arguments in a loop (this can be done with the arguments object too, but is sometimes simpler with an actual array)
A common way that the function call is made is from a list of arguments that is built programmatically (more convenient for the caller to just be able to pass the array).
The called function wants to be able to easily pass the list of arguments to some other function call. While this can be done without the array by processing the arguments object, it takes more code to do if the args aren't passed in an array to start with.
The caller can generally work-around either issue by using .apply() if the function isn't built to take an array, but the caller has arguments in an array.
FYI (though I don't think this was the main subject of your question), another option is to pass an object with a variable number of properties. The options object is particularly useful when there are a number of different arguments and most or all are optional. The called function can contain default values for all options and the caller can just pass the arguments they want to override the default for. The options object generally isn't the best solution for a variable number of the same type of argument that is better represented in an array or as a list of arguments.
Related
Background
I'm the maintainer of a low level library for fast object traversal in Node.js. The focus of the library is speed and it is heavily optimised. However there is one big slowdown: Callback Parameters
The Problem
Callbacks are provided by the library consumer and can be invoked many, many times per scan. For every invocation all parameters are computed and passed to the callback. In most cases only a fraction of the parameters are actually used by the callback.
The Goal
The goal is to eliminate the unnecessary computation of these parameters.
Solutions Ideas
Ideally NodeJs would expose the callback parameters as defined by the callback. However obtaining them doesn't seem to be possible without a lot of black magic (string parsing). It would also not solve the situation where parameters are only required conditionally.
Instead of trying to obtain the parameters from the callback, we could require the callback to expose the required parameters. It sounds very inconvenient and error prone and would also not solve conditionally requires.
We could introduce a different callback for every parameter combination. This sounds like a bad idea.
Instead of passing in the parameters directly, we could pass in a function for each parameter that computes and returns the parameter value. Inside the callback the parameter would then be invoked as required. It's ugly but might be the best approach?
Questions
How do other libraries solve this?
What are other ways this can be solved?
This is a very fundamental design decision and I'm trying to get this right.
Thank you very much for your time! As always appreciated!
You could pass to the callback an object that has various methods on it that the client using the callback could call to fetch whatever parameters they actually need. That way, you'd have a clean object interface and you'd only compute the necessary information that was actually requested.
This general design pattern is sometimes called "lazy computation" where you only do the computation as required. You can use either accessor functions or getters, depending upon the type of interface you want to expose.
For performance reasons, you can perhaps reuse the same object for each time you call the callback rather than building a new one (depends upon details of your implementation).
Note that you don't even have to put all the information needed for the computation into the object itself as the methods on the object can, in some cases, refer to your own local context and locally scoped variables when doing their computation.
However there is one big slowdown: Callback Parameters
Did you actually benchmark this? I doubt constructing the argument values is that costly. Notice that if this is a really heavily used call, V8 might be able to inline it and then optimise away unused argument values.
Ideally NodeJs would expose the callback parameters as defined by the callback.
Actually, it does. If you do want to rely on this property though, you should properly document that you do, otherwise this magic could lead to obscure bugs.
We could introduce a different callback for every parameter combination. This sounds like a bad idea.
It doesn't seem to be that much of a problem to provide two options, filter(key, value) and filterDetailed(key, value, context). If the optimisation is really worth it, and as you say this is a low-level library, just go for it.
Instead of passing in the parameters directly, we could pass in a function for each parameter that computes and returns the parameter value. Inside the callback the parameter would then be invoked as required. It's ugly but might be the best approach?
Constructing a closure object to pass instead of a parameter does have some overhead as well, so you will need to benchmark this properly. It might not be worth it.
However, I see that you are actually passing a single context object as the argument on which the computed values are accessed as properties. In that case, you can simply make these properties getters that will compute the value when they are accessed, not when the object is constructed.
I have a situation in which I would greatly benefit from the use of function caching (memoization).
However, my function takes a single argument, whose value is a very large and multi-dimensional Array.
The standard way to do caching ( and the only way that I can think of) is to create a cache property on the function itself (cache is a hash). Each run of the function, you can check for the existence of myFunc.cache[arg] and simply return that value if it exists, otherwise you perform the calculations as normal and add the argument as a property of cache afterwards.
However, it appears that javascript does not try to evaluate the strings used as hash keys when you are creating them and always just treats them as strings. example
I could apply JSON.stringify to the argument, but because the array will large and nested, I am wondering if there is a more efficient way to identify unique arguments.
This question is basically what I was asking without knowing it:
JavaScript Hashmap Equivalent
In my case, the simplest solution is just to manually make the 0th index of my arrays an ID that I can use as the hash key.
Without a means of doing this, or something similar, you have to either create your own hashmap (outlined in the linked question) or wait for the official Map object to be implemented.
I've heard alot of people saying that accessing the arguments object is expensive. (example: Why was the arguments.callee.caller property deprecated in JavaScript?)
Btw what exactly does that statement mean at all? isn't accessing the arguments object simply a simple property lookup? what exactly is the big deal?
The big deal is at least twofold:
1) Accessing the arguments object has to create an arguments object. In particular, modern JS engines don't actually create a new object for the arguments every time you call a function. They pass the arguments on the stack, or even in machine registers. As soon as you touch arguments, though, they have to create an actual object. This is not necessarily cheap.
2) Once you touch the arguments object, various optimizations that JS engines can otherwise perform (e.g. detecting cases in which you never assign to an argument and optimizing that common case) go out the window. Every access to the function arguments, not just ones through arguments becomes much slower because the engine has to deal with the fact that you might have messed with the arguments via arguments.
I have also never heard a serious explanation for why accessing the arguments object is expensive. However, this site: http://www.playmycode.com/blog/2011/03/simple-yet-effective-javascript-optimisations/ notes that arguments is not really an array and is less efficient than accessing an array. The above linked site even suggests converting arguments to an array as an optimization.
Going to check with those who know JS interpreters more intimately...
Looking at a lot of NodeJS and Javascript code recently, it seems arguments is not an instance of Array but still behaves like one, so people do stuff like Array.prototype.slice.call(arguments, ...) or [].slice.call(arguments) which adds verbosity and increases hurdle for newbies to understand etc.. Is there a reason why arguments isnt an instance of Array or is this just one those bad parts?
NO. arguments is a standalone object that just so happens to have a length property and the ability to use [] to index it. But otherwise, it is just an object, not an Array object.
And yes, this is indeed one of the bad parts of JavaScript.
Since it seems like the first thing people do is convert arguments into a real array, I'm interested in why the Javascript language authors and implementers decided, and continue to think, that arguments should not be a real Array. I don't mean this as flamebait, I'm sincerely interested in the thinking behind it. Since the function is naturally being called when you're in its body, I don't think it's because the objects arguments are referencing can change, like with some of the DOM results...
My conjecture:
The concept of the arguments object has been on the language since the very beginning, it's even described in the ECMAScript First Edition Standard(PDF).
In that version of ECMAScript, the Array.prototype was really basic, array objects contained only 4 methods!: toString, join, reverse and sort.
I think that's one of the major reasons about they make arguments to inherit from Object.prototype, at that time those Array methods didn't look too useful.
But the Array.prototype object was extended in the next versions of the standard, now on ES5, Array objects have methods such as map, reduce, every, some, etc, that are really powerful.
The last year, there was a proposal in ES5 to make arguments inherit from Array.prototype, in the draft stages of the standard, but was dropped off time later.
In those drafts, arguments inherited from Array.prototype, but for backwards compatibility with ES3, the arguments object had defined two own properties, toString and toLocaleString, both pointing to the same methods on Object.prototype, but finally, the committee decided to keep inheriting from Object.prototype.
The arguments object has the very unusual feature that its array-like elements are synonyms for the local variables that hold the function arguments. For example:
function f(x) {
console.log(arguments[0]); // Displays the initial value of the argument x
x = 5; // Changes the value of the local variable x
console.log(arguments[0]); // Now displays 5
}
I always had the impression that this "magical behaviour" is the reason why arguments is not an array.
It's important to note that without one of the designers present, we can only really conjecture why. But we can come up with some decent reasons... here's mine:
From the perspective of a function, one reason could be because you can't - obviously - actually change the arguments that were passed into you. You could change an array that represents the arguments passed into you, but the arguments as they were passed is set in stone before you ever receive execution scope.
You can splice, dice and pop arrays, and if you did that to the arguments object then you just ruined what is conceptually an immutable structure (sad face!). The design of the real arguments object is closer to a kind of immutability JavaScript can offer.
It is similar to querystring parameters. You get a collection handed to you by the client sending the request. It's part of the request information, which is already set and done.
arguments doesn't just return the arguments. It returns callee object, and the array of arguments. If it were just an array, the first element might be the callee object and be more confusing.