Friday, 20 December 2013

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

1. Usage of for..in to iterate Arrays

Example:

var myArray = [ “a”, “b”, “c” ];
var totalElements = myArray.length;
for (var i = 0; i < totalElements; i++) {
   console.log(myArray[i]);
}
The main problem here is that the for..in statement does not guarantee the order. Effectively this means that you could obtain different results at different executions. Moreover, if someone augmented Array.prototype with some other custom functions, your loop will iterate over those functions as well as the original array items.
Solution: always use regular for loops to iterate arrays.

var myArray = [ “a”, “b”, “c” ];
for (var i=0; i<myArray.length; i++) {
    console.log(myArray[i]);
}

2. Array dimensions

Example:

var myArray = new Array(10);
There are two different problems here. First, the developer is trying to create an array already containing 10 items, and it will create an array with 10 empty slots. However, if you try to get an array item, you will get ‘undefined’ as result. In other words, the effect is the same as if you did not reserved that memory space. There is no really good reason to predefine the array length.
The second problem is that the developer is creating an array using Array constructor. This is technically correct. However it’s slower than the literal notation.
Solution: Use literal notation to initialize arrays. Do not predefine array length.

var myArray = [];

3. Undefined properties

Example:

var myObject = {
    someProperty: “value”,
    someOtherProperty: undefined
}
Undefined properties (such as someOtherProperty in the above example) will create an element in the object with key 'someOtherProperty' and value 'undefined'. If you loop through your array checking the existence of an element the following 2 statements will both return 'undefined':
typeof myObject['someOtherProperty'] // undefined typeof myObject['unknownProperty'] // undefined
Solution: if you want to explicitly declare a uninitialized properties inside an object, mark them as null

var myObject = {
    someProperty: “value”,
    someOtherProperty: null
}

4. Misuse of Closures

Example:

function(a, b, c) {
    var d = 10;
    var element = document.getElementById(‘myID’);
    element.onclick = (function(a, b, c, d) {
        return function() {
            alert (a + b + c + d);
        }
    })(a, b, c, d);
}
Here the developer is using two functions to pass the arguments a, b and c to the onclick handler. The double function is not needed only adding complexity to the code.
The variables a, b and c are already defined in the inner function because they are already declared as parameters in the main function. Any function inside the inner function will create a closure with all variables defined by main function. This includes ‘regular’ variables (like d) and arguments (like a, b and c). Thus It is not necessary to pass them again as parameters using a auto-executable function.
See JavaScript Closures FAQ for an awesome explanation about closures and contexts.
Solution: use closures to simplify your code

function (a, b, c) {
    var d = 10;
    var element = document.getElementById(‘myID’);
    element.onclick = function() {
        //a, b, and c come from the outer function arguments.
        //d come from the outer function variable declarations.
        //and all of them are in my closure
        alert (a + b + c + d);
}; }

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));

9. Usage of strings with setTimeout/setInterval

Example:

function log1() { console.log(document.location); }
function log2(arg) { console.log(arg); }
var myValue = “test”;
setTimeout(“log1()”, 100);
setTimeout(“log2(” + myValue + “)”, 200);
Both setTimeout() and setInterval() can accept either a function or a string as the first parameter. If you pass a string, the engine will create a new function using Function constructor. This is very slow in some browsers. Instead pass the function itself as the first argument; it’s faster, more powerful and clearer.
Solution: never use strings for setTimeout()or setInterval()

function log1() { console.log(document.location); }
function log2(arg) { console.log(arg); }
var myValue = “test”;
setTimeout(log1, 100); //Reference to a function
setTimeout(function(){ //Get arg value using closures
    log2(arg);
}, 200);

10. Usage of setInterval() for heavy functions

Example:

function domOperations() {
    //Heavy DOM operations, takes about 300ms
}
setInterval(domOperations, 200);
We can have a problem when using intervals where operation time is bigger than the interval step time. In the example we are doing a complex (i.e.: long) operation with DOM objects every 200ms. If the domOperations() function takes more than 200ms to complete, each step will overlap with the previous step and eventually some steps may get discarded. This can become a problem.
setInterval() schedules a function to be executed only if there isn’t another execution already waiting in the main execution queue. The JavaScript engine only adds the next execution to the queue if there is no another execution already in the queue. This may yield to skip executions or run two different executions without waiting 200ms between them. To make it clear, setInterval() doesn’t take in account how long it takes domOperations() to complete its job.
Solution: avoid setInterval(), use setTimeout()

function domOperations() {
    //Heavy DOM operations, takes about 300ms
//After all the job is done, set another timeout for 200 ms setTimeout(domOperations, 200); } setTimeout(domOperations, 200);
11. Misuse of ‘this’ There is no example for this common mistake as it is very difficult to build one to illustrate it. The value of this in JavaScript is very different from other languages, where the this behaviour is usually clearer. However, in JavaScript this is a bit different.
First of all, the value of this inside a function is defined when the function is called, not when it is declared. Its very important to understand this, because the value of this depends on how the function is called. In the following cases, this has a different meaning inside myFunction
* Regular function: myFunction(‘arg1’);
this points to the global object, wich is window for all browers.
* Method: someObject.myFunction(‘arg1’);
this points to object before the dot, someObject in this case.
* Constructor: var something = new myFunction(‘arg1’);
this points to an empty Object.
* Using call()/apply(): myFunction.call(someObject, ‘arg1’);
this points to the object passed as first argument.
In this way you can have the same function (myFunction in the example above), that internally use this. However, the value of this is not related to the function declaration itself, only to the way that function is called.

12. Usage of eval() to access dynamic properties

Example:

var myObject = { p1: 1, p2: 2, p3: 3};
var i = 2;
var myResult = eval(‘myObject.p’+i);
The main problem lies in that starting a new execution context with eval() is extremely slow. The same can be achieved using square bracket notation instead of dot notation.
Solution: use square bracket notation instead of eval()

var myObject = { p1: 1, p2: 2, p3: 3};
var i = 2;
var myResult = myObject[“p”+i];

13. Usage of undefined as a variable

Example:

if ( myVar === undefined ) {
    //Do something
}
This check usually works, but it does by pure chance. In the code above undefined is effectively a variable. All JavaScript engines will create the variable window.undefined initialized to undefined as its value. However note that variables isn’t read-only, and any other code can change its value. It’s very weird to find a scenario where window.undefined has a value different from undefined. (nobody will never actually do undefined = 10;). But why take the risk? It is better to use typeof checks.
Solution: use typeof when checking for undefined.

if ( typeof myVar === “undefined” ) {
    //Do something

No comments:

Post a Comment

Angular Tutorial (Update to Angular 7)

As Angular 7 has just been released a few days ago. This tutorial is updated to show you how to create an Angular 7 project and the new fe...