htmx
JavaScript

JavaScript Best Practices(Part -1)

Beware of Implicit Coercions

JavaScript allows for some surprising type coercions. The below expression throws an error in most languages.

3 + true //4

In the above expression JavaScript coerces true as the number 1 and adds it to 3 to produce 4. All the arithmetic operators +, -, /, *, % try to convert their arguments to numbers before adding them. The only exception is that when + is used with a string argument + converts both arguments to Strings and produces a string result.

2+3; //5
“hello” + “ world”; //“hello world”
“2” + 3. //“23”

While coercions are convenient in some cases, they can also hide errors. A variable that turns out to be null will not fail but gets quietly converted to 0. An undefined variable will convert to NaN. Silent coercions can make debugging difficult. When a calculation goes wrong , the best approach to debugging is to inspect the intermediate results of a calculation, working back to the last point before things went wrong.

The last kind of coercion is truthiness. Operators if, || and. && logically work with boolean values but they actually work with any values. Most javascript values are truths and coerced to true. There are exactly seven falsy values: false, 0, -0, “”, NaN, null and undefined. Its not always safe to use truthiness to check whether a function argument or object property is-defined.

Prefer Primitives to Object Wrappers

JavaScripts has five types of primitive values: booleans, numbers, strings, null and undefined. At the sametime the standard library provides constructors for wrapping booleans, numbers, and strings as objects. We can create a String object that wraps a string value:

var s = new String(“hello”);

In some ways, a String object behaves similarly to the string value it wraps. However when we use typeof for a wrapper object, it is a true object.

typeof “hello”; // “string”
typeof s; // “object”

This means we can’t compare the contents of two distinct String objects using built-in operators

var s1 = new String(“hello”);
var s2 = new String(“hello”);
s1===s2; //false

Since these wrappers don’t behave quite right, they don’t serve much of a purpose.

Avoid using == with Mixed Types

Consider the below expression.

"1.0e0" == { valueOf: function() { return true; } };

These two seemingly unrelated values are actually considered equivalent by the == operator. The string “1.0e0” parses as the number 1 and the object is converted to a number 1.

When the two arguments are of the same type, there’s no difference in behaviour between == and ===. So if you know that the arguments are of same type, they are interchangeable. But using strict equality is a good way to make it clear to readers that there is no conversion involved in the comparison.

Even very simple functions that define their temporary variables globally would have to worry whether any other code might use those same variable names.

Minimize Use of the Global Object

Global variable take less effort to create, since they don’t require any kind of declaration and they are automatically accessible to all code throughout the program. However we.must avoid global variables as it pollutes the global name space and introduces the possibility of accidental name collisions.
Since the global namespace is the only real way for separate components of a Javascript program to interact some uses of the global namespace are unavoidable. We have two mechanisms to choose from for creating a global variable: You can declare it with var in the global scope, or you can add it to the global object. Either works, but the var declaration has the benefit of more clearly conveying the effect on the program’s scope.

Always Declare Local Variables

Forgetting to declare a local variable silently turns it into a global variable. Purposefully creating global variables is a bad style, but accidentally creating global variables can be a downright disaster. Because of this, many programmers use lint tools, which inspects your program’s source code for bad style or potential bugs.

Get Comfortable with Closures

Understanding closures only requires learning three essential facts. The first fact is that JavaScript allows us to refer to variables that were defined outside of current function:

function makeSandwich() {
    var magicIngredient = "peanut butter";
    function make(filling) {
        return magicIngredient + " and " + filling;
    }
    return make("jelly");
}
makeSandwich(); // "peanut butter and jelly"

Notice how the inner make function refers to magic ingredient, a variable defined in the outer makeSandwich function.

The second fact is that functions can refer to variables defined in outer functions even after those outer functions have returned! If that sounds implausible, remember that JavaScript functions are first-class objects. That means that you can return an inner function to be called sometime later on:

function sandwichMaker() {
    var magicIngredient = "peanut butter";
    function make(filling) {
        return magicIngredient + " and " + filling;
    }
    return make;
}
var f = sandwichMaker();
f("jelly");        // "peanut butter and jelly"
f("bananas");      // "peanut butter and bananas"
f("marshmallows"); // "peanut butter and marshmallows"

This is almost identical to the first example, except that instead of immediately calling make(“jelly”) inside the outer function, sandwichMaker returns the make function itself. So the value of f is the inner make function, and calling f effectively calls make. But somehow, even though sandwichMaker already returned, make remembers the value of magicIngredient.

How does this work? The answer is that JavaScript function values contain more information than just the code required to execute when they’re called. They also internally store any variables they may refer to that are defined in their enclosing scopes. Functions that keep track of variables from their containing scopes are known as closures. The make function is a closure whose code refers to two outer variables: magicIngredient and filling. Whenever the make function is called, its code is able to refer to these two variables because they are stored in the closure.
A function can refer to any variables in its scope, including the parameters and variables of outer functions. 

Understand Variable Hoisting

JavaScript supports lexical scoping: With only a few exceptions, a reference to a variable foo is bound to the nearest scope in which foo was declared. However, JavaScript does not support block scoping: Variable definitions are not scoped to their nearest enclosing statement or block, but rather to their containing function.
Failing to understand this idiosyncrasy of JavaScript can lead to subtle bugs such as this:

