Whenever we run a JavaScript code or even refer an empty JavaScript code file in HTML and run that code, the JavaScript engine creates an execution context and in that context it creates a global object and a special variable called 'this’ (also a link to outer environment is created if the outer environment exists). The global variable is available everywhere in the current window or current JavaScript file. If you are running the code inside a browser (unlike node.js) then the global object is the ‘window’ object. When you create a variable or function which is not inside any function(global level) then those variable or function gets attached to the global object. So if you create a global variable 'a' then you can refer to it as just 'a' or 'this.a' or 'window.a' (if running inside a browser).
The execution context works in 2 steps:
In the first 'creation’ step, the parser parses the whole JavaScript code and sets up memory for the variables(if the execution context is for a function then this will also include variables which are parameters of the function) and functions declared in current execution context (the variables or functions declared nested inside current level functions will not be available). This phase is called ‘hoisting’. (Note: variables are only declared and not initialized at this time, so their value will be 'undefined’, but function definitions will be stored so their definitions will be available). This means before the interpreter starts executing the code line by line, all variables and functions are available in memory for access by the code.
‘Undefined’ is a special value which means the value is not defined. So in the creation phase mentioned above, when memory is allocated to all declared variables, this undefined value is set at those memory locations. Although we can set a variable’s value to undefined, it is not recommended to set it manually as while debugging it will be difficult for a programmer to determine whether a variable is not defined till now or whether he manually set it to undefined. So you should set it to null instead of undefined.
The second step is execution step in which the code is executed line by line.
Whenever a function is invoked, a new execution context is created and put on the top of stack. The execution context on top of the stack is always the current execution context. This will also work in 2 phases i.e. creation and execution. Once the function execution is complete, the execution context is popped out of the stack. Even when a function invokes itself, a new execution context is created and pushed on top of the stack.
When a referred variable is not found in current execution context, then it is searched in the outer environment using the link created in the creation phase of the execution context.
JavaScript runs synchronously (single threaded) but other parts of browser like rendering engine are working in parallel with JavaScript engine. When an event e.g. click, occurs in browser and the JavaScript engine needs to be notified for this event, then that event is put in the event queue of the JavaScript engine.
When the stack is empty i.e. all execution context are complete including the global execution context (i.e. all code is executed), then only JavaScript will look periodically into this event queue and create and execute execution contexts for the event handler functions one by one.
When we write multiple <script></script> blocks one below the other to include JavaScript files in our HTML page, JavaScript behaves as if it is a single JavaScript file. Therefore all these files will share the same global execution context and all global variables will be attached to the same global object (window object in case of JavaScript running inside browser). So if a variable x is present in multiple JavaScript files at global level, it will be considered as the same variable.
JavaScript uses dynamic typing means datatype of variable is determined at runtime and not at compile time. So a single variable can hold different types of data.
The operators like ×-+ are basically functions with a different syntax.
Coercion is the implicit type casting that happens when two different types of parameters are passed to an operator.
Few conversions:
Number(true)=1
Number(false)=0
Number(undefined)=NaN
Number(null)=0
When we use strict equality(===) or strict inequality(!==) operators then coercion does not happen. So false == 0 is true but false === 0 is false. Hence always use strict equality/inequality operators unless it is required to ignore the types.
But, sometimes coercion can be useful. e.g. Boolean(undefined)==Boolean(null)==Boolean(“”)==Boolean(0)==false. So we can use ‘if(x)’ at the beginning of a block so that the block will execute only when x exists and has a valid value.
The || (or operator) behaves in a special way while comparing a non Boolean value. It returns the value which coerces to true instead of true. e.g. undefined || “hello” will return “hello” because undefined will coerce to false and “hello” will coerce to true as it is a non empty string. If both the arguments coerces to true then it will return the first one. We can use this behavior of or operator to assign default values to function parameters by writing x = x || 1 (Note: be careful while using 0 as Boolean(0)=false).
Reference:
JavaScript under the hood: https://youtu.be/Bv_5Zv5c-Ts
Good information shared....
ReplyDelete