Javascript tips and tricks

JavaScript Tricks

  1. Understanding Scoping and Hoisting

    Scoping


    • Scope determines the accessibility of variables inside a JS program. There are 3 types of scope in javascript as follows:
      1. Functional Scope
        • A variable declared inside a function is only accessible within or to other functions which are declared inside the said function. These variables are also knows as Local variables.
        • In the example below we are declaring a variable inside the function scope and printing the value of it. Which will successfully output as 9 as it was inside the scope of the function. When we try to access the variable outside the function scope it will print the error that the variable is not defined.
          function scope()
          {
              var variable = "9";
              console.log(variable); // Outputs 9
          }
          scope();
          console.log(variable); // Error: Variable is not defined.
          
        • Now, consider the example below when we create a variable outside the functional scope and print the value of the variable, we get separate value for the same variable on the basis of the scopes they were defined in.
          var variable = "7";
          function scope()
          {
              var variable = "9";
              console.log(variable); // Outputs 9
          }
          scope();
          console.log(variable); // Outputs 7
          
      2. Global Scope
        • A function or variable that is defined outside any functional scope but inside a global score are called global variables / functions. Global functions / variables can be accessed and manipulated by any member inside that program.
        • Using the same example below we can say that, the variable defined in the first line is in the global scope (global scope variable), which can be accessed or changed by any member in the program.
          var variable = "7";
          function scope()
          {
              var variable = "9";
              console.log(variable); // Outputs 9
          }
          scope();
          console.log(variable); // Outputs 7
          
        • From the example, we can also conclude that local variable inside a function are given more priority then global variables. While both (global variable and local variable) has the same name, but when the variable was accessed inside the function scope the value of variable was printed as 9 due to it’s priority over the global variable.
      3. Block Scope
        • Variables declared inside a {...} block cannot be accessed from outside the block.
        • Block Scope was introduced with the release of ES6, let and const are the two providers for Block Scope.
        • Important thing to note is that, variables declared inside a {...} block using var are not block scope variables as they are accessible from outside of the {...} block.
          {
              let x = 2;
              var y = 3;
              console.log(x, y); // Outputs 2 3
          }
          console.log(y); // Outputs 3 (x can NOT be used here).
          

    Hoisting


    • In-formerly Hoisting is referred as a mechanism where function / variables declarations in the program are moved to the top of their scope before code execution.
    • Meaning, that no matter where functions / variables are declared, they are moved to the top of their scope regardless of weather they were globally scoped or locally scoped.
    • In-short, The variable / function declarations are put into memory during the compile phase, but stay exactly where you typed them in your code.
    • JavaScript putting function declarations into memory before it executes any code segment is that it allows you to use a function before you declare it in your code as shown in the example below.
      function catName(name)
      {
          console.log("My Cat's name is " + name); // outputs My Cat's name is Burgers
      }
      catName("Burgers");
      
      catName("Burgers");
      function catName(name)
      {
          console.log("My Cat's name is " + name); // outputs My Cat's name is Burgers
      }
      
    • Hoisting let’s use initialize and use variables before they are declared as shown in the code snippet below.
      cat = "Meow";
      console.log(cat); // Outputs Meow
      var cat;
      
    • JavaScript only hoists declarations not initialization / assignments to that variable. As only declarations are hoisted, initializing a declared variable after use will give undefined as shown in the following code snippet.
      console.log(cat); // Outputs undefined
      var cat;
      cat = "Meow";
      
  1. When to use var, let and const.

    Using var


    • var declarations are globally scoped or function / locally scoped. The scope is global when a var variable is declared outside a function. This means that any variable that is declared with var outside a function block is available for use in the whole program.
    • var is function scoped when it is declared within a function. This means that it is available and can be accessed only within that function.
    • Let’s take a look at an example that defines the problem with var.
    • In the following code, greeter is redefined to “say Hello instead“. While this is not a problem if you knowingly want greeter to be redefined, it becomes a problem when you do not realize that a variable greeter has already been defined before.
      var greeter = "hey hi";
      if (true)
      {
          var greeter = "say Hello instead";
          console.log(greeter); // outputs "say Hello instead"
      }
      console.log(greeter); // outputs "say Hello instead"
      
    • var can be updated as well as re-defined regardless of the scope so the following code snippet would also work like a charm, but result in the same output as above.
      if (true)
      {
          var greeter = "hey hi";
          var greeter = "say Hello instead";
          console.log(greeter); // outputs "say Hello instead"
      }
      console.log(greeter); // outputs "say Hello instead"
      
    • If you have used greeter in other parts of your code, you might be surprised at the output you might get. This will likely cause a lot of bugs in your code. This is why let and const are necessary.
    • var variables are hoisted to the top of their scope and initialized with a value of undefined.

    Using let


    • let is now preferred for variable declaration. It’s no surprise as it comes as an improvement to var declarations. It also solves the problem with var that we just covered above.
    • let is block scoped. A block is a chunk of code bounded by {...}. Anything within curly braces is a block. So a variable declared in a block with let is only available for use within that block as shown below.
      let greeter = "hey hi";
      if (true)
      {
          let greeter = "say Hello instead";
          console.log(greeter); // outputs "say Hello instead"
      }
      console.log(greeter); // outputs "hey hi"
      
    • let can be updated but not re-declared. Just like var, a variable declared with let can be updated within its scope. Unlike var, a let variable cannot be re-declared within its scope.
      if (true)
      {
          let greeter = "hey hi";
          let greeter = "say Hello instead";
          console.log(greeter); // outputs "SyntaxError: Identifier 'greeter' has already been declared"
      }
      
    • Why is there no error in the code snippet earlier than the one above? This is because both instances are treated as different variables since they have different scopes.
    • This fact makes let a better choice than var. When using let, you don’t have to bother if you have used a name for a variable before as a variable exists only within its scope.
    • Also, since a variable cannot be declared more than once within a scope, then the problem discussed earlier that occurs with var does not happen.
    • Just like var, let declarations are hoisted to the top. Unlike var which is initialized as undefined, the let keyword is not initialized. So if you try to use a let variable before declaration, you’ll get a Reference Error.

    Using const


    • Variables declared with the const maintain constant values. const declarations share some similarities with let declarations.
    • const declarations are block scoped just like let declarations. const declarations can only be accessed within the block they were declared.
      const greeter = "hey hi";
      if (true)
      {
          const greeter = "say Hello instead";
          console.log(greeter); // outputs "say Hello instead"
      }
      console.log(greeter); // outputs "hey hi"
      
    • const cannot be updated or re-declared. Meaning that the value of a variable declared with const remains the same within its scope. It cannot be updated or re-declared. So if we declare a variable with const, every const declaration, therefore, must be initialized at the time of declaration.
      // Type error is when you try to update the value of the const in the same scope.
      if (true)
      {
          const greeter = "hey hi";
          greeter = "say Hello instead"; // TypeError: Assignment to constant variable.
          console.log(greeter);
      }
      
      // Syntax error is when you try to re-declare the const variable in the same scope.
      if (true)
      {
          const greeter = "hey hi";
          const greeter = "say Hello instead"; // SyntaxError: Identifier 'greeter' has already been declared
          console.log(greeter);
      }
      
    • This behavior is somehow different when it comes to objects declared with const. While a const object cannot be updated, the properties of this objects can be updated.
      const greeting =
      {
          message: "say Hi",
      }
      
      console.log(greeting); // outputs "say hi"
      
      greeting.message = "say Hello instead";
      console.log(greeting); // outputs "say Hello instead"
      
      
      const greeting =
      {
          message: "say Hello instead",
      } // SyntaxError: Identifier 'greeting' has already been declared
      
      greeting =
      {
          message: "say Hello instead",
      } // TypeError: Assignment to constant variable.
      
    • Hoisting of const is just like let. const declarations are hoisted to the top but are not initialized.

