Related
I'm trying to recreate the functionality of the underscore _.invoke for learning purposes and I would like to really understand how it works as it seems to be something not too complicated.
The exercise is asking me to return an array with the result of calling "a" method to it. Ok, so here we start.
_.invoke = function (collection, methodName) {
let result = [];
// debugger;
if (Array.isArray(collection)) { // check if collection is an array.
for (let i = 0; i < collection.length; i++) { // iterate over collection
result.push(Array.prototype.methodName.call(collection[i]));
}
}
console.log('result:', result);
return result;
};
I don't know exactly what method is being past to methodName nor if it has any extra arguments to be forwarded (this I understand it would be used in case I'd use a method that requires args like .reduce for instance if I'm not wrong).
As I understand, when I use the .call method on methodName, it should return (push) the iterated element with the "function" applied onto it. Obviously there is something not right, I have used the debugger to see what it does on each step and once it runs the loop and arrives to the call, it quits the loop and runs to check whatever it is it does in the config file of the test.
I get this message in the error log of the HTML file:
_.invoke(mocks.arr, 'testCall').should.eql(mocks.arr);
_.invoke(mocks.obj, 'testCall').should.eql(mocks.objValuesArr);
argsArr = [mocks.arr, mocks.obj];
_.invoke(mocks.arr, 'testArgs', mocks.arr, mocks.obj);
called.should.be.true;
called = false;
argsArr = [mocks.obj, mocks.arr];
_.invoke(mocks.obj, 'testArgs', mocks.obj, mocks.arr);
called.should.be.true;
The this, thisArg and such are still a little hard for me to understand, can someone explain to me what am I missing here..?
So, after some digging, trial and error, I was totally wrong about my approach to the exercise, so I had to re-make the whole thing.
_.invoke = function (collection, methodName) {
// Spread all arguments into a variable.
let args = [...arguments];
// Since the arguments have been all passed to args, we don't need to call them as we normally would.
// Use an already defined function (_.map) with an iteratee to be passed as method.
return _.map(args[0], function (value) {
// Return the iterated value passed through the function of _.map
// and apply the rest of arguments to the element with the function from _.map if there are any.
return value[args[1]].apply(value, args.slice(2));
});
};
I don't know much about underscore.js, but I'm pretty sure _ isn't defined at all, so maybe do window._.invoke = ... instead to properly define it.
Is there a way to allow "unlimited" vars for a function in JavaScript?
Example:
load(var1, var2, var3, var4, var5, etc...)
load(var1)
Sure, just use the arguments object.
function foo() {
for (var i = 0; i < arguments.length; i++) {
console.log(arguments[i]);
}
}
In (most) recent browsers, you can accept variable number of arguments with this syntax:
function my_log(...args) {
// args is an Array
console.log(args);
// You can pass this array as parameters to another function
console.log(...args);
}
Here's a small example:
function foo(x, ...args) {
console.log(x, args, ...args, arguments);
}
foo('a', 'b', 'c', z='d')
=>
a
Array(3) [ "b", "c", "d" ]
b c d
Arguments
0: "a"
1: "b"
2: "c"
3: "d"
length: 4
Documentation and more examples here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters
Another option is to pass in your arguments in a context object.
function load(context)
{
// do whatever with context.name, context.address, etc
}
and use it like this
load({name:'Ken',address:'secret',unused:true})
This has the advantage that you can add as many named arguments as you want, and the function can use them (or not) as it sees fit.
I agree with Ken's answer as being the most dynamic and I like to take it a step further. If it's a function that you call multiple times with different arguments - I use Ken's design but then add default values:
function load(context) {
var defaults = {
parameter1: defaultValue1,
parameter2: defaultValue2,
...
};
var context = extend(defaults, context);
// do stuff
}
This way, if you have many parameters but don't necessarily need to set them with each call to the function, you can simply specify the non-defaults. For the extend method, you can use jQuery's extend method ($.extend()), craft your own or use the following:
function extend() {
for (var i = 1; i < arguments.length; i++)
for (var key in arguments[i])
if (arguments[i].hasOwnProperty(key))
arguments[0][key] = arguments[i][key];
return arguments[0];
}
This will merge the context object with the defaults and fill in any undefined values in your object with the defaults.
It is preferable to use rest parameter syntax as Ramast pointed out.
function (a, b, ...args) {}
I just want to add some nice property of the ...args argument
It is an array, and not an object like arguments. This allows you to apply functions like map or sort directly.
It does not include all parameters but only the one passed from it on. E.g. function (a, b, ...args) in this case args contains
argument 3 to arguments.length
Yes, just like this :
function load()
{
var var0 = arguments[0];
var var1 = arguments[1];
}
load(1,2);
As mentioned already, you can use the arguments object to retrieve a variable number of function parameters.
If you want to call another function with the same arguments, use apply. You can even add or remove arguments by converting arguments to an array. For example, this function inserts some text before logging to console:
log() {
let args = Array.prototype.slice.call(arguments);
args = ['MyObjectName', this.id_].concat(args);
console.log.apply(console, args);
}
Although I generally agree that the named arguments approach is useful and flexible (unless you care about the order, in which case arguments is easiest), I do have concerns about the cost of the mbeasley approach (using defaults and extends). This is an extreme amount of cost to take for pulling default values. First, the defaults are defined inside the function, so they are repopulated on every call. Second, you can easily read out the named values and set the defaults at the same time using ||. There is no need to create and merge yet another new object to get this information.
function load(context) {
var parameter1 = context.parameter1 || defaultValue1,
parameter2 = context.parameter2 || defaultValue2;
// do stuff
}
This is roughly the same amount of code (maybe slightly more), but should be a fraction of the runtime cost.
While #roufamatic did show use of the arguments keyword and #Ken showed a great example of an object for usage I feel neither truly addressed what is going on in this instance and may confuse future readers or instill a bad practice as not explicitly stating a function/method is intended to take a variable amount of arguments/parameters.
function varyArg () {
return arguments[0] + arguments[1];
}
When another developer is looking through your code is it very easy to assume this function does not take parameters. Especially if that developer is not privy to the arguments keyword. Because of this it is a good idea to follow a style guideline and be consistent. I will be using Google's for all examples.
Let's explicitly state the same function has variable parameters:
function varyArg (var_args) {
return arguments[0] + arguments[1];
}
Object parameter VS var_args
There may be times when an object is needed as it is the only approved and considered best practice method of an data map. Associative arrays are frowned upon and discouraged.
SIDENOTE: The arguments keyword actually returns back an object using numbers as the key. The prototypal inheritance is also the object family. See end of answer for proper array usage in JS
In this case we can explicitly state this also. Note: this naming convention is not provided by Google but is an example of explicit declaration of a param's type. This is important if you are looking to create a more strict typed pattern in your code.
function varyArg (args_obj) {
return args_obj.name+" "+args_obj.weight;
}
varyArg({name: "Brian", weight: 150});
Which one to choose?
This depends on your function's and program's needs. If for instance you are simply looking to return a value base on an iterative process across all arguments passed then most certainly stick with the arguments keyword. If you need definition to your arguments and mapping of the data then the object method is the way to go. Let's look at two examples and then we're done!
Arguments usage
function sumOfAll (var_args) {
return arguments.reduce(function(a, b) {
return a + b;
}, 0);
}
sumOfAll(1,2,3); // returns 6
Object usage
function myObjArgs(args_obj) {
// MAKE SURE ARGUMENT IS AN OBJECT OR ELSE RETURN
if (typeof args_obj !== "object") {
return "Arguments passed must be in object form!";
}
return "Hello "+args_obj.name+" I see you're "+args_obj.age+" years old.";
}
myObjArgs({name: "Brian", age: 31}); // returns 'Hello Brian I see you're 31 years old
Accessing an array instead of an object ("...args" The rest parameter)
As mentioned up top of the answer the arguments keyword actually returns an object. Because of this any method you want to use for an array will have to be called. An example of this:
Array.prototype.map.call(arguments, function (val, idx, arr) {});
To avoid this use the rest parameter:
function varyArgArr (...var_args) {
return var_args.sort();
}
varyArgArr(5,1,3); // returns 1, 3, 5
Use the arguments object when inside the function to have access to all arguments passed in.
Be aware that passing an Object with named properties as Ken suggested adds the cost of allocating and releasing the temporary object to every call. Passing normal arguments by value or reference will generally be the most efficient. For many applications though the performance is not critical but for some it can be.
Use array and then you can use how many parameters you need. For example, calculate the average of the number elements of an array:
function fncAverage(sample) {
var lenghtSample = sample.length;
var elementsSum = 0;
for (var i = 0; i < lenghtSample; i++) {
elementsSum = Number(elementsSum) + Number(sample[i]);
}
average = elementsSum / lenghtSample
return (average);
}
console.log(fncAverage([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])); // results 5.5
let mySample = [10, 20, 30, 40];
console.log(fncAverage(mySample)); // results 25
//try your own arrays of numbers
As part of the web app I'm building, multiple commands can come in from the server at once.
When these commands are processed, data will tend to be updated and then fairly heavy page HTML generation can occur.
At the moment when multiple similar commands come in, the program will process the data, then parts of the page are regenerated each time. This can lead to a lot of wasted processing time.
As such, I'm trying to make a callstack, so when a command is processed it looks to see if the function that the command triggers is in the stack and if it isn't found it adds it.
My problem is with keeping the called function from the callstack in the correct scope while also being able to eliminate duplicates.
Base Code:
var tools = {
functions:{
function1: function(){
return this;
},
function2: function(){
}
},
unique: function(arr){
var returnArr=[],
x, i;
for (x=0; x<arr.length; x++){
for (i=(x+1); i<arr.length; i++){
if (arr[x]===arr[i]){
arr.splice(i, 1);
}
}
returnArr.push(arr[x]);
}
return returnArr;
}
}
Example 1:
var callstack=[tools.functions.function1, tools.functions.function1];
callstack = tools.unique(callstack);
for (var x=0; x<callstack.length; x++){
console.log(callstack[x](), "should equal tools.functions");
}
This fails as "this" will return [function()]
Example 2:
var callstack=[
(function(){return tools.functions.function1()}),
(function(){return tools.functions.function1()})
];
callstack = tools.unique(callstack);
for (var x=0; x<callstack.length; x++){
console.log(callstack[x](), "should equal tools.functions");
}
This fails because you can't ensure that the functions are unique, so it'll still run the function twice.
This can be worked around by using two arrays (one that keeps track of the names of the functions, one that holds the encapsulated functions) and keeping them in sync, but I can't help but feel there must be a cleaner way using .call but I can't see it.
JSFiddle: http://jsfiddle.net/Kj6E8/
Generically speaking it is impossible to preserve the value of this in the way that you require. As soon as you push those functions into the array, the object that those functions once resided on is lost.
If you are only calling functions on a single object, you need to bind your functions to that object in some way. There are many ways of doing that, for example, you could pass the object to bind those function to in your unique function.
http://jsfiddle.net/8WAZb/1/
var tools = {
functions:{
function1: function(){
return this;
},
function2: function(){
}
},
unique: function(arr, o){
var returnArr=[],
x, i;
for (x=0; x<arr.length; x++){
for (i=(x+1); i<arr.length; i++){
if (arr[x]===arr[i]){
arr.splice(i, 1);
}
}
returnArr.push(this.bind(arr[x], o));
}
return returnArr;
},
bind: function(f, o) {
return function () {
return f.apply(o, arguments);
}
}
}
console.info("Example 1:");
var callstack=[tools.functions.function1, tools.functions.function1];
callstack = tools.unique(callstack, tools.functions);
console.log("Callstack:",callstack);
for (var x=0; x<callstack.length; x++){
console.log(callstack[x](), "should equal tools.functions");
}
How and when you infer the value of this really depends on when you have that object in scope. Presumably you are more likely to have that object in scope during the callstack creation phase, and perhaps not necessarily at the point where you wish to invoke the functions in the callstack. If that is the case, binding as early as possible (i.e. during the reduction to a unique callstack as demonstrated) seems like a sensible option.
In JavaScript, this is defined from the outside caller, so when you do:
callstack[x]()
You are bringing the window scope into the functions. If you want to bring in the tools.functions object, you need to do:
callstack[x].call(tools.functions);
http://jsfiddle.net/Yv4sM/
According to my research and googling, Javascript seems to lack support for locale aware sorting and string comparisons. There is localeCompare(), but it has been reported of browser specific differencies and impossibility to explicitly set which locale is used (the OS locale is not always the one wanted). There is some intentions to add collation support inside ECMAScript, but before it, we are on our own. And depending how consistent the results are across browsers, may be we are on our own forever :(.
I have the following code, which makes alphabetical sort of an array. It's made speed in mind, and the ideas are got from https://stackoverflow.com/a/11598969/1691517, to which I made some speed improvements.
In this example, the words array has 13 members and the sort-function is called 34 times. I want to replace some of the letters in the words-array (you don't have to know what replacements are made, because it's not the point in this question). If I make these replacements in sort-function ( the one that starts with return function(a, b) ), the code is inefficient, because replacements are made more than once per array member. Of course I can make these replacements outside of this closure, I mean before the line words.sort(sortbyalphabet_timo);, but it's not what I want.
Question 1: Is it possible to modify the words-array in between the lines "PREPARATION STARTS" and "PREPARATION ENDS" so that the sort function uses modified words-array?
Question 2: Is it possible to input arguments to the closure so that code between PREPARATION STARTS and PREPARATION ENDS can use them? I have tried this without success:
var caseinsensitive = true;
words.sort( sortbyalphabet_timo(caseinsensitive) );
And here is finally the code example, and the ready to run example is in http://jsfiddle.net/3E7wb/:
var sortbyalphabet_timo = (function() {
// PREPARATION STARTS
var i, alphabet = "-0123456789AaÀàÁáÂâÃãÄäBbCcÇçDdEeÈèÉéÊêËëFfGgHhIiÌìÍíÎîÏïJjKkLlMmNnÑñOoÒòÓóÔôÕõÖöPpQqRrSsTtUuÙùÚúÛûÜüVvWwXxYyÝýŸÿZz",
index = {};
i = alphabet.length;
while (i--) index[alphabet.charCodeAt(i)] = i;
// PREPARATION ENDS
return function(a, b) {
var i, len, diff;
if (typeof a === "string" && typeof b === "string") {
(a.length > b.length) ? len = a.length : len = b.length;
for (i = 0; i < len; i++) {
diff = index[a.charCodeAt(i)] - index[b.charCodeAt(i)];
if (diff !== 0) {
return diff;
}
}
// sort the shorter first
return a.length - b.length;
} else {
return 0;
}
};
})();
var words = ['tauschen', '66', '55', '33', 'täuschen', 'andern', 'ändern', 'Ast', 'Äste', 'dosen', 'dösen', 'Donaudam-0', 'Donaudam-1'];
$('#orig').html(words.toString());
words.sort(sortbyalphabet_timo);
$('#sorted').html(words.toString());`
Is it possible to modify the words-array in between the lines "PREPARATION STARTS" and "PREPARATION ENDS" so that the sort function uses modified words-array?
No, not really. You don't have access to the array itself, your function only builds the compare-function that is later used when .sort is invoked on the array. If you needed to alter the array, you'll need to write a function that gets it as an argument; for example you could add a method on Array.prototype. It would look like
function mysort(arr) {
// Preparation
// declaration of compare function
// OR execution of closure to get the compare function
arr.sort(comparefn);
return arr;
}
Is it possible to input arguments to the closure so that code between PREPARATION STARTS and PREPARATION ENDS can use them?
Yes, of course - that is the reason to use closures :-) However, you can't use sortbyalphabet_timo(caseinsensitive) with your current code. The closure you have is immediately invoked (called an IIFE) and returns the compare-function, which you pass into sort as in your demo.
If you want sortbyalphabet_timo to be the closure instead of the result, you have to remove the brackets after it. You also you can use arguments there, which are accessible in the whole closure scope (including the comparefunction):
var sortbyalphabet_timo_closure = function(caseinsensitive) {
// Preparation, potentially using the arguments
// Declaration of compare function, potentially using the arguments
return comparefn;
}
// then use
words.sort(sortbyalphabet_timo_closure(true));
Currently, you are doing this:
var sortbyalphabet_timo_closure = function(/*having no arguments*/) {
// Preparation, potentially using the arguments
// Declaration of compare function, potentially using the arguments
return comparefn;
}
var sortbyalphabet_timo = sortbyalphabet_timo_closure();
// then use
words.sort(sortbyalphabet_timo);
…which just caches the result of executing the closure, if you'd need to sort multiple times.
It is common place to see code like that around the web and in frameworks:
var args = Array.prototype.slice.call(arguments);
In doing so, you convert the arguments Object into a real Array (as much as JS has real arrays anyway) and it allows for whatever array methods you have in your Array prototypes to be applied to it, etc etc.
I remember reading somewhere that accessing the arguments Object directly can be significantly slower than an Array clone or than the obvious choice of named arguments. Is there any truth to that and under what circumstances / browsers does it incur a performance penalty to do so? Any articles on the subject you know of?
update interesting find from http://bonsaiden.github.com/JavaScript-Garden/#function.arguments that invalidates what I read previously... Hoping the question gets some more answers from the likes of #Ivo Wetzel who wrote this.
At the bottom of that section it says:
Performance myths and truths
The arguments object is always created
with the only two exceptions being the
cases where it is declared as a name
inside of a function or one of its
formal parameters. It does not matter
whether it is used or not.
this goes in conflict with http://www.jspatterns.com/arguments-considered-harmful/, which states:
However, it's not a good idea to use
arguments for the reasons of :
performance
security
The arguments object is not automatically created every time the function is called, the JavaScript engine will only create it on-demand, if it's used. And that creation is not free in terms of performance. The difference between using arguments vs. not using it could be anywhere between 1.5 times to 4 times slower, depending on the browser
clearly, can't both be correct, so which one is it?
ECMA die-hard Dmitrty Soshnikov said:
Which exactly “JavaScript engine” is
meant? Where did you get this exact
info? Although, it can be true in some
implementations (yep, it’s the good
optimization as all needed info about
the context is available on parsing
the code, so there’s no need to create
arguments object if it was not found
on parsing), but as you know
ECMA-262-3 statements, that arguments
object is created each time on
entering the execution context.
Here's some q&d testing. Using predefined arguments seems to be the fastest, but it's not always feasible to do this. If the arity of the function is unknown beforehand (so, if a function can or must receive a variable amount of arguments), I think calling Array.prototype.slice once would be the most efficient way, because in that case the performance loss of using the arguments object is the most minimal.
The arguments has two problems: one is that it's not a real array. The second one is that it can only include all of the arguments, including the ones that were explicitly declared. So for example:
function f(x, y) {
// arguments also include x and y
}
This is probably the most common problem, that you want to have the rest of the arguments, without the ones that you already have in x and y, so you would like to have something like that:
var rest = arguments.slice(2);
but you can't because it doesn't have the slice method, so you have to apply the Array.prototype.slice manually.
I must say that I haven't seen converting all of the arguments to a real array just for the sake of performance, only as a convenience to call Array methods. I'd have to do some profiling to know what is actually faster, and it may also depend faster for what, but my guess would be that there's not much of a difference if you don't want to call the Array methods in which case you have no choice but to convert it to a real array or apply the methods manually using call or apply.
The good news is that in new versions of ECMAScript (Harmony?) we'll be able to write just this:
function f(x, y, ...rest) {
// ...
}
and we'll be able to forget all of those ugly workarounds.
No one's done testing on this in a while, and all the links are dead. Here's some fresh results:
var res = []
for(var i = 0, l = arguments.length; i < l; i++){
res.push(arguments[i])
}
}
function loop_variable(){
var res = []
var args = arguments
for(var i = 0, l = args.length; i < l; i++){
res.push(args[i])
}
return res
}
function slice(){
return Array.prototype.slice.call(arguments);
}
function spread(){
return [...arguments];
}
function do_return(){
return arguments;
}
function literal_spread(){
return [arguments[0],arguments[1],arguments[2],arguments[3],arguments[4],arguments[5],arguments[6],arguments[7],arguments[8],arguments[9]];
}
function spread_args(...args){
return args;
}
I tested these here: https://jsben.ch/bB11y, as do_return(0,1,2,3,4,5,6,7,8,9) and so on. Here are my results on my Ryzen 2700X, on Linux 5.13:
Firefox 90.0
Chromium 92.0
do_return
89%
100%
loop_variable
74%
77%
spread
63%
29%
loop
73%
94%
literal_spread
86%
100%
slice
68%
81%
spread_args
100%
98%
I would argue against the accepted answer.
I edited the tests, see here: http://jsperf.com/arguments-performance/6
I added the test for slice method and a test for memory copy to preallocated array. The latter is multiple times more efficient in my computer.
As You can see, the first two memory copy methods in that performance test page are slow not due to loops, but due to the push call instead.
In conclusion, the slice seems almost the worst method for working with arguments (not counting the push methods since they are even not much shorter in code than the much more efficient preallocation method).
Also it might be of interest, that apply function behaves quite well and does not have much performance hit by itself.
First existing test:
function f1(){
for(var i = 0, l = arguments.length; i < l; i++){
res.push(arguments[i])
}
}
Added tests:
function f3(){
var len = arguments.length;
res = new Array(len);
for (var i = 0; i < len; i++)
res[i] = arguments[i];
}
function f4(){
res = Array.prototype.slice.call(arguments);
}
function f5_helper(){
res = arguments;
}
function f5(){
f5_helper.apply(null, arguments);
}
function f6_helper(a, b, c, d){
res = [a, b, c, d];
}
function f6(){
f6_helper.apply(null, arguments);
}