.NET Daily

Javascript

Little things that make a big difference in Javascript

Posted on .

Little things that make a big difference in Javascript

Introduction

Since I started working as a front-end developer, I encountered many situations when JavaScript code was buggy or had various issues even though the logic seemed correct.

As a beginner it is difficult to follow all the best practices and sometimes can even be confusing, as this topic can be a bit subjective. Of course, there are a few classics, like don’t place the JavaScript in the page’s head unless really need it, don’t query the DOM a million times, consider caching and so on. These topics are intensely discussed on many sites, but there are also some small things that can affect a web page in a big way, and i haven’t seen them covered.

So I put together a list of things that I noticed people tend to not consider very carefully, some are more subtle than others and can take one quite some time to realize the mistake. Some of the ones worth mentioning are the following:

  1. Using the curly baces (and semi-colons)

    Sometimes when writing statements that only have one line of code inside, some developers might be tempted to skip the curly braces. This is fine, the code runs as expected, without any errors, and the expected result is not affected.
    However, this is not a best practice because the code written in this maner is not very easy to mentain. From my point of view the code written in this manner is not so easy to read nor consistent, but this can be viewed as a personal prefference. Maintenability on the other hand is a real concern, because if the curly braces are omitted, when someone else wants to add another line to this code it will still be valid, no errors will be thrown, but the end result will not be as expected: the new line will not be executed as part of the statement, it will be executed after the statement.

    Lets see an example:

    var x = 1,
        y = 2,
        z;
    if (x > y) {
        z = x - y;
        console.log(z + 5);
    }
    console.log(z);
    // undefined
    var x = 1,
        y = 2,
        z;
    if (x > y)
        z = x - y;
        console.log(z + 5);
    console.log(z);
    // NaN
    // undefined

    And chances are that the developer makes more changes in the code, aside from adding that new line. So even if the skipping curly braces might not seam a big deal at first, the issue that follows it is painfull to debug, not to mention time consuming. So why skipping the braces in the first place? To save a few seconds? When making this choise consider the time to be spent later on for bug-fixing and yoga (to restore your mental health).

  2. Operations: addition vs. concatenation

    As you may know, the “+” operator can behave differently based on the type of the variables used. So if we have two variables, a and b, then a + b is a number representing the arithmetic sum of a and b, for the case they are are both numbers. But the result can be for example a string, if at least one of them is a string.

    Lets see a few examples:

    var x, 
        y = 5;
    // undefined
    
    x = null;
    // null
    
    typeof(x)
    // "object"
    
    x + y
    // 5
    var x, 
        y = 5;
    // undefined
    
    typeof(x)
    // "undefined"
    
    x + y
    // NaN
    var x = null,
        y = null;
    // undefined
    
    x + y
    // 0
    var x,
        y = null;
    // undefined
    
    x + y
    // NaN
    
    typeof(x)
    // "undefined"
    
    typeof(y)
    // "object"
    
    var x = 6,
        y = [];
    // undefined
    
    typeof(x)
    // "number"
    
    typeof(y)
    // "object"
    
    x + y
    // 6
    var x = true,
        y = 6;
    // undefined
    
    x + y
    // 7
    
    var z = null;
    // undefined
    
    x + z
    // 1
  3. Mixing local and global scope (variables)

    It is more than best practice to use different naming for global variables versus the local ones. If some local variable has the same name as a global one, overwriting may occur and the expected result might be altered.

    Here are some examples:

    var x = 0,
        func = function() {
            var a = 6,
                b = 4;
            x++;
            return (a + b + x);
        };
    // undefined
    
    func()
    // 11
    
    x
    // 1
    var x = 0,
        func = function(x, y) {
            var a = 6,
                b = 4;
            x++;
            return (a + b + x);
        };
    // undefined
    
    func(7,8)
    // 18
    
    x
    // 0
    var x = 3,
        func = function() {
            return ++x;
        };
    // undefined
    
    func()
    // 4
    
    x
    // 4
    var x = 3,
        func = function() {
            var x = 4;
            return ++x;
        };
    // undefined
    
    func()
    // 5
    
    x
    // 3
  4. Comparison operators

    The “is equal to” (==) operator is used to compare two values to see if they are the same.
    Example:

    • (“3” == 3) returns true
    • (“3” == “3”) returns true

    The “strict equal to” operator compares two values to check if both their data type and value are the same.
    Example:

    • (“3” == 3) returns false
    • (“3” == “3”) returns true

    Same logic applies for the non equal operator (!= and !==).

  5. Type coercion and weak typing

    If you use a data type that JavaScript did not expect, it tries to make sense of the operation instead of throwing an error.
    Example:
    “two” / 2 = NaN
    typeof(NaN) is a number. It occurs when the expected result is a number (or it should be a number), but it is not returned.

    As the data type for a variable can change, JavaScript is considered to use weak typing.

    JavaScript can convert datatypes behind the scens in order to perform an operation. This is known as type coercion.
    Example: “1” > 0 evaluates as true because the string “1” is converted to a number.

    Because of the type coercion, the strict equality operators “===” and “!==” result in fewer unexpected values than “==” and “!=”.
    Examples:

    • (false == 0) returns true
    • (false == “”) returns true
    • (0 == “”) returns true
    • (undefined == null) returns true
    • (false === 0) returns false
    • (false === “”) returns false
    • (0 === “”) returns false
    • (undefined === null) returns false
  6. Truthy and falsy values

    Because of the type coercion, every value can be treated as if they were true or false. Here are some interesting examples:

    Falsy values are values treated as if they are false. They can also be treated and number zero.

    • false (boolean false)
    • 0 (number zero)
    • “” (empty string)
    • NaN (not a number)
    • “two” / 2 (NaN)
    • 10 / “one” (NaN)
    • undefined (var with no value assigned to it)
    • null

    Truthy values are values that are treated as if they are true. Almost everything that is not falsy can be considered truthy. Truthy values can also be treated as the number 1. Usually arrays and objects are considered truthy.

    • true (boolean true)
    • 1 (number one)
    • “some string” (strings with text)
    • 2 / 2 (number operations)
    • “true” (true written as string)
    • “false” (false written as string)
    • “0” (zero written as string)
    • [] (arrays)
    • {} (objects)

    Although null and undefined are both falsy, they are not equal to anything other than themselves, but this also depends whether strict comparison is used or not (to have into account the data type):

    • (undefined == undefined) returns true
    • (null == null) returns true
    • (undefined == null) returns true
    • (null == false) returns false
    • (undefined == false) returns false
    • (null == 0) returns false
    • (undefined == 0) returns false
    • (null == NaN) returns false
    • (undefined == NaN) returns false
    • (undefined === undefined) returns true
    • (null === null) returns true
    • (undefined === null) returns false

    Note: NaN retuns false in any comparison, even with itself, regardless of strict or loose.

    • (NaN == NaN) returns false
    • (NaN === NaN) returns false
  7. Cross-site scripting (XSS) attacks

    When inserting HTML to a page using innerHTML() or other jQuery methods, some precautionary measures should be considered, otherwise malicious code might be placed into the site by some attacker. This is what XSS is about: JavaScript mixed with HTML markup and inserted into the page.

    Using this method, an attacker can gain access to cookies, session tokens or other information and use it for their own malicious purposes.
    Data you don’t have complete control over, like images/videos uploaded by the users, articles or comments and other feeds represent untrusted data and it must be handled with care.

    Any HTML form untrusted sources opens your site to XSS attacks.

    In order to defend your site against these attacks, some precautions should be taken:

    Validate data going to the server:

    • Use form validation and similar validators
    • Do not allow untrusted users to submit HTML or JS
    • Double-check validation on server before storing the data into the DB (users can turn off JS on the browser and bypass client validation)

    Escape data coming from the server and DB

    • Do not create DOM fragments with content from untrusted sources. It should be escaped and only added as text
    • Limit where user content goes (malicious users can use CSS or URLs to create and XSS attack, not just use “script” tags)
    • All potentially dangerous characters should be escaped.

    Conclusion: Regarding the data from untrusted sources, the content should be escaped and added as text on the page (not markup), so instead of innerHTML try using textContent.
    Also client side and server side validation is very important.

  8. Preventing jQuery conflicts with other libraries

    jQuery is a very popular JavaScript library and even if it is probably the most used JS library, sometimes it might be used along with some other libraries.
    The shorthand for jQuery() is the dolar sign ($), and this symbol is also used by other ibraries such as MooTools or prototype.js. So in order to avoid conflicts with those scripts, some precautions should be taken.
    Here are the two scenarious:

    • When jQuery is included after other libraries

      In this case we can use the .noConflict() method at the start of our script in order to release the $ shortcut so that other scripts can use it. So we eill use the full name instead or we can wrap our script into an IIFE and still use the $ (pass jQuery as argument).
      Another way is to specify our own alias for the jQuery() instead of the $().

    • When jQuery is included before other libraries

      In this case the $ will have the meaning defined in the other library, so .noConflict() will not work. We can use the full name jQuert, or we can pass $ as an argument to the anonimous functions called by .ready() method, or a similar one.

Carmen

Carmen

There are no comments.

View Comments (0) ...
Navigation

Privacy Preference Center