function isWinner(player, others) {
    var highest = 0;
    for (var i = 0, n = others.length; i < n; i++) {         var player = others[i];         if (player.score > highest) {
            highest = player.score;
        }
    }
    return player.score > highest;
}

This program appears to declare a local variable player within the body of a for loop. But because JavaScript variables are function-scoped rather than block-scoped, the inner declaration of player simply redeclares a variable that was already in scope—namely, the player parameter. Each iteration of the loop then overwrites the same variable. As a result, the return statement sees player as the last element of others instead of the function’s original player argument.

Hoisting can also lead to confusion about variable redeclaration. It is legal to declare the same variable multiple times within the same function. This often comes up when writing multiple loops:

function trimSections(header, body, footer) {
    for (var i = 0, n = header.length; i < n; i++) {
        header[i] = header[i].trim();
    }
    for (var i = 0, n = body.length; i < n; i++) {
        body[i] = body[i].trim();
    }
    for (var i = 0, n = footer.length; i < n; i++) {
        footer[i] = footer[i].trim();
    }
}

The trimSections function appears to declare six local variables (three called i and three called n), but hoisting results in only two. In other words, after hoisting, the trimSections function is equivalent to this rewritten version:

function trimSections(header, body, footer) {
    var i, n;
    for (i = 0, n = header.length; i < n; i++) {
        header[i] = header[i].trim();
    }
    for (i = 0, n = body.length; i < n; i++) {
        body[i] = body[i].trim();
    }
    for (i = 0, n = footer.length; i < n; i++) {
        footer[i] = footer[i].trim();
    }
}

Use Immediately Invoked Function Expressions to Create Local Scopes

Creation of a local scope by creating a nested function and calling it right away is known as the immediately invoked function expression, or IIFE.

function wrapElements(a) {
    var result = [];
    for (var i = 0, n = a.length; i < n; i++) {
       (function() {
           var j = i;
           result[i] = function() { return a[j]; };
       })();
    }
    return result;
}

However, be careful when using an IIFE to create a local scope, because wrapping a block in a function can introduce some subtle changes to the block. First of all, the block cannot contain any break or continue statements that jump outside of the block, since it is illegal to break or continue outside of a function. Second, if the block refers to this or the special arguments variable, the IIFE changes their meaning. Chapter 3 discusses techniques for working with this and arguments.

Understand the Difference between Function, Method, and Constructor Calls

If you’re familiar with object-oriented programming, you’re likely accustomed to thinking of functions, methods, and class constructors as three separate things. In JavaScript, these are just three different usage patterns of one single construct: functions.

The simplest usage pattern is the function call:

function hello(username) {
    return "hello, " + username;
}
hello("Keyser Söze"); // "hello, Keyser Söze"

Methods in JavaScript are nothing more than object properties that happen to be functions:

var obj = {
    hello: function() {
        return "hello, " + this.username;
    },
    username: "Hans Gruber"
};
obj.hello(); // "hello, Hans Gruber"

Notice how hello refers to this to access the properties of obj. You might be tempted to assume that this gets bound to obj because the hello method was defined on obj. But we can copy a reference to the same function in another object and get a different answer:

var obj2 = {
    hello: obj.hello,
    username: "Boo Radley"
};
obj2.hello(); // "hello, Boo Radley"

What really happens in a method call is that the call expression itself determines the binding of this, also known as the call’s receiver. The expression obj.hello() looks up the hello property of obj and calls it with receiver obj. The expression obj2.hello() looks up the hello property of obj2—which happens to be the same function as obj.hello—but calls it with receiver obj2. In general, calling a method on an object looks up the method and then uses the object as the method’s receiver.

Since methods are nothing more than functions called on a particular object, there is no reason why an ordinary function can’t refer to this:

function hello() {
    return "hello, " + this.username;
}

The third use of functions is as constructors. Just like methods and plain functions, constructors are defined with function:

function User(name, passwordHash) {
    this.name = name;
    this.passwordHash = passwordHash;
}
Invoking User with the new operator treats it as a constructor:
var u = new User("sfalken",
                 "0ef33ae791068ec64b502d6cb0191387");
u.name; // "sfalken"

Unlike function calls and method calls, a constructor call passes a brand-new object as the value of this, and implicitly returns the new object as its result. The constructor function’s primary role is to initialize the object.

Get Comfortable Using Higher-Order Functions

Higher-order functions are nothing more than functions that take other functions as arguments or return functions as their result. Taking a function as an argument (often referred to as a callback function because it is “called back” by the higher-order function) is a particularly powerful and expressive idiom, and one that JavaScript programs use heavily.

The standard library could have required the caller to pass in an object with a compare method, but since only one method is required, taking a function directly is simpler and more concise. In fact, the above example can be simplified further with an anonymous function:

[3, 1, 4, 1, 5, 9].sort(function(x, y) {
    if (x < y) {         return -1;     }     if (x > y) {
        return 1;
    }
    return 0;
}); // [1, 1, 3, 4, 5, 9]

Learning to use higher-order functions can often simplify your code and eliminate tedious boilerplate. Many common operations on arrays have lovely higher-order abstractions that are worth familiarizing yourself with.

Once you get the hang of using higher-order functions, you can start identifying opportunities to write your own. The telltale sign of a higher-order abstraction waiting to happen is duplicate or similar code.