As javascript developers we all have to write a lot of for loops. Before a couple of months I saw an alternative syntax, which I really liked. However, I'm now interested, is there any other nice way.
Let's say that I have an array of data representing users in a system. What I did before is:
var users = [
{ name: "A"},
{ name: "B"},
{ name: "C"},
{ name: "D"},
{ name: "E"}
];
var numOfUsers = users.length;
for(var i=0; i<numOfUsers; i++) {
var user = users[i];
// ...
}
There is one additional row var user = users[i];. Normally I feel more comfortable if I have user instead of users[i]. So, the new way:
for(var i=0; user=users[i]; i++) {
// ...
}
I'm also wondering if the second approach produces problems in some of the browsers. One of my colleagues reported that this syntax is a little bit buggy under IE.
Edit:
Thankfully, the answers below pointed me out to the right direction. If some of the elements of the array is falsy then the loop will stop. There is some kind of solution:
for(var i=0; typeof (user=users[i]) !== "undefined"; i++) {
// ...
}
But that's too much for me. So, I guess that I'll use this syntax only when I'm 100% sure that all the elements are truly (which means never :)).
In your “new” approach, you don’t need numOfUsers any more.
As for the potential problems: This approach relies on all users[i] having values evaluating to true for the loop to continue (and user becoming undefined, equal to false and therefor ending the loop after the last user is processed) – but sometimes you might have data where not every record evaluates to true, but “false-y” values might also occur in the data – and in that case, this approach of course fails.
The problem with this approach:
for(var i=0; user=users[i]; i++) {
// ...
}
...is that it assumes user won't be "falsey" (0, "", null, undefined, NaN, or of course false) until you've gone past the end of the array. So it'll work well with an array of non-null object references, but if you then get in the habit of using it, it will bite you when you have an array of numbers, or strings, or such.
The other reason not to declare variables within the for construct is that it's misleading: Those variables are not scoped to the for loop, they're function-wide. (JavaScript's var doesn't have block scope, only function or global scope; ES6 will get let which will have block scope.)
On modern JavaScript engines (or with an "ES5 shim"), you can of course do this:
users.forEach(function(user) {
// ...
});
...which has the advantage of brevity and not having to declare i or numUsers or even user (since it's an argument to the iteration callback, and nicely scoped to that). If you're worried about the runtime cost of doing a function call for each entry, don't be. It'll be washed out by whatever actual work you're doing in the function.
I'm amazed if the second syntax works at all your middle operation should evaluate to true for each loop you want to complete and false as soon as you want to be done looping. As for any issues with your first for loop, a JavaScript is function scoped so that inner var statement will still leak to the containing function (as well as that i). This is different than most other languages that have block scoping. It's not so much of a problem but something to keep in mind if you are debugging.
If you are already using jQuery, you can use the jQuery.each function to loop over your arrays.
In any case you can look at the source code of that function and copy the relevant parts for your own foreach function: http://james.padolsey.com/jquery/#v=1.10.2&fn=jQuery.each
Related
So I have a fairly good amount of experience in coding. I've dabbled in Basic, HTML, Javascript, C, and C++, though the ones I've been using most recently are HTML and Javascript.
I am incredibly familiar with the for-loop. I've used it many times to loop through arrays, to operate recursive functions, etc. I know what it does and how to use it, but my question is about how it works.
Premise
In most languages, the basic syntax of a for loop is such:
var upperLimit = 10;
for(var i = 0; i < upperLimit; i++) {
/*Code to be executed*/
console.log(i);
}
In Javascript, this will output the numbers from 0 to 9 in the console.
I know that the parentheses contains 3 parts, each separated by semicolons.
The first is the initialization, which typically sets up the variables to be used to loop the statements.
The second is the condition, which runs before any of the code between the curly braces is executed. If it results in a True, the code is executed. Otherwise, the for-loop stops.
The third is the increment, which contains the last bit of code to be executed in the loop, and, by extension, the last thing executed before the next condition check.
Question
So, again, my question is how strict are these definitions?
The initialization's definition doesn't allow for much. It just says that that line is executed once, it's executed before anything else in the loop, and it's scope is limited to the loop. I can't think of much else you'd want to put in that position other than an iterative variable.
But what about the other two? I've seen codes where the condition is simply a variable, and as long as it's positive (since positive numbers taken as a boolean just covert to true), the loop continues.
Then there's the increment, which seems to be the loosest of these parts. Is it really just the last thing to be executed in a code, or does it explicitly need to iterate the variable declared in the initialization? It seems to be the former for the languages I'm familiar with.
For example, I decided to make a non-standard for-loop, and I came up with this routine:
var numbers = [0,1,2,3,4,5,6,7,8,9];
for(var i = 0;
numbers.length;
console.log(numbers.pop())) {}
It runs exactly as I expected: It outputs each member of the numbers array in the console in descending order, leaving an empty numbers array afterwards, and it's done using what is basically an empty for-loop.
Ending
So are my assumptions correct? If so, are there any practical applications for using a for-loop in a format apart from the one I wrote at the top of this question (possibly closer to he second format)?
Before all, you give a array
var numbers = [0,1,2,3,4,5,6,7,8,9];
The codes below is a correct for loop.
for(var i = 0;
numbers.length;
console.log(numbers.pop())) {}
Javascript defined for like this
for ([initialization]; [condition]; [final-expression])
statement
For you code initialization is 'var i = 0', and execute once at start of loop.
The condition is 'numbers.length', and value is 10. When a number not 0, Javascript will convert it to boolean true. So condition is true.
The final-expression is 'console.log(numbers.pop())'. When execute 'numbers.pop()', numbers.length change to 9. But it still is true.
At second time, condition will return true again. The final-expression is execute too.
Until numbers.length become 0, Javascript convert it to boolean false. The loop will end.
The scope of the initialized variable is not limited to the loop, it's valid for the whole function (undefined before that line). You can initialize multiple variables using a comma. for (var i=0, j=1; i < 10; i++)
The second part, anything that evaluates to a truthy value will cause the loop to keep going:
Truthy: 1, -1, true, "something", {a: 2}
Falsey: 0, false, null, undefined
You could omit this and rely on a break in your code
The third part just lets you update the looping variable, you could omit it and do it within the for loop body.
Here's an answer that provides a nice way to loop that is non-standard, and comes with caveats, please see the link.
var list = [{a:1,b:2}, {a:3,b:5}, {a:8,b:2}, {a:4,b:1}, {a:0,b:8}];
for (var i=0, item; item = list[i]; i++) {
// Look no need to do list[i] in the body of the loop
console.log("Looping: index ", i, "item" + item);
}
In most languages, the basic syntax of a for loop is such:
for(initialization; condition; iteration) {
/*Code to be executed*/
}
Both three are usual expressions and you can use any valid expressions here:
for(
var i=arr.length, othercond=true;
i;
othercond?i--:i++, console.log(i),updateothercond()
);
It is hard to Google for some keywords like "with" word, so I am testing to ask here.
Is the with statement in JavaScript inefficient?
For instance, say I have:
with(obj3) {
with(obj2) {
with(obj1) {
with(obj0) {
eval("(function() { console.log(aproperty) })();");
}
}
}
}
Would the above be more or less efficient, if for instance, I walked over obj0, obj1, obj2, obj3 and merged them together, and then used either:
One with statement alone
Created a parameters string with the keys of obj0, obj1, obj2 and obj3, and an args array for the values and used:
eval("function fn(aproperty, bproperty) { console.log(aproperty); }")
fn.apply(undefined, args);
Which of these three approaches can be deemed to be quicker? I am guessing on with statements but so many with's makes me think I can optimize it further.
If you're looking for options, then you may want to consider a third approach, which would be to create (on the fly if needed) a prototype chain of objects.
EDIT: My solution was broken. It requres the non-standard __proto__ property. I'm updating to fix it, but be aware that this isn't supported in all environments.
var objs = [null,obj3,obj2,obj1,obj0];
for (var i = 1; i < objs.length; i++) {
objs[i].__proto__ = Object.create(objs[i-1]);
}
var result = objs.pop();
This avoids with and should be quicker than merging, though only testing will tell.
And then if all you needed was a product of certain properties, this will be very quick.
var props = ["x2","b1","a3"];
var product = result.y3;
for (var i = 0; i < props.length; i++)
product *= result[props[i]];
Newer browsers have an internal tokening mechanism to make the javascript interpretation cheaper. It is very like JIT in the newer JVMs. I think there isn't a much problem with your deeply embedded with-s, practically it will be some like
__get_aproperty() {
if (obj0.has("aproperty")) return obj0.aproperty;
if (obj1.has("aproperty")) return obj1.aproperty;
if (obj2.has("aproperty")) return obj2.aproperty;
if (obj3.has("aproperty")) return obj3.aproperty;
}
So, the structure of your js is highly embedded, but the structure of the real execution in the JS-engine of the browsers, will be simple and linear.
But the tokenization of the JS, that is costly. And if the JS-engine finds an eval, needs to tokenize.
I voted for the first version.
With statement will make your code run like it's 1980 - literally every optimization implemented in a JIT cannot be used when it's in effect.
So I am just wondering why the following code dosen't work. I am looking for a similar strategy to put the for loop in a variable.
var whatever = for (i=1;i<6;i++) {
console.log(i)
};
Thanks!
Because a for loop is a statement and in JavaScript statements don't have values. It's simply not something provided for in the syntax and semantics of the language.
In some languages, every statement is treated as an expression (Erlang for example). In others, that's not the case. JavaScript is in the latter category.
It's kind-of like asking why horses have long stringy tails and no wings.
edit — look into things like the Underscore library or the "modern" add-ons to the Array prototype for "map" and "reduce" and "forEach" functionality. Those allow iterative operations in an expression evaluation context (at a cost, of course).
I suppose what you look for is function:
var whatever = function(min, max) {
for (var i = min; i < max; ++i) {
console.log(i);
}
}
... and later ...
whatever(1, 6);
This approach allows you to encapsulate the loop (or any other code, even declaring another functions) within a variable.
Your issue is that for loops do not return values. You could construct an array with enough elements to hold all the iterations of your loop, then assign to it within the loop:
arry[j++] = i;
You can do this, but it seems that you might want to check out anonymous functions. With an anonymous function you could do this:
var whatever = function(){
for (var i=1;i<6;i++) {
console.log(i);
}
};
and then
whatever(); //runs console.log(i) i 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);
}
I'm writing a Javascript function that would manipulate an array written on-the-fly and sent as a parameter.
The function is written as follows:
function returnJourney(animation,clean){
var properties = {};
// loads of other inane stuff
for(i in animation[0]) properties[animation[0][i]] = animation[0].i;
// heaps more inane stuff
}
The animation in question is a set of parameters for a jQuery animation. Typically it takes the format of ({key:value,key:value},speedAsInteger,modifierAsString).
So to kick off initial debugging I call it with:
returnJouney(({'foo':'bar'},3000),1);
And straight off the bat things are way off. As far as I see it this would have returnJourney acknowledge clean === 1, and animation being an array with an object as its first child and the number 3000 as its second.
Firebug tells me animation evaluates as the number 3000. What am I doing wrong?
properties[animation[0][i]] = animation[0].i;
should be
properties[animation[0][i]] = animation[0][i];
.i is the property literally called 'i'. As that (probably) doesn't exist, you'll be assigning undefined to each property.
returnJouney(({'foo':'bar'},3000),1);
also makes little sense — do you mean an array?:
returnJourney([{'foo':'bar'},3000],1);
(there is no ‘tuple’ type in JavaScript.)
Also, use var i in rather than the (typo) in in. Forgetting var gives you an accidental global, with potentially annoying-to-debug side-effects.
There's no tuple type in JavaScript. All you have is either object {} or array []. Both of them can understand any mixture of types. So you can either pass your animation parameter as array ([{'foo':'bar'},3000]), which looks like exactly what you wanted.
Or, as it usually done in JavaScript, use object instead:
returnJourney({props: {foo: "bar"}, speed: 3000}, 1);
function returnJourney(animation, clean) {
var props = animation.props;
var speed = animation.speed;
}
Note that object notation let you ignore things you don't want to pass and makes it very clear what value means what.
As for why your animation resolves as 3000, it is really simple, this is how , operator works. It returns the last thing in braces. So (10, 20, 30) would evaluate to 30, so will (f(1000), "hello", 30). Only last value matters, others just ignored (but run anyway, so any side effects will be there).
It's treating ({'foo':'bar'},3000) as an expression using the comma operator, which returns the right operand as its result. Perhaps you meant [{'foo':'bar'},3000].