Notable Difference


  • Difference between var, let and const:
    • var declarations are globally scoped or function scoped while let and const are block scoped.
    • var variables can be updated and re-declared within its scope; let variables can be updated but not re-declared; const variables can neither be updated nor re-declared.
    • They are all hoisted to the top of their scope. But while var variables are initialized with undefined, let and const variables are not initialized.
    • While var and let can be declared without being initialized, const must be initialized during declaration.

var let const
Globally scoped or function scoped Block scoped Block scoped
Updated and re-declared Updated but not re-declared cannot be Updated nor re-declared
initialized as Undefined Not Initialized Not Initialized
Can be declared without being initialized Can be declared without being initialized Cannot be declared without being initialized

  1. Use === and !== instead of == and !=
    • The “==” and “!=” operator performs and an automatic type conversion if needed, Whereas the “===” and “!==” will not perform any conversion. The “===” and “!==” operators will compare the value and the type, which is also considered to be a faster approach.
      [10] === 10  // is false
      [10] == 10  // is true
      
      '10' == 10  // is true
      '10' === 10  // is false
      
      [] == 0  // is true
      [] === 0  // is false
      
      '' == false  // is true but true == "a" is false
      '' === false  // is false
      
  1. Difference Between typeof, instanceof and constructor are as follows:
    • typeof : a JavaScript unary operator used to return a string that represents the primitive type of a variable, don’t forget that typeof null will return object, and for the majority of object types (Array, Date, and others) will also return object.
    • constructor : is a property of the internal prototype property, which could be overridden by code.
    • instanceof : is another JavaScript operator that check in all the prototypes chain the constructor it returns true if it’s found and false if not.
      var arr = ["a", "b", "c"];
      typeof arr;  // return "object" 
      arr instanceof Array  // true
      arr.constructor();  // []
      
  1. Self Calling Function
    • This is often referred as a Self-Invoked Anonymous Function or a Immediately Invoked Function Expression (IIFE). It’s a function that runs on its own once created.
    • An IIFE can be used for some default calculations like calculating percentage, addition of some numbers etc.
      // Syntax :
      (function(){
          // some private code that will be executed automatically
      })();
      
    • Consider a scenario where there is a discount day for all customers at McDonald’s offering 25% off on all products.
    • Instead of adding the discount every time in the bill a Self-Invoked Anonymous Function can achieve this task in the following manner.
      // Example:
      var bill_amount = 2500;
      
      (function(bill_amount){
          var totalPrice = bill_amount - (bill_amount * 0.25) ;
          console.log(totalPrice); // 1875
      })(bill_amount)
      
  1. Generate a random set of Alpha-Numeric Characters
    • A random alpha-numeric generator can be used for many purposes ranging from generating different keys for different users which can stay unique or generating keys for encryption and decryption of data.
      console.log(generateRandomAlphaNum(50))
      
      function generateRandomAlphaNum(len) {
          var randomString = "";
          for( ; randomString.length < len; randomString  += Math.random().toString(36).substr(2));
          return  randomString.substr(0, len);
      }
      
      // output:
      // gvc88xtu4v8bg9nax9977d7qpft22ek5idqk8pzij4hcc57f3s
      
  1. Use Splice instead of Delete
    • Deleting an element from an array will not actually delete it, but instead it will replace the element with undefined
    • Splicing an element from an array will remove the element as well as it’s index from the array.
      var fruits = ["Banana", "Orange", "Apple", "Mango"];
      
      fruits.length; // returns 4
      delete fruits[2]; // returns true
      fruits.length; // returns 4 because array is now ["Banana", "Orange", undefined x 1, "Mango"]
      
      fruits.length; // returns 4
      fruits.splice(2, 1); // return Apple
      fruits.length; // returns 3 because array is now ["Banana", "Orange", "Mango"]
      
  1. Use Logical AND (&&) / OR (||) for conditions also known as Short circuit conditionals.
    • The logical AND (&&) / OR (||) operators will reduce the lines of code as well as provide more code readability compared to using if statements.
    • Logical AND (&&) / OR (||) operators can also be used to set default values for function argument.
      var foo = 10;
      
      // Instead of using an if Statement
      if (foo === 10)
      {
          doSomething();
      }
      
      // Use Logical AND operator
      foo === 10 && doSomething();
      
      // Instead of using an if Statement
      if (foo !== 10)
      {
          doSomething();
      }
          
      // Use Logical OR operator
      foo !== 10 || doSomething();
      
      
      // Default value using logical OR
      function defaultArguments(arg1){ 
          return arg1 = arg1 || 10; // arg1 will have 10 as a default value if it’s not already set
      }
      console.log(defaultArguments()); // Sets the value of arg1 to default and prints 10
      console.log(defaultArguments(20)); // Sets the value of arg1 to 20 and prints 20
      
  1. Use the map() function method to loop through an array’s items
    • The map() function method creates a new array with the results of calling a function for every array element.
    • The map() function method does not execute the function for empty elements neither does it change the original array.
      // Array Looping
      var squares = [1,2,3,4];
      for(let val = 0; val < squares.length; val++)
      {
          squares[val] = squares[val] * squares[val];
      }// squares will be equal to [1, 4, 9, 16]
      
      // Instead of looping over an array use map() as follows:
      var squares = [1,2,3,4].map(function (val) {  
          return val * val;  
      }); // squares will be equal to [1, 4, 9, 16]
      
  1. Comma Operator
    • The comma operator (,) evaluates each of its operands (from left to right) and returns the value of the last operand.
    • This lets you create a compound expression in which multiple expressions are evaluated, with the compound expression’s final value being the value of the rightmost of its member expressions.
    • This is commonly used to provide multiple parameters to a for loop.
    • It can also be used to assign values to a variables as shown in the following code snippet.
      var a = 0; 
      var b = ( a++, 99 ); 
      console.log(a);  // a will be equal to 1 
      console.log(b);  // b is equal to 99
      
      var a = 0; 
      var b = ( a+++a++, 99+a ); 
      console.log(a);  // a will be equal to 2 
      console.log(b);  // b is equal to 101
      
  1. Primitive Operations vs Function calls
    • A Primitive operation is an operation like finding minimum or maximum from two given numbers without calling the min() or max() function call from the MATH library.
    • Math.max() uses the ternary operator to return the max.
    • Avoiding the call to the Math.min() function and directly using the ternary operator will spare some nanosecond. However, this works differently in Math.max().
    • Using ternary operators, improve performance compared to making a call to the Math.min() or Math.max() functions.
      // Variable declarations
      var a = 5, b = 10;
      
      // Function Call
      Math.min(a,b); // returns 5
      Math.max(a,b); // returns 10
      
      // Primitive Operation
      a < b ? a : b; // returns 5
      a > b ? a : b; // returns 10
      
  1. Debugging using console.time() and console.timeEnd()
    • Boosting performance is one of the major concerns for all the developers as the end users always wants everything working correctly as well as fast.
    • Debugging is not just about using a console.log() every time and everywhere, instead to analyze the performance of a code block you can simply debug it with console.time() and console.timeEnd().
    • Calling the console.time() function is easy and it is also very handy tool to measure the performance. This function is usually written at top of the code block which a developer wants to test.
    • console.time() function takes in one argument which is a string that describes what you want to analyze.
    • console.timeEnd() function is written at the bottom of the code block that is to be tested.
    • console.timeEnd() function takes in one argument which is a string that is same as the one given in console.time() function. You’ll then see the time it took to run the code in your console.
      console.time('Loop_items');
      var items = [];
      for(var i = 0; i < 1000000; i++)
      {
          items.push({index: i});
      }
      console.timeEnd('Loop_items'); // outputs "Loop_items: 667.908ms"
      
  1. Passing arguments as objects instead of passing arguments individually to functions.
    • This way of passing arguments has lots of benefits:
      • The order of the parameters does not matter anymore, allowing you to concentrate on delivering high-quality code instead of repeatedly checking the function definition.
      • Auto-completion becomes easier as the IDE will focus on the specific argument that you are providing.
      • This method communicates intent in a clear way as function calls specify the value of each property.
      • Large codebase will benefit tremendously from the additional verbosity.
    // Instead of using 
    function personPrintArguments(firstName, lastName, age)
    {
        console.log(firstName + " " + lastName + " is " +age + " Years Old."); // John 40 is Doe Years Old.
    }
    
    personPrintArguments("John", 40, "Doe" ) // While passing arguments in the functions with parameters the order matters a lot or instead use named parameters.
    
    // Using Object as argument
    function personPrintObject(args)
    {
        console.log(args.firstName + " " + args.lastName + " is " + args.age + " Years Old."); // prints John Doe is 40 Years Old.
    }
    
    personPrintObject({ firstName : "John", age : 40, lastName : "Doe" })
    
  1. Dynamic property names with object passing as an argument
    • Assigning a dynamic property name can be helpful in the long run. There could be multiple scenarios where name of the property needs to be changed or updated due to some unforeseen reasons.
    • Dynamic property names can also be very code efficient as a developer does not need to change the property name very where in the entire script, changing a single variable value will affect in changing the property name in the entire script.
    // Dynamic property variables
    const dynamicFName = 'firstName';
    const dynamicLName = 'lastName';
    const dynamicAge = 'age';
    
    // Function calling
    function personPrintObject(args)
    {
        console.log(args[dynamicFName] + " " + args[dynamicLName] + " is " + args[dynamicAge] + " Years Old."); // prints John Doe is 40 Years Old.
    }
    
    personPrintObject({ [dynamicFName] : "John", [dynamicAge] : 40, [dynamicLName] : "Doe" })
    

    NOTE

    • The assigning and calling syntax for Dynamic property names are slightly different from the Passing arguments as object.
    • The Setting for Passing arguments as object uses the "firstName" property name directly. Whereas the Dynamic property name uses the "[dynamicFName]" variable to set the property name.
    • The calling for Passing arguments as object uses the (.) dot notation to access it’s member "args.firstName". Whereas the Dynamic property name uses the "args[dynamicFName]" dictionary format to call it’s property.

  1. Required Function Parameters
    • Another useful trick in javascript for Passing arguments as objects or Dynamic property name. Required function parameter is just another useful technique that will always make the user enter a value.
    • Expanding on the default parameter technique, we can mark a parameter as mandatory. First define a function to throw an error with an error message, as shown in the following code snippet:
    let isRequired = () =>
    {
        throw new Error('This is a mandatory parameter.');
    }
    
    • Then assign the function as the default value for the required parameters. Remember, the default values are ignored when a value is passed for a parameter at the invocation time. But, the default value is considered if the parameter value is undefined.
    function personPrintObject(args = isRequired())
    {
        console.log(args.firstName + " " + args.lastName + " is " + args.age + " Years Old."); // prints John Doe is 40 Years Old.
    }
    
    personPrintObject({ firstName : "John", age : 40, lastName : "Doe" })
    personPrintObject() // will call the is require function as no parameter is passed.
    personPrintObject( {} ) // Will call the personPrintObject() function as a blank object is equal to "undefined".
    
    • In the above code, args will be undefined and that will try to set the default value for it which is the isRequired() function. It will throw an error as,
  1. Object Destructing
    • The object destructuring is a useful JavaScript feature to extract properties from objects and bind them to variables.
    • Object destructuring can extract multiple properties in one statement, can access properties from nested objects, and can set a default value if the property doesn’t exist.
    • Object Destructuring in simple term means, extracting data from arrays, objects, and maps alongside setting them into new, distinct variables.
    • Three different examples for Object destructing:
      • Assigning to existing variable names
        var employee = // Object we want to destructure
        {
            firstname: 'Jon',
            lastname: 'Snow',
            dateofbirth: '1990'
        };
        // Destructuring the object into our variables
        var { firstname, lastname, dateofbirth } = employee;
        
        console.log( firstname, lastname, dateofbirth); // outputs Jon Snow 1990
        
      • Assigning to new variable names
        • The following code destructured the object into variables with a different name than the object property
        var employee = // Object we want to destructure
        {
            firstname: 'Jon',
            lastname: 'Snow',
            dateofbirth: '1990'
        };
        
        // Destructuring the object into variables with
        // different names than the object variables
        var { firstname: fn, lastname: ln, dateofbirth: dob } = employee;
        console.log( fn, ln, dob);  // outputs Jon Snow 1990
        
      • Assigning to a variable with default values
        • We can also assign default values to variables whose keys may not exist in the object we want to destructure. This will prevent our variable from having an undefined value being assigned to it.
        var employee = // Object we want to destructure
        {
            firstname: 'Jon',
            lastname: 'Snow',
            dateofbirth: '1990'
        };
        
        // Destructuring the object into variables without 
        // assigning default values 
        var { firstname, lastname, country } = employee;
        console.log( firstname, lastname, country); // outputs "Jon Snow undefined"
        
        // Destructuring the object into variables by 
        // assigning default values 
        var { firstname = 'default firstname', 
            lastname = 'default lastname', 
            country = 'default country' } = employee;
        console.log( firstname, lastname, country); // outputs "Jon Snow default country"
        

NOTE

  • Hope these tricks were helpful and you learnt something new out of it.


Mr. Ellis Tarmaster