How to optimize JS code? - javascript

Chrome profiling say: "Not optimized: assignment to parameter in arguments object". What can i do for optimize this code?
this.buffer.forEach(function(tilepos, ypos)
{
tilepos.forEach(function(tileinfo, xpos)
{
_self.tiles.putTile('ground', xpos, ypos, _self.ground);
});
});

It likely does not like that you are acting on tilepos within the block.
I would recommend doing it as follows if you wish to eliminate the error, but also get a bit of a performance boost:
for(var i = 0; i < this.buffer.length; i++)
{
for(var j = 0; j < this.buffer[i].length; j++)
{
_self.tiles.putTile('ground', i, j, _self.ground);
}
}

You are most likely overwriting the arguments by reassigning a function parameter or an arguments element.
function f(a) {
a = 100; // reassigns arguments[0]
};
Or
function f(a) {
arguments[0] = 100; // same thing
};
Your code looks fine to me, so I assume the problem lies in your putTile method. If you need to reassign the function parameters, create a local copy of it:
function f(a) {
var localA = a;
localA = 100;
}
If you provide the putTilemethod then I can take a look and update my answer.

Related

Abstracting some two deep loop procedure

In my project I have to do some two-deep loop procedure several (meaning a lot of) times. I'll have to do the same:
for (var i = 0; i < length; i++) {
something_here_maybe;
for (var j = 0; j < second_length; j++) {
something_else_here;
}
perhaps_other_thing_here;
}
Now I don't want to keep doing that, so I tried some:
function traverse(before, inside, after) {
for (var i = 0; i < length; i++) {
(before) ? before(i) : null;
for (var j = 0; j < second_length; j++) {
(inside) ? inside(i, j) : null;
}
(after) ? after(i) : null;
}
}
Of course, that seemed much more desirable for me, given that I thought I could do something like:
traverse(function(x) { blabla; }, function(x, y) { blabla; }, function(x) { blabla; });
Mbut ... I simply got to the point where those three functions need to interact with one another. And the variables in them are local - so they can't interact. I'd need to define those variables in traverse(), but I don't know beforehand what variables I'll need. I'd try to define another "initialize" parameter in traverse (as the first argument) which would be a function that initializes those values. But it would still be a function, and those variables would still be local to it, not taken by traverse();
Could you help me with any ideas about this approach ? Or it simply can't be done ? Any idea or advice would be much appreciated. Thank you in advance.
You could use inner functions to accomplish what you are describing, so, if the function traverse takes a parameter based on which the functions A, B and C get defined, define them by writing a function inside the traverse function which returns the three functions as it's return value, (you can return an array with the three functions in them) and then invoke the three functions in your loop.
example:
function traverse(param) {
function defineProcedures(args) {
/* local vars which have function scope(visible to the entire defineProcedures
body
*/
var funcA = function(params) { //blah };
var funcB = function(params) { //blah };
var funcC = function(params) { //blah };
return [funcA, funcB, funcC];
}
var procs = defineProcedures(param);
var firstFunc = procs[0],
secondFunc = procs[1],
thirdFunc = procs[2];
//for loops go here and invoke the functions appropriately.
}

Pass variable to closure on event trigger

I have a loop where I define a closure for an event:
for (var i = 0; i < 3; i++) {
obj.onload = function(e) {
me.myFunction(e, i);
};
}
I need to pass that counter variable to the function that is called when the load event is triggered. I always get the same value being passed.
How can I pass the right counter value to the function?
The usual way to solve this is with a builder function, so that the handler closes over something that doesn't change:
function buildHandler(index) {
return function(e) {
me.myFunction(e, index);
};
}
for (var i = 0; i < 3; i++) {
obj.onload = buildHandler(i);
}
You can do that with an immediately-invoked function expression (IIFE), but in theory it creates and throws away a function on every loop (I expect most JavaScript engines these days optimize that) and I, at least, think it's a lot harder to read:
for (var i = 0; i < 3; i++) {
obj.onload = (function(index) {
return function(e) {
me.myFunction(e, index);
};
})(i);
}
There's an ES5 feature, Function#bind, that you can use if you want to use me.myFunction directly, but you'd have to change the order in which you were expecting the arguments (index first, then the event):
for (var i = 0; i < 3; i++) {
obj.onload = me.myFunction.bind(me, i);
}
bind creates a function that, when called, calls the original function with this set to the first argument (so, me above), then any further arguments you give it, and then any arguments that were used when calling the function. (That's why the order myFunction was expecting them had to change to index, e rather than e, i.)
On older browsers, you need a shim for Function#bind.

JavaScript: Can you substitute variables into anonymous functions on creation? [duplicate]

This question already has answers here:
Closed 12 years ago.
Possible Duplicate:
Javascript closure inside loops - simple practical example
Rather than explaining the question, I'll give an example:
for (var i = 0; i < 100; i ++) {
get_node(i).onclick = function() {
do_something_very_important(i);
}
}
Is there any way to have the value of i substituted into the function upon creation rather than execution? Thanks.
Yes, you can, but that won't work for the example you provided. You would be having a very common closure problem in that for loop.
Variables enclosed in a closure share the same single environment, so by the time the onclick callback is called, the for loop will have run its course, and the i variable will be left pointing to the last value it was assigned. In your example, the do_something_very_important() function will be passed the value 100 for each node, which is not what you intend.
You can solve this problem with even more closures, using a function factory:
function makeClickHandler(i) {
return function() {
do_something_very_important(i);
};
}
// ...
for(var i = 0; i < 100; i++) {
get_node(i).onclick = makeClickHandler(i);
}
This can be quite a tricky topic, if you are not familiar with how closures work. You may want to check out the following Mozilla article for a brief introduction:
Mozilla Dev Center: Working with Closures
UPDATE:
You could also inline the above function factory as #adamse suggested in the other answer. This is actually a more common approach, but is practically the same as the above:
for(var i = 0; i < 100; i++) {
get_node(i).onclick = (function(p) {
return function () {
// we could have used i as a parameter variable as well,
// but we're using p to better illustrate what's happening
do_something_very_important(p);
}
})(i);
}
Any yet another solution is to enclose each iteration in its own scope, by using self invoking anonymous functions:
for(var i = 0; i < 100; i++) {
(function (p) {
// we now have a separate closure environment for each
// iteration of the loop
get_node(i).onclick = function() {
do_something_very_important(p);
}
})(i);
}
Yes this works...
for (var i = 0; i < 100; i++) {
get_node(i).onclick = (function(i) {
return function () {
do_something_very_important(i);
}
})(i);
}

changing the scope of an anonymous function on a setTimeout causes a weird warning

this has interested me purely as research and personal development. i have a namespaced set of functions / variables.
within 1 function I need to call another through setTimeout but keeping the scope to 'this'. i am struggling with this a little, can't seem to bind it for when the setTimeout runs.
var foo = {
ads: ["foo","bar"],
timeDelay: 3,
loadAds: function() {
var al = this.ads.length;
if (!al)
return; // no ads
for(var i = 0; i < al; i++) {
setTimeout(function() {
this.scrollAd(this.ads[i]);
}.apply(this), this.timeDelay * 1000);
}
},
scrollAd: function(adBlock) {
console.log(adBlock);
}
};
};
the .apply(this) DOES change the scope as the console.log outputs the right object back, but it runs the function immediately and then the exception/warning comes up as the callback remains empty:
useless setTimeout call (missing quotes around argument?)
is there an elegant way of doing this at all? i know i could do
var _this = this;
and reference _this in the anon callback. for example, in mootools i'd use .bind(this) instead...
and no, as this involves animating, i don't want to use " " around the string as it will need to be eval'd and would impact performance...
for(var i = 0; i < al; i++) {
setTimeout(function() {
this.scrollAd(this.ads[i]);
}.apply(this), this.timeDelay * 1000);
}
apply doesn't bind a function, it calls it. So you execute the scroll straight away and then pass its return value (undefined) to setTimeout, which is ineffective.
You probably meant to use a closure like this over this and the loop variable (which must be closed or it will be the same, post-loop value for every timeout):
for(var i = 0; i < al; i++) {
setTimeout(function(that, j) {
return function() {
that.scrollAd(that.ads[j]);
};
}(this, i), this.timeDelay * 1000);
}
However you may prefer to use the new ECMAScript Fifth Edition function binding feature, which has a much more compact syntax:
for (var i= 0; i<al; i++)
setTimeout(this.scrollAd.bind(this, this.ads[i]), this.timeDelay*1000);
(There's an implementation of function.bind for browsers that don't have have it natively at the bottom of this answer.)
From what I know you should indeed use something like this:
var self = this;
setTimeout(function(){self.scrollAd(ad);}, this.timeDelay * 1000);
But if you badly want to use .apply(), then do it like this:
var self = this;
setTimeout(function(){
function(){
}.apply(self);
}, this.timeDelay * 1000);
Also note that if you run this inside a for loop and use i's value inside a function that is run in timer, then your function will always run with the last value of i (i.e. i == al). In order to fix that, you'll need to make a closure with each value of i separately.
So taking your code and making it work it should look like this:
var foo = {
ads: ["foo","bar"],
timeDelay: 3,
loadAds: function() {
function runTimed(o, fn, args, time)
{
setTimeout(function(){ fn.apply(o, args); }, time);
}
var al = this.ads.length;
if (!al)
return; // no ads
for(var i = 0; i < al; i++) {
runTimed(this, this.scrollAd, this.ads[i], this.timeDelay*1000);
}
},
scrollAd: function(adBlock) {
console.log(adBlock);
}
};
};
Note: I haven't run this code so it may contain some mistakes.
Also if I were you, I'd use the data from object and don't pass it to the scrollAd (i is enough).

Doesn't JavaScript support closures with local variables? [duplicate]

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 8 years ago.
I am very puzzled about this code:
var closures = [];
function create() {
for (var i = 0; i < 5; i++) {
closures[i] = function() {
alert("i = " + i);
};
}
}
function run() {
for (var i = 0; i < 5; i++) {
closures[i]();
}
}
create();
run();
From my understanding it should print 0,1,2,3,4 (isn't this the concept of closures?).
Instead it prints 5,5,5,5,5.
I tried Rhino and Firefox.
Could someone explain this behavior to me?
Fixed Jon's answer by adding an additional anonymous function:
function create() {
for (var i = 0; i < 5; i++) {
closures[i] = (function(tmp) {
return function() {
alert("i = " + tmp);
};
})(i);
}
}
The explanation is that JavaScript's scopes are function-level, not block-level, and creating a closure just means that the enclosing scope gets added to the lexical environment of the enclosed function.
After the loop terminates, the function-level variable i has the value 5, and that's what the inner function 'sees'.
As a side note: you should beware of unnecessary function object creation, espacially in loops; it's inefficient, and if DOM objects are involved, it's easy to create circular references and therefore introduce memory leaks in Internet Explorer.
I think this might be what you want:
var closures = [];
function createClosure(i) {
closures[i] = function() {
alert("i = " + i);
};
}
function create() {
for (var i = 0; i < 5; i++) {
createClosure(i);
}
}
The solution is to have a self-executing lambda wrapping your array push. You also pass i as an argument to that lambda. The value of i inside the self-executing lambda will shadow the value of the original i and everything will work as intended:
function create() {
for (var i = 0; i < 5; i++) (function(i) {
closures[i] = function() {
alert("i = " + i);
};
})(i);
}
Another solution would be to create yet another closure which captures the correct value of i and assigns it to another variable which would "get caught" in the final lambda:
function create() {
for (var i = 0; i < 5; i++) (function() {
var x = i;
closures.push(function() {
alert("i = " + x);
});
})();
}
Yes closures are working here. Each time you loop the function you are creating grabs the i. Each function you create shares the same i. The problem you are seeing is that since they all share the same i they also share the final value of i since it is the same captured variable.
Edit: This article by Mr. Skeet explains closures in some depth and addresses this issue in particular in a way that is much more informative then I have here. However be careful as the way that Javascript and C# handle closures have some subtle differences. Skip to the section called "Comparing capture strategies: complexity vs power" for his explanation on this issue.
John Resig's Learning Advanced JavaScript explains this and more. It's an interactive presentation that explains a lot about JavaScript, and the examples are fun to read and execute.
It has a chapter about closures, and this example looks a lot like yours.
Here's the broken example:
var count = 0;
for ( var i = 0; i < 4; i++ ) {
setTimeout(function(){
assert( i == count++, "Check the value of i." );
}, i * 200);
}
And the fix:
var count = 0;
for ( var i = 0; i < 4; i++ ) (function(i){
setTimeout(function(){
assert( i == count++, "Check the value of i." );
}, i * 200);
})(i);
Just defining an inner function, or assigning it to some variable:
closures[i] = function() {...
does not create a private copy of the whole execution context. The context isn't copied until the nearest outer function is exiting (at which point those external variables could be garbage collected, so we'd better grab a copy).
This is why wrapping another function around your inner function works - the middle guy actually executes and exits, cuing the innermost function to save his own copy of the stack.
Here is what you should do to achieve your result:
<script>
var closures = [];
function create() {
for (var i = 0; i < 5; i++) {
closures[i] = function(number) {
alert("i = " + number);
};
}
}
function run() {
for (var i = 0; i < 5; i++) {
closures[i](i);
}
}
create();
run();
</script>

Categories