Variables and data types in javascript

Important points to remember

  • JS is dynamic typed language as opposed to statically typed language (c, java, c#). You can find the type of variables at runtime using typeof keyword.
  • JS is weakly typed (can add number+string) as opposed to strongly typed language (python - can not add stringwith num)

Variables

In Javascript variable declaration is optional. But it is not good practise. You can force variable declaration using "use strict" statement at the beginning of the script. Variables can be declared using 3 ways
  • var - Variable has function scope and it is hoisted. Variables created without the keyword var have global scope even if they are created inside a function.
  • let - Variable has block scope. let and const are also hoisted but are not initialized with default value.
  • const - similar to Const. Const variables can be assigned only once

//example 1
if (true)
{
var a = 10
}
// prints 10
console.log(a)


//example 2
if (true)
{
let b = 10
}
// Uncaught ReferenceError: b is not defined
console.log(b)

Hoisting

Variables are hoisted meaning variable declarations are moved to the top of current scope. With 'use strict', you can force devs to declare the variable before using it.
  • Hoisting means moving the declarations to the top of current scope - to the top of function or script or block in case of let and const variables
  • Only declarations are hoisted.. Initialization is not hoisted

//can not access a before initialization = Temporal dead zone
a = 5 // We are in temporal dead zone as this statement comes before declaration of a
console.log(a)
let a = 10 //temporal dead zone ends

// Temporal dead zone
{    
    const func = () => console.log(b);   
    let b = 3; 
    func(); // Called function outside of TDZ
  }
  
//let benefits - block scope, can not access before initialization and can not redeclare same variable

//prints undefined because c is hoisted and initilized with undefined
//can access c before initialization in temporal dead zone
console.log(c)
var c = 10

Variables Types

Here is the list of primitive data types (stored on STACK )
  • Number
  • Bigint
  • String
  • Boolean
  • null
  • Undefined
  • Symbol

Symbol is used to get the unique id which can be used as a key for the object.


console.log("Hello World");

// JavaScript is dynamically typed language and weakly typed language
//int a = 10 -> static type - c,c++,c#,Java

let a = 10;
let b = "Hello"

console.log('typeof a->', typeof a); // infered type is number
console.log('typeof b->', typeof b); // infered type is string


//python - strongly typed language
//javascript - weakly typed language

console.log('a' + 10);
console.log('11' + 10);
console.log(parseInt('11') + parseInt('10'));
console.log( 10 + '11');
console.log( 10 * '11'); // '11' is automatically converted from string type to number type

//primitives are immutable and stored on stack and copied by value
//string [String], number[Number], bigint [BigInt], symbol[Symbol], boolean[Boolean], undefined, null
//BigInt can be used to hold any number larger than 2^53 - 1


let obj = {};
let prop = Symbol();

obj[prop] = 1;  //symbol prop is unique.
obj.prop  = 2;  // prop can be overwritten by other lib

console.log(obj[prop], obj.prop, obj); 

//reference or Objects on heap
// object, function, Array, Map, Set, Date
           
let user = {
  name: "sagar"
};

// creating Symbol
let x = Symbol();

// adding symbol as a key
user[x] = "abc";

y = Symbol();
user[y] = "xyz";

console.log(user)

//output will be as below
//Object { name: "sagar", Symbol(): "abc", Symbol(): "xyz" }
Here is the list of reference data types (stored on HEAP )
  • Objects
  • Functions
  • Arrays

          let str = "Sagar"; 
          alert(str.toUpperCase() );
In above example, The string str is a primitive. But when accessing method, a special object is created (boxing process) that has methods like charAt(), split(), slice(), toUpperCase() etc. That method runs and returns a new string. The special object is destroyed, leaving the primitive str alone.

Type Conversions

  • string to number - You can use - Number("11.2"), parseInt, parseFloat, Math.round(), Math.floor() or Math.ceil()
  • number to string - obj.toString() or String(obj)
  • string to date - Date.parse() - ISO 8601 date format is YYYY-MM-DD or new Date() can be used
  • date to string -

console.log(Number("1"))
//1

console.log(Number("1 1"))
//NaN

console.log(parseInt("1 1"))
//1

console.log(parseInt("dfdf"))
//NaN

var date = new Date('09/13/22 10:23:48'); date.toString() 
'Tue Sep 13 2022 10:23:48 GMT+1000 (Australian Eastern Standard Time)'

var date = new Date('09/13/22 10:23:48 PM UTC');date.toString() 
'Wed Sep 14 2022 08:23:48 GMT+1000 (Australian Eastern Standard Time)'

new Date()
Wed Sep 14 2022 08:36:37 GMT+1000 (Australian Eastern Standard Time)

new Date().toGMTString()
'Tue, 13 Sep 2022 22:37:04 GMT'

new Date().toISOString()
'2022-09-13T22:37:33.132Z'

Execution context

  • In js everything is lexically scoped except this keyword.
  • execution context is created when function is called
  • execution context is pushed on the stack
  • global execution context provides globalObject and this variables. You can use them anywhere in code
  • global execution context is created by default by js engine. That's how we can access global object and this object
  • global object - window and global in node
  • let a = 10 - this statment pollutes global execution context
  • Scope chain help execution context in inner functions to get access to variables in outer scope
  • this keyword is not lexically scoped. If you want to make it lexically scoped, you can use arrow functions
  • It does not matter where "this" is used. what matters is which object called the function

    const o1 = {
    hi(){
        console.log("outer" , this)

        let f1 = function(){
            console.log("inner" , this)
        }
        f1()
    }
}

o1.hi()
const o1 = {
    hi(){
        console.log("outer" , this)
        //arrow functions are lexically scoped
        let f1 = () => {
            console.log("inner" , this)
        }
        f1()
    }
}

o1.hi()
const o1 = {
    hi(){
        console.log("outer" , this)
        //before arrow functions were introduced, they used bind
        let f1 = function(){
            console.log("inner" , this)
        }
        f1.bind(this)()
    }
}

o1.hi()

Scope of variables

  • determines where we can use the variable
  • if you have a variable declared with var in a function, scope is restricted to that function only and closures and inner functions
  • variables that have not been declared with var are not hoisted
  • let and const have scope limited to block
  • function and variables declarations are hoisted
  • only var, let, const and functions are hoisted
  • function expression are not hoisted - (function x ()) will not be hoisted
  • variables delcared in outside/enclosing scope can be accessed from inside scope
  • variables declared in inner scope can not be accessed from outside scope
  • function scope (var - accessible in function anywhere) vs block scope (let/const - accessible only in a block)

//print undefined as declarations are hoisted not the assignments
console.log(a)

//d is not defined - reference error
console.log(d)

var a = 10
d = 20

function x()
{
    //function scope - inner scope
    b = 11
}

//b is not defined - reference error - can not access b declared
// in inner scope from outside
console.log(b)

Scope Chain

When there are layers of scopes, then lexical scoping rules are followed except for "this"

let a = 10

//globally you can access only a. Everthing declared inside functions can 
//be accesse from here

function x()
{
    let b = 20
    
    //function x can access a and b only

    function y()
    {
        
        let c = 30
        //function can access a,b,c
        //if any variable is not available in current scope (function y), 
        //engine will lookup outer scope (function x)
        //if not found, it will lookup in global scope
        //if not found referencError will be shown
        console.log(a,b,c)
    }
    y()

}
x()

More examples


// console.dir(globalThis);
// console.dir(global);


//global, function and block
//Before ES6, only global and function scope was available


//You will learn
/*
- var vs not var
- hoisting
- temporal dead zone
- var vs let vs const
- use "strict"
*/


//ReferenceError -  a is not defined
//console.log(a)
a=10 // a will not be hoisted as it is not declared using var. Bad practise so do not use 

//now initialise without var - Pollutes global object
//b is attached to global
//b is not hoisted
b=10
console.log("Value of b -> ", b)

//var - This is hoisted and initial value is undefined. No ReferenceError
// function or global scope is created
//c is not attached to global in node but in Window it is attached so Avoid creating globally scoped
//variables

console.log("Value of c -> ", c)
var c = 22
console.log(globalThis)

function x(){
  //c is resolved using lexical scope
  console.log("globally scoped c - " , c)
}

function y(){
  var c = 23 // this c is function scoped
  console.log("function scoped c - ", c)
}

x()
y()

var f1 = () => {
  var c = 24
  console.log("f1 lambda scoped c - ",c)
}

f1()

// var vs let
// let allows block level scoping - anything inside {} is block scoped
{
  let c = 25
  console.log("block level  c - ",c)
  let d = 32
}

console.log("globally scoped c - ",c);
//console.log(" can we access d - ",d) // reference error

//reference error is thrown when variable is not found lexical scope
//first find if variable is declared/hoisted in current scope e.g. block,
// if not, find if variable is declared/hoisted in outer scope e.g. function 
// if not, find if variable is declared/hoisted in outer most scope e.g. global scope 
// if not found, throw referenceError exception


//IIFE
(()=>{
  let k = 10
  if (true)
    {
      // k is not in if block, so JS will check outer scope i.e. lambda function
      // k is found so it is printed on console
      console.log(k);
    }
})()

// use "strict"
// global or function level strict mode can be set
// you must declare variables in strict mode
// reserved keywords should not be used in code

Web development and Automation testing

solutions delivered!!