Undoubtedly, JavaScript is by far one of the best dynamic programming languages whole over the tech industry. If you go far as a JavaScript Developer you need to know what happens behind the scene in JavaScript.
Every JavaScript Engine has a powerful and intelligent parser. This is works with the source code. If the source code is right in the parser then it is converted into ‘Abstract Window Tree or Data Structure’ after that converted into Machine Code, finally, the code is ‘Run’ and we will get our expected answer. See the example.
var name = 3;
name == window.name;
//Output: true
Execution Stack: Execution Stack is also known as "calling stack". It’s basically who the code executed step by step. This stack works with the concept of Last In First Out (LIFO).
var name = 'Zonayed';
function first() {
var welcome = 'Hello ';
second();
console.log(welcome + name);
}
function second() {
var welcome2 = 'Hi! ';
third();
console.log(welcome2 + name);
}
function third() {
var welcome3 = 'Hey! ';
console.log(welcome3 + name);
}
first();

- Variable Object — It contains function arguments, inner variable declarations, and function declarations.
- Scope Chain — It contains the current variable object and all the variable objects of all its parents.
- “This” Variable
Note that, When a function gets a call there creates a new execution context and it happens in a two-step.
-
Creation Phase
a) Create a Variable Object.
b) Create Scope Chain. -
Execution Phase
a) Determining the value of the “this” variable.
b) Read & Run line by line
c) Define All Variables
👉🏻 let and const variables ==> ReferenceError: Cannot access before initialization
👉🏻 var ==> undefined👉🏻 var ==> var hoisting is just a byproduct of function declaration
***Accessing variables before declaration is a bad practice and should be avoided.
// Variables //
console.log(me); // returns: undefined
console.log(job); // returns: ReferenceError: Cannot access 'job' before initialization
console.log(year); // returns: ReferenceError: Cannot access 'job' before initialization
var me = 'jonas';
let job = 'teacher';
const year = 1991;
// Functions //
console.log(addDecl(2, 3)); // returns: 5
console.log(addExpr(2, 3)); // returns: ReferenceError: Cannot access 'addExpr' before initialization
console.log(addArrow(2, 3)); // returns: ReferenceError: Cannot access 'addArrow' before initialization
function addDecl(a, b){
return a + b;
}
const addExpr = function(a, b){
return a + b;
}
const addArrow = (a,b) => a + b;
// Using var //
var addExpr = function(a, b){
return a + b; // returns: TypeError: addExpr is not a function
}
var addArrow = (a,b) => a + b; // returns: TypeError: addArrow is not a function
// Reason: any variable declared with var will be hoisted and set to undefined and now this 'addExpr or addArrow' are undefined, we are trying to call undefined(2,3)
// Example
if(!numberProducts) deleteShoppingCart();
var numberProducts = 10;
function deleteShoppingCart(){
console.log('All products deleted!');
}
// returns: All products deleted!
// reason: numberProducts = 10 but we are using 'var' variable and we call it before declaration so var hoisting is undefined, so return is very DANGEROUS
/*
// Solutions //
1. just dont use 'var' to declare variable
2. use 'const' most of the time
3. if need to change then use 'let'
4. to write clean code should declare variable at the first
5. always declare functions first and use them only after declaration
6. you could use function declaration before you declare them but dont try this
*/
var x = 1;
let y = 2;
const z = 3;
console.log(x === window.x); // returns: true
console.log(y === window.y); // returns: false
console.log(z === window.z); // returns: false
// variable declared with 'var' will create a window property
'use strict';
const firstName = 'Jonas'; // <== Global variable
function calcAge(birthyear) { // <== Scope
const age = 2037 - birthyear; // <== Local variable | if we call return here, variable not needed
function printAge() {
let output = `${firstName} You are ${age} years old, born in ${birthyear}`;
console.log(output);
if (birthyear >= 1981 && birthyear <= 1996) { // <== Block scope
// Creating NEW variable with same name as outer scope's variable
let firstName = 'Steven'; // <== JavaScript use this variable and don't lookup for similer global variable
// Reassigning outer scope's variable
output = 'NEW OUTPUT!';
var millenial = true; // <== Keep in mind when working with Old codes
const str = `Oh, and you're a millenial ${firstName}`;
console.log(str);
function add(a, b) {
return a + b;
}
}
console.log(output)
/*
add(2, 3); // <== calling a function outside of function because 'strict' mode is disabled
console.log(add(2, 3));
*/
console.log(millenial);
// concole.log(str) <== var is a functional scope, we can acces this scope outside of a function
}
printAge();
return age;
console.log(age); // <== After return code will not run
}
calcAge(1991); //
// console.log(age); <== We can't access inside function
// printAge(); <==
/*
const firstName = 'Steven'; <== Local variable
const firstName = 'Jonas'; <== Global variable
// No problem with that those are completely different variable just similer name
// You can also declare similer perameter name, because each parameter is associated with each function
*/
The this keyword
'use strict';
console.log(this); // returns: window | global object
const calcAge = function (birthyear) {
console.log(2037 - birthyear);
console.log(this) // returns: undefined | if strict mode off ==> returns: window
};
calcAge(1994);
const calcAgeArrow = birthyear => {
console.log(2037 - birthyear);
console.log(this) // returns: window
};
calcAgeArrow(1994);
// Arrow function use 'Lexical this' | parent scope
const jonas = {
year: 1994,
calcAge: function () {
console.log(2037 - this.year);
console.log(this) // returns: jonas object
}
}
jonas.calcAge();
// This is point to the Object that is calling the Method
// We might thing we wrote calcAge method inside of jonas object, that's why 'this' is point to jonas ==NO==
// The reason why 'this' keyword is point to jonas object just because jonas is calling the Method
const matilda = {
year: 2017,
};
// Method Borrowing
matilda.calcAge = jonas.calcAge; // Copy Method to one object to other
matilda.calcAge(); // calling function
// Method Borrowing //
// copy method from one object to other
// *** 'This' keyword pointing on ==> who is 'calling' the method
*** 'this' keyword pointing on ==> who is 'calling' the method
*** This is NOT static. It depends on how the function is called, and its value is only assigned when the function is actually called
Regular Functions vs Arrow Functions
const jonas = {
firstName: 'Jonas',
year: 1991,
calcAge: function() {
console.log(this); // returns: Jonas object
console.log(2037 - this.year);
},
greet: () => console.log(`Hey ${this.firstName}`), // returns: Hey undefined
// Arrow function() does not get its own 'this' keyword
};
jonas.greet();
console.log(this) // returns: window object
console.log(this.firstName) // undefined
// Reason: On the window object there is no 'firstName' property so, returns: undefined
Again, What if
var firstName = 'Matilda';
//if we declare variables with 'var' it create global property on window/global object
console.log(this) // window object
window.firstName = 'Matilda';
const jonas = {
firstName: 'Jonas',
year: 1991,
calcAge: function() {
console.log(2037 - this.year);
},
greet: () => console.log(`Hey ${this.firstName}`), // returns: Hey Matilda
};
jonas.greet();
Solution | Use Regular Function
const jonas = {
firstName: 'Jonas',
year: 1991,
calcAge() {
console.log(this); // returns: Jonas object
console.log(2037 - this.year);
},
/*
greet: () => console.log(`Hey ${this.firstName}`), // returns: Hey undefined
// Arrow function() does not get its own 'this' keyword
*/
greet: function () {
console.log(this); // returns: Jonas object
console.log(`Hey ${this.firstName}`) // returns: Hey Jonas
},
};
jonas.greet();
Method vs Function
Method and a function are the same, with different terms. A method is a procedure or function in object-oriented programming.
A function is a group of reusable code which can be called anywhere in your program. This eliminates the need for writing the same code again and again. It helps programmers in writing modular codes.
Function inside a Function, Using 'this' keyword
const jonas = {
firstName: 'Jonas',
year: 1991,
calcAge: function () {
console.log(this); // returns: Jonas object
console.log(2037 - this.year); // returns: 46
const isMillenial = function(){
console.log(this) // returns: undefined
console.log(this.year >= 1981 && this.year <=1996);
// returns: TypeError: Cannot read property 'year' of undefined
};
isMillenial();
},
greet: function () {
console.log(this); // returns: Jonas object
console.log(`Hey ${this.firstName}`) // returns: Hey Jonas
},
};
jonas.greet();
jonas.calcAge(); // function inside a method
Solution 1 | Pre ES6 Solution
const jonas = {
firstName: 'Jonas',
year: 1991,
calcAge: function () {
console.log(this); // returns: Jonas object
console.log(2037 - this.year); // returns: 46
// Solution 1 | Pre ES6 solution //
const self = this; // self or that
const isMillenial = function () {
console.log(self); // returns: jonas object
console.log(self.year >= 1981 && self.year <= 1996);
// console.log(this.year >= 1981 && this.year <= 1996);
};
isMillenial();
},
greet: function () {
console.log(this); // returns: Jonas object
console.log(`Hey ${this.firstName}`) // returns: Hey Jonas
},
};
jonas.greet();
jonas.calcAge(); // function inside a method
Solution 2 | Modern solution using Arrow Function
const jonas = {
firstName: 'Jonas',
year: 1991,
calcAge: function () {
console.log(this); // returns: Jonas object
console.log(2037 - this.year); // returns: 46
// Solution 1 | Pre ES6 solution //
/*
const self = this; // self or that
const isMillenial = function () {
console.log(self);
console.log(self.year >= 1981 && self.year <= 1996);
// console.log(this.year >= 1981 && this.year <= 1996);
};
*/
// Solution 2 | Modern solution using Arrow Function //
const isMillenial = () => {
// Arrow function inherit 'this' keyword from its parent scope
console.log(this);
console.log(this.year >= 1981 && this.year <= 1996);
};
// Arrow Function inherit 'this' keyqord from parent scope
isMillenial();
},
greet: function () {
console.log(this); // returns: Jonas object
console.log(`Hey ${this.firstName}`) // returns: Hey Jonas
},
};
jonas.greet();
jonas.calcAge(); // function inside a method
Arguments Keyword
👉🏻 Arguments keyword exists but it only exists in the Regular function but not in the arrow function.
const addExpr = function (a, b) {
console.log(arguments) // returns: arguments array
return a + b;
};
addExpr(2, 5);
addExpr(2, 5, 8, 12); // we can add more arguments
const addArrow = (a, b) => {
console.log(arguments) // returns: ReferenceError: arguments is not defined
return a + b;
};
addArrow(2, 5, 8)
Primitives vs. Objects (Primitive vs. Reference Types)
// Primative types
let age = 30;
let oldAge = age; // at this point age still 30
age = 31; // now age changed but it will not effect oldAge variable
console.log(age) // returns: 31
console.log(oldAge) // returns: 30
// What if,
1 === 1 // returns: true, value same
[1] === [1] // returns: false, it just compare reference
{1} === {1} // returns: false, it just compare reference
// Reference types
const me = {
name: 'Jonas',
age: 30,
}
const friend = me;
friend.age = 27;
// same as
me.age = 28
console.log('Friends:', friend); // returns: { name: 'Jonas', age: 27}
console.log('Me', me);
// returns: { name: 'Jonas', age: 27}
// 'const' variable are not changeable in PRIMATIVE values
// but NOT changeable in REFERENCE values
// When we declare a variable as an object, an indentifier is created which point to piece of memory in STACK which turn point to piece of memory in the HEAP // Primitives Types
let lastName = 'Williams'; // new value
let oldLastName = lastName; // copied value
lastName = 'Davis'; // reassign value - present value
console.log(lastName, oldLastName); // Davis Williams
// Reference Types
const jessica = {
firstName: 'Jesica',
lastName: 'Williams',
age: 27,
}; // Jesica got married and have to changed his lastName
const marriedJessica = jessica;
marriedJessica.lastName = 'Davis'
console.log('Before Marriage: ' , jessica);
// returns: {firstName: 'Jesica', lastName: 'Williams', age: 27}
console.log('After Marriage: ', marriedJessica);
// returns: {firstName: 'Jesica', lastName: 'Williams', age: 27}
// It will not changed 😟
// Because: when we are attemped to copy original jesica object, NOT create a new object in the HEAP. marriedJessica is not a new object in the HEAP, it just a simple another variable in the STACK to hold the reference, Both of this two variable simply indicate the same stored memory address in the HEAP. Two different variable but same memory address.
// Assinging a new object to it is completely different to change property
// Copying object
const jessica2 = {
firstName: 'Jesica',
lastName: 'Williams',
age: 27,
family: ['Alice', 'Bob'] // Nested Reference Value
};
const jessicaCopy = Object.assign({}, jessica2); // OR const jessicaCopy = {...jessica2}
jessicaCopy.lastName = 'Davis'
console.log('Before Marriage: ' , jessica2);
// returns: {firstName: 'Jesica', lastName: 'Williams', age: 27}
console.log('After Marriage: ', jessicaCopy);
// returns: {firstName: 'Jesica', lastName: 'Davis', age: 27}
// THERE IS A PROBLEM: Nested Reference Value will not Copy IMMUTABLY if there make any changes
jessicaCopy.family.push('Mary');
jessicaCopy.family.push('John');
// Deep Clone Library 🧲LODASH























0 Comments