Thứ Hai, 4 tháng 8, 2014

Top 13 JavaScript Mistakes(part2)

5. Closures in loops

Example:

var elements = document.getElementByTagName(‘div’);
for (var i = 0; i<elements.length; i++) {
    elements[i].onclick = function() {
        alert(“Div number “ + i);
    }
}
In this example, we want to trigger an action (display “Div number 1”, “Div number 2”... etc) when the user clicks on the different divs on the page. However, if we have 10 divs in the page, all of them will display “Div number 10”.
The problem is that we are creating a closure with the inner function, so the code inside the function has access to variable i. The point is that i inside the function and i outside the function refers to the same variable (i.e.: the same position in memory). When our loop ends, i points to the value 10. So the value of i inside the inner function will be 10.
See JavaScript Closures FAQ for an awesome explanation about closures and contexts.
Solution: use a second function to pass the correct value.

var elements = document.getElementsByTagName(‘div’);
for (var i = 0; i<elements.length; i++) {
    elements[i].onclick = (function(idx) { //Outer function
        return function() { //Inner function
            alert(“Div number “ + idx);
        }
    })(i);
}
The outer function is a function that executes inmediatly, receiving i as a parameter. That parameter is called idx inside the outer function, thus inner function creates a closure with idx (instead of i). Therefore idx is completly different from across iterations (i.e. they point to different memory address). This example is very important to understand how closures work. Be sure to read it and play with the code in your browser until you fully understand what’s going on there.

6. Memory leaks with DOM objects

Example:

function attachEvents() {
    var element = document.getElementById(‘myID’);
    element.onclick = function() {
        alert(“Element clicked”);
    }
};
attachEvents();
This code creates a reference loop. The variable element contains a reference to the function (it is assigned to onclick properties). Also, function is keeping a reference to the DOM element (note that inside the function you have access to element because of the closure). So JavaScript garbage collector cannot clean neither element nor the function, because both are referenced by each other. Most JavaScript engines aren’t clever enough to clean circular references.
Solution: avoid those closures or undo the circular reference inside the function

function attachEvents() {
    var element = document.getElementById(‘myID’);
    element.onclick = function() {
        //Remove element, so function can be collected by GC
        delete element;
        alert(“Element clicked”);
    }
};
attachEvents();

7. Differentiate float numbers from integer numbers

Example:

var myNumber = 3.5;
var myResult = 3.5 + 1.0; //We use .0 to keep the result as float
In JavaScript, there is no difference between float and integers. Actually, every number in JavaScript is represented using double-precision 64-bits format IEEE 754. In plain words, all numbers are floats.
Solution: don’t use decimals to “convert” numbers to floats.

var myNumber = 3.5;
var myResult = 3.5 + 1; //Result is 4.5, as expected

8. Usage of with() as a shortcut

Example:

team.attackers.myWarrior = { attack: 1, speed: 3, magic: 5};
with (team.attackers.myWarrior){
    console.log ( “Your warrior power is ” + (attack * speed));
}
Before talking about with(), let’s see how JavaScript contexts works. Each function has an execution context that, put in simple words, holds all the variables that the function can access. Thus the context contain arguments and defined variables. Also a context points to a “parent” context (the context that our caller function has). For example, if functionA() calls functionB(), functionB’s context points to functionA’s context as its parent context. When accessing any variable inside a function, the engine first search in his own context. If not found, it switches to the parent context and so on until it finds the variable or it reaches the end of the context chain. Execution contexts are what makes closures work.
What with() actually does, is to insert an object into our context chain. It injects between my current context and my parent’s context. In this way, the engine first searches in the current context for the requested variable, and then it searches for it in the recently injected object, and finally in the “real” parent context. As you can see, the shortcut used in the example code above is a side effect of using context injection. However usage of with() is very slow, and thus using it for shortcuts is just insane.
Just a side note. Every book recommends not to use with(). Nevertheless all of them are focused on its usage as a shortcut. Context injection is really useful and you may find that you need to use it in advanced JavaScript. In those cases, it’s acceptable to use with() with its real meaning. Although be aware that it’s it’s still very slow from a performance perspective.
See JavaScript Closures FAQ for an awesome explanation about closures and contexts.
Solution: don’t use with() for shortcuts. Only for context injection when you really need it.

team.attackers.myWarrior = { attack: 1, speed: 3, magic: 5};
var sc = team.attackers.myWarrior;
console.log(“Your warrior power is ” + (sc.attack * sc.speed));


Reference link: http://corporate.tuenti.com/en/dev/blog/top-13-javascript-mistakes

Không có nhận xét nào:

Đăng nhận xét