JavaScript, Understand why `foreach` implementaion slow than `for` - javascript

i have write a forEach implemention for understand this Response:
function myForeach(sourch, func) {
for (var i = 0, len = sourch.length; i < len; i++) {
func(sourch[i], i, arr);
}
}
and like forEach, its slow than simple for-loop:
for (var i = 0, len = arr.length; i < len; i++) {
(function(item) {
action(item);
})(arr[i], i, arr); //exactly what foreach dose!
}
here, the two way have function setup & teardown.
why the for its so faster?

I think it's largely related to the fact that your action() is a no-op which gets optimized away by the runtime in some cases, but not in other cases.
If you change your action to actually do something (e.g. add the array values to a sum variable), the differences are a lot smaller.
Here are my updated benchmark results, also on Chrome:

When declaring an anonymous function in a loop i.e. 'function() { // doStuff}' the interpreter has to define a new function at runtime every iteration of the loop, thus creating alot of overhead for the interpreter.

Related

Is there a difference between a for loop without increment vs. one with a non incremented variable?

In JS, is there any difference between the following two types of loops?
A for loop without an increment such as:
for (i = 0; i < 1000;) { // i will be changed inside the block
vs
A for loop with a variable defined, but one that is not being incremented? Like this:
for (i = 0; i < 1000; i) { // i changed inside block
I saw this strange construct in several Stack Overflow questions.
i meant to note the i variable changes, not that i myself changed something or that i have been changed in some way.
Under normal circumstances: no, there is no difference.
The for statement creates a loop that consists of three optional
expressions, enclosed in parentheses and separated by semicolons,
followed by a statement (usually a block statement) to be
executed in the loop.
The three expressions (initialization, condition and final-expression) are all optional (unlike the semicolons themselves), so if you omit the final-expression (which is most commonly used to increment the i/index/counter) that part simply will not be used.
If you use an isolated i as the final-expression, then this will be evaluated after every iteration of the for-loop. But an isolated variable on its own usually doesn't have any side effects. The value wont change and the value that is returned by the i expression (the value of i) is ignored in case of the final-expression.
console.log('For loop without final-expression:');
for (let i = 0; i < 10;) {
console.log(i++);
}
console.log('For loop with final-expression:');
for (let i = 0; i < 10; i) {
console.log(i++);
}
But that is all under normal circumstances. There are cases where it does make a difference, but you really should never encounter this in production-ready code.
const target = {
i: null,
};
const proxy = new Proxy(target, {
get(target, prop) {
if (prop === 'i') {
return target._i++;
}
return Reflect.get(...arguments)
},
set(target, prop, value) {
if (prop === 'i') {
target._i = value;
} else {
return Reflect.set(...arguments);
}
}
});
with(proxy) {
console.log('For loop without final-expression:');
for (i = 0; i < 10;) {
console.log("loop");
}
console.log('For loop with final-expression:');
for (i = 0; i < 10; i) {
console.log("loop");
}
}
In the above example I used a Proxy to be able to intercept all access to the properties of an object and I used a with statement so that every variable is treated as a property of this proxied object. As a result, when i is read, the getter of the proxy is called, and I increment the value of i every time this happens.
In for (i = 0; i < 10; i), i is set once at i = 0 and read twice in i < 10 and the final-expression i. Because it is read twice, the i will be incremented twice, so the loop will only iterate 5 times. In the other for (i = 0; i < 10;) loop i is only read once, so the loop will iterate 10 times.
There are likely more ways to achieve a similar effect, but again you should really never encounter this in production ready code, so it shouldn't be taken into account when deciding if you want to keep the i in the final-expression or not.
Personally I would say that if you don't use an optional expression, then you should leave it out. Especially because when glancing over the code, i can be misread as i++ which might confuse the reader. But in the end it is a matter of taste.

Does underscore.js creates anonym func objects in _.each that needs to be garbaged?

I use a lot _.each instead of the for loop in my code. Does _.each function from the Underscore.js library create a new anonymous function on each call? Eg.
_.each(this.sprites, function(sprite) {
sprite.draw();
this.spriteDraws++
}, this);
Does the Unerscore.js create a new anonymous function on each frame of the application loop? Does this function needs to be garbage collected?
I have noticed that my application creates a buch of new objects each frame and am not able to root them out (a nonascending sawtooth pattern - on each frame, the same number of objects gets created and afterwards garbaged periodically). Maybe this could be a reason.
Thanks!
UPDATE:
As stupid as it may seem, I've realized that I am the culprit. In every call to _.each, I am creating a new anonymous function and passing it to the Underscore.js. Thank you Bergi on confirming it. I've tested the code and it already creates less objects.
An updated question:
Should one rather predifine a function and put it in a variable for the reuse? Or should one rather use a more traditional for loop when dealing witha arrays?
No. It doesn't
From: https://github.com/jashkenas/underscore/blob/master/underscore.js
// The cornerstone, an `each` implementation, aka `forEach`.
// Handles raw objects in addition to array-likes. Treats all
// sparse array-likes as if they were dense.
_.each = _.forEach = function(obj, iteratee, context) {
iteratee = optimizeCb(iteratee, context);
var i, length;
if (isArrayLike(obj)) {
for (i = 0, length = obj.length; i < length; i++) {
iteratee(obj[i], i, obj);
}
} else {
var keys = _.keys(obj);
for (i = 0, length = keys.length; i < length; i++) {
iteratee(obj[keys[i]], keys[i], obj);
}
}
return obj;
};

Javascript loop looses context in recursion

Here's an example:
function try2RecurseIt() {
var MyArr = [[1,[2,3],4],[5,6],[7,8,[9,0]]];
letsDance(MyArr);
}
function letsDance(arr) {
for (i=0; i<arr.length; i++) {
console.log("arr["+i+"] "+ arr[i]);
if (arr[i].constructor.toString().indexOf("Array") > -1) { // isArray check
letsDance(arr[i]);
}
}
}
I expected it would loop through all elements, but the result:
// arr[0] 1,2,3,4
// arr[0] 1
// arr[1] 2,3
// arr[0] 2
// arr[1] 3
Somewhere my letsDance() function loses context and I can't figure out where. What am I doing wrong?
Yes, I know about forEach, but I'm trying to understand where I'm wrong here. Further I'm going to use this method to parse htmlElements.children[n] which are not arrays.
I'm learning JavaScript and need to understand the basics without using any libraries.
Your problem is failure to keep variables local:
for (i=0; i<arr.length; i++) {
here, i becomes global on the first loop so subsequent loops increment it, use:
for (var i=0; i<arr.length; i++) {
The bug in your code is that i is a global variable. Therefore each call to the function rewrites the same i and i keeps being reset to 0.

Check the length of an array before of to do a loop in JavaScript

Does make sense to check if the length of the array is not equal to 0 before of to do the loop?
var arr = [1,2,3];
if (arr.length !== 0) {
// do loop
}
In the general case it doesn't make sense. For a foreach, for, or while loop, the loop condition will prevent the loop executing anyway, so your check is redundant.
var arr = [];
for (var loopCounter = 0; loopCounter < arr.length; loopCounter++)
{
// This loop will never be entered.
}
foreach (var element in arr)
{
// This loop will never be entered.
}
var loopCounter = 0;
while (loopCounter < arr.length)
{
// This loop will never be entered.
loopCounter++;
}
However, the only time it is important is if you are using a do...while loop. In this scenario, the loop executes, and then you check your loop-condition (when it is too late). It's likely that your code would have thrown an exception inside the loop in this case, as demonstrated in the following code.
var arr = [];
var loopCounter = 0;
do
{
someMethod(arr[loopCounter]); // This will throw an error
loopCounter++;
}
while(loopCounter < arr.length);
No, the check is unnecessary. If the array has zero elements, the loop will execute zero times (assuming it is written correctly).
If the array could be null, then checking for that would be valid, e.g.
if(arr !== null)
{
// loop
}
Well, it depends on the loop. If it's of the form:
for (i = 0; i < arr.length; i++) {
do_something_with (arr[i]);
}
then, no, the if is superfluous, the for loop body will not execute in this case.
However, if you've done something like (and this seems likely given that you mention a do loop, although you may have actually meant "do the loop", so it's hard to say):
i = 0;
do {
do_something_with (arr[i]);
i++;
while (i < arr.length);
then, yes, you'll need it to avoid the problem of using arr[0] if no such beast exists.
But I would consider that second case (check-after) "broken" since the while () {} or for () {} variants (check-before) would be more natural in that case.

Three map implementations in javascript. Which one is better?

I wrote a simple map implementation for some task. Then, out of curiosity, I wrote two more. I like map1 but the code is kinda hard to read. If somebody is interested, I'd appreciate a simple code review.
Which one is better? Do you know some other way to implement this in javascript?
var map = function(arr, func) {
var newarr = [];
for (var i = 0; i < arr.length; i++) {
newarr[i] = func(arr[i]);
}
return newarr;
};
var map1 = function(arr, func) {
if (arr.length === 0) return [];
return [func(arr[0])].concat(funcmap(arr.slice(1), func));
};
var map2 = function(arr, func) {
var iter = function(result, i) {
if (i === arr.length) return result;
result.push(func(arr[i]));
return iter(result, i+1);
};
return iter([], 0);
};
Thanks!
EDIT
I am thinking about such function in general.
For example, right now I am going to use it to iterate like this:
map(['class1', 'class2', 'class3'], function(cls) {
el.removeClass(cls);
});
or
ids = map(elements, extract_id);
/* elements is a collection of html elements,
extract_id is a func that extracts id from innerHTML */
What about the map implementation used natively on Firefox and SpiderMonkey, I think it's very straight forward:
if (!Array.prototype.map) {
Array.prototype.map = function(fun /*, thisp*/) {
var len = this.length >>> 0; // make sure length is a positive number
if (typeof fun != "function") // make sure the first argument is a function
throw new TypeError();
var res = new Array(len); // initialize the resulting array
var thisp = arguments[1]; // an optional 'context' argument
for (var i = 0; i < len; i++) {
if (i in this)
res[i] = fun.call(thisp, this[i], i, this); // fill the resulting array
}
return res;
};
}
If you don't want to extend the Array.prototype, declare it as a normal function expression.
As a reference, map is implemented as following in jQuery
map: function( elems, callback ) {
var ret = [];
// Go through the array, translating each of the items to their
// new value (or values).
for ( var i = 0, length = elems.length; i < length; i++ ) {
var value = callback( elems[ i ], i );
if ( value != null )
ret[ ret.length ] = value;
}
return ret.concat.apply( [], ret );
}
which seems most similar to your first implementation. I'd say the first one is preferred as it is the simplest to read and understand. But if performance is your concern, profile them.
I think that depends on what you want map to do when func might change the array. I would tend to err on the side of simplicity and sample length once.
You can always specify the output size as in
var map = function(arr, func) {
var n = arr.length & 0x7fffffff; // Make sure n is a non-neg integer
var newarr = new Array(n); // Preallocate array size
var USELESS = {};
for (var i = 0; i < n; ++i) {
newarr[i] = func.call(USELESS, arr[i]);
}
return newarr;
};
I used the func.call() form instead of just func(...) instead since I dislike calling user supplied code without specifying what 'this' is, but YMMV.
This first one is most appropriate. Recursing one level for every array item may make sense in a functional language, but in a procedural language without tail-call optimisation it's insane.
However, there is already a map function on Array: it is defined by ECMA-262 Fifth Edition and, as a built-in function, is going to be the optimal choice. Use that:
alert([1,2,3].map(function(n) { return n+3; })); // 4,5,6
The only problem is that Fifth Edition isn't supported by all current browsers: in particular, the Array extensions are not present in IE. But you can fix that with a little remedial work on the Array prototype:
if (!Array.prototype.map) {
Array.prototype.map= function(fn, that) {
var result= new Array(this.length);
for (var i= 0; i<this.length; i++)
if (i in this)
result[i]= fn.call(that, this[i], i, this);
return result;
};
}
This version, as per the ECMA standard, allows an optional object to be passed in to bind to this in the function call, and skips over any missing values (it's legal in JavaScript to have a list of length 3 where there is no second item).
There's something wrong in second method. 'funcmap' shouldn't be changed to 'map1'?
If so - this method loses, as concat() method is expensive - creates new array from given ones, so has to allocate extra memory and execute in O(array1.length + array2.length).
I like your first implementation best - it's definitely easiest to understand and seems quick in execution to me. No extra declaration (like in third way), extra function calls - just one for loop and array.length assignments.
I'd say the first one wins on simplicity (and immediate understandability); performance will be highly dependent on what the engine at hand optimizes, so you'd have to profile in the engines you want to support.

Categories

Resources