Concepts of JavaScript — Part 1

Image for post
Image for post

📄 Table of Contents

▬▬▬▬▬▬▬▬▬▬▬▬▬▬ ✦ ✦ ✦ ▬▬▬▬▬▬▬▬▬▬▬▬▬▬

◉ so, Javascript What are you ?

  • Synchronous, Blocking, Single-threaded language=> means that only one operation can be in progress at a time but when you hear folks say that JavaScript is an ASYNchronous language, what they mean is that you can manipulate JavaScript to behave in an asynchronous way. It’s not baked in, but it’s possible!
  • Is it a scripting or an interpreted programming language=> most of the time, it’s a little bit of both. But, we cannot treat JavaScript as a full-fledged programming language. JS lacks the following important features:

→ Client-side JavaScript does not allow the reading or writing of files. It has been kept for the security reason.

→ JavaScript could not used for networking applications because there is no such support available.

Client side scripting cannot be used to connect to the databases on the web server.

→ JavaScript doesn’t have any multithreading or multiprocessor capabilities.

  • Is it used both on the front or back end and it is open and cross-platform.
  • loosely typed => meaning that you don’t have to tell the interpreter (in advance) what kind of value you plan to store in a particular variable.
  • dynamically typed => means the type of a value in a particular variable can be changed.
  • parser blocking resource => This means that the parsing of the HTML document itself is blocked by JavaScript. When the parser reaches a <script> tag, whether that be internal or external, it stops to fetch (if it is external) and run it.
  • In JavaScript, functions are first class objects/citizens. As such, we can work with them in the same way we work with other objects, like assigning them to variables and passing them as arguments into other functions.
  • Javascript doesn’t support IMMUTABILITY, by default. However, it can be made to follow immutability in case of Objects and arrays using so called

concat(), slice(),

filter(), map(), reduce(), Object.assign(), …(spread Operator)

◉ Data types

In JS, there are primitive data types and reference data types. There are 7 primitive data types and an (1) object type. The 7 primitive types are boolean, null, undefined, number, BigInt, string and symbol.primitive data types are referenced by value while the non-primitive/reference data types point to memory addresses.

Image for post
Image for post
Data Types in JavaScript

When we write a primitive data type like this:

let one = 1;

On the call stack, the one variable directly points to the value 1:

Call Stack
#000 one -> | 1 |
#001 | |
#002 | |
#003 | |

And if we change the value of the one variable

let one = 1
one = 3

The one memory address #000 which holds the value 1 is directly changed to 3.

But, if we write a reference data type like this:

let arr = {
one: 1
}

OR

let arr = new Object()
arr.one = 1

JS will create the object in the Heap memory and store the memory address of the object in arr call stack location:

Call Stack       Heap
#000 arr -> | #101 | #101 | one: 1 |
#001 | | #102 | |
#002 | | #103 | |
#003 | | #104 | |

You see arr doesn't store the object directly but points to the memory location(#101) of the object.

Unlike primitive data types that hold its value directly.

Image for post
Image for post
  • console.log(false == “0”); #true coz of implicit type conversion
  • console.log(false === “0”); # no implicit type conversion

0, -0, “ “ (empty string), false, null, undefined, NaN are always falsy values in JS.

  • Any string NOT empty is true, even the string “false” is true.
Image for post
Image for post

◉ == and ===

When the equality operators are comparing primitives, they simply check if the values are the same. When the equality operators, == are used on reference-type variables, they check the reference.

However, the difference between == and === is that:

  • == converts the variable values to the same type before performing comparison. This is called type coercion.
  • === does not do any type conversion (coercion) and returns true only if both values & types are identical for the two variables being compared.

If we have two distinct objects and want to see if their properties are the same, the easiest way to do so is to turn them both into strings and then compare the strings.

var arr1str = JSON.stringify(arr1);
var arr2str = JSON.stringify(arr2);
console.log(arr1str === arr2str); // true

Another option would be to recursively loop through the objects and make sure each of the properties are the same.

◉ Variable Shadowing

In JavaScript, variables with the same name can be specified at multiple layers of nested scope.If you declare a local variable and a global variable with the same name, the local variable will take precedence when you use it inside a function. This type of behavior is called shadowing.

Simply put, the inner variable shadows the outer.

◉ Scoping

Variables declared Globally (outside any function) have Global Scope.

Image for post
Image for post
var, let, const

var can create both global & local scope depending on how it is being used.If you’re outside a function, then var will create a global scope.If you’re in a function, then var will create a local scope.

☛ “no var” will look up the scope chain until it finds the variable or hits the global scope (at which point it will create it):

block level scope in JS (ES5) vs C

Image for post
Image for post
Block level Scope in “C” vs JS

➪ exception with `const`

While Strings and Numeric values have always been IMMutable, objects & arrays can still be altered after assigned as a const.

If we assign an empty array with const, such as

const arr = [];

we can still push values to the array instance.

What we CANNOT do is assign a different array to our array variables.

Image for post
Image for post

◉ Hoisting

Hoisting in JavaScript is a feature in which any kind of declarations be it variable or function, wherever they occur, are processed before any code is executed.

In literal terms (as if) the interpreter moves the function and variable declarations to the top of their containing scope.

  • All declarations, both functions and variables, are hoisted to the top of the containing scope, before any part of your code is executed.
  • Hoisting only moves the declaration and NOT the assignment.
  • Ideally, Functions are hoisted first, and then variables, but in case of variable and function having same names,

Variable assignments > function declarations > variable declarations

Image for post
Image for post
  • Declare all of your variables at the top of their scope (at the top of the global scope or at the top of the function scope)
  • Make sure you put all your functions, if you can, also at the top of their scope.
Image for post
Image for post
var vs let vs const

◉ typeof (BS²NU-OF)

  • Objects with the BigInt type will return 'bigint'
Image for post
Image for post
typeof present in Javascript

Errors while checking typeof

Using the typeof operator before a variable declared with let or const will result in a ReferenceError being thrown, since it’s referencing a variable that’s not declared yet. Previous using the typeof operator on undeclared variables will return the type 'undefined’. The typeof operator never generated an error before ES2015.

For example, if we have:

typeof letVariable; 
typeof constant;
typeof c;
let letVariable;
const constant = 'constant';
class c{};

The first 3 lines will throw a ReferenceError when they’re run.

The typeof operator is useful for checking types of primitive types and objects. For primitive types, it will return the type of the object, except for null type, which will return object as the type. Objects, including arrays and other iterable objects, will always return object as the type. With variables declared with let or const keywords and classes, we have to declare them before using the typeof operator on them.

◉ Different types of Error(s):-

A ReferenceError is thrown if you use (reference) a variable that has not been declared:

A SyntaxError is thrown if you try to evaluate code with a syntax error.

A TypeError is thrown if you use a value that is outside the range of expected types:

  • ReferenceError: Cannot access ‘x’ before initialization
  • ReferenceError: i is not defined
  • SyntaxError: Missing initialiser in const declaration
  • SyntaxError: Identifier ‘xxx’ has already been declared
  • TypeError: Assignment to constant variable.
  • TypeError: a is not a function

◉ Immutable (Primitives) vs Mutables (Objects)

Primitives are IMMUtable in nature & stored by value while Non-Primitives (Objects) are Mutable & are stored by reference.

let a = [10]
let b = a
console.log(a === b) // true
a.push(10)
console.log(a) // [10, 10]
console.log(a === b) // true
let a = [];
let b = a;
let c = b;
a.push(1)
console.log(a) // [1]
console.log(b) // [1]
console.log(c) // [1]
b.push(2)
console.log(a) // [1, 2]
console.log(b) // [1, 2]
console.log(c) // [1, 2]
console.log([10] === [10]) // false

Here, what you’re comparing is NOT the value but the addresses. The [10] on the left has a different address in memory than the [10] on the left.

var y = {};
var x = y;

Above will NOT create a copy of “y”, rather it is “y”.

Both “x” and “y” will point to the same object.

❗How to achieve Immutability on JS data types.

  • > Object.assign(), concat, filter, map, reduce. All these methods return a brand new data/object.
Image for post
Image for post

◉ JSArray Methods: Mutating vs. Non-Mutating

JavaScript offers several ways to add, remove, and replace items in an array — but some of these ways mutate the array, and others are non-mutating; they produce a new array.

Image for post
Image for post
source: https://lorenstewart.me/2017/01/22/javascript-array-methods-mutating-vs-non-mutating/

Like array.pop() and array.shift(), array.splice() returns the items it removes. This means you can 'catch' the deleted item in a variable.

let mutatingRemove = ['a', 'b', 'c', 'd', 'e'];const returnedValue1 = mutatingRemove.pop();  
console.log(mutatingRemove); // ['a', 'b', 'c', 'd']
console.log(returnedValue1); // 'e'
const returnedValue2 = mutatingRemove.shift();
console.log(mutatingRemove); // ['b', 'c', 'd']
console.log(returnedValue2); // 'a'
Image for post
Image for post

◉ call, apply & bind

Use . call() or . apply() when you want to invoke the function immediately, and modify the context. Call/apply call the function immediately, whereas bind returns a function that, when later executed, will have the correct context set for calling the original function.

Image for post
Image for post
call, apply and bind with example
Image for post
Image for post
call, apply vs Bind

IIFE (Immediately Invoked Function Expression)

how an IIFE (Immediately Invoked Function Expression) is formed and what it looks like. ..

(function () {
var message = "Hello there !!!";
})();

The above code is called an IIFE, and is immediately invoked as soon as it is defined. Note that it’s a Function Expression which is why the function that is wrapped inside the parentheses is anonymous. You might ask, why the parentheses. Try removing the parentheses and execute the function in browser’s console. It throws an error as the parser identifies it as an function declaration and function declarations cannot be anonymous.

Why do we need an IIFE?

  • The most use case of an IIFE is to restrict the scope of variables to local so that they don’t pollute the global context. For example, compare the below two scenarios:
var loc = "global";//Function declaration
function logger() {
console.log(loc); // logs global
console.log(window.loc); // logs global
}
logger();---------------//Function Expression

(function () {
var loc = "global";
function logger() {
console.log(loc); // logs global
console.log(window.loc); // logs undefined
}
logger();
})();

In the first example, the variable is declared outside the function, which makes it available globally and can be accessed and changed through the global object ( in browser, the global object is window & in Node, the global object is global).

To prevent it from happening where one might get access to all globally declared variables and can change them in browser’s console, the whole piece of code including variables, functions, objects , if present are wrapped inside an IIFE, which gets invoked immediately in a different execution context as shown in the second example. You might ask, if the variables are created in different execution context, won’t they be discarded as soon as the function gets invoked. Yes, they do. But that’s where closures come into picture and there are many ways to harness combined power of closures and IIFEs to design a modular application.

  • Revealing Module Pattern makes use of IIFE internally.
  • Another typical use case of an IIFE and closure combination is to create a private state for any variable.
let reference = (function() {   
let secret = "I cannot be changed by simple assignment";
return {
//ES5 new method syntax
change(value) {
secret = value;
},
get secret() {
return secret;
}
};
})();
console.log(reference.secret); // "I cannot be changed by simple assignment"
reference.change("I am changed");
console.log(reference.secret); // "I am changed"

In the above example, the secret value can only be changed by using the method as the variable is not available globally and is discarded off as soon the IIFE gets executed. Due to the power of closures in JS, the secret value is still preserved and is passed along with the object to the reference variable, making it available to the change method, when required.

◉ High Order Functions (HOF)

A higher order function is a function that takes a function as an argument, or returns a function. Also, taking an other function as an argument is often referred as a callback function, because it is called back by the higher-order function.

Examples: map, sort, reduce, filter, forEach, map are HOF.

setTimeout, setTimeInterval, etc. are examples of Callback. We can also create custom callback.

One of the great advantages of using higher order functions when we can is composition.

Higher order function is in contrast to first order functions, which don’t take a function as an argument or return a function as output.

◉ Currying

Currying always returns another function with only one argument until all of the arguments have been applied. So, we just keep calling the returned function until we’ve exhausted all the arguments and the final value gets returned.

Note: Curry takes a binary function and returns a unary function that returns a unary function. Its JavaScript code is

// Normal function
function addition(x, y) {
return x + y;
}
addition(3,4);
// Curried function
function addition(x) {
return function(y) {
return x + y;
}
}
addition(3)(4);
OR
var sum = addition(3);
sum(4);
// Partial Application
function addition(x, y) {
return x + y;
}
const plus5 = addition.bind(null, 5)
plus5(10) // output -> 15

Currying are also closure functions.

const one = document.getElementById('one');
const t = a => b => c => a.addEventListener(b, (event) => {
event.target.style.color = c;
});
t(one)(‘click’)(‘red');

◉ JS Function Chaining (as in JQuery Chaining)

understanding jQuery chaining first of all

When we invoke jQuery functions, by default it returns us the object reference of the element on which the previous execution was performed. After each execution, the currently referenced object is returned and the next execution is performed on the same element reference.

Take a look at this code for clarification:

var mainDiv = $("#selectedDiv");var returnedDiv = mainDiv.css('color: red');// Here the Object returned from JQuery Function is same as "mainDiv"// Following would return true...
console(mainDiv == returnedDiv)

The code above first operates on an element myDiv. When the first jQuery operation is performed, the same element is returned back from the css function. If we try to equate the two variables, mainDiv and returnedElement, they come out to be the same references.

Achieving Chaining with JavaScript

In JavaScript, this refers to the current executing context. In other words, it refers to the object on which the function is executing. Let's try using this to achieve functional chaining.

We need to return the current object reference from the addNumber function:

How to perform chaining in JS (as in happens in jQuery)
# try another example
calculator.one().three().five().value() => 1+3+5 => 9

more @ https://medium.com/better-programming/javascript-function-chaining-8b2fbef76f7f

◉ Pure Functions

We refer to functions that don’t affect anything in the outside scope as pure functions. As long as a function only takes primitive values as parameters and doesn’t use any variables in its surrounding scope, it is automatically pure, as it can’t affect anything in the outside scope. All variables created inside are garbage-collected as soon as the function returns.

A function that takes in an Object, however, can mutate the state of its surrounding scope. If a function takes in an array reference and alters the array that it points to, perhaps by pushing to it, variables in the surrounding scope that reference that array see that change. After the function returns, the changes it makes persist in the outer scope. This can cause undesired side effects that can be difficult to track down.

Many native array functions, including Array.map and Array.filter, are therefore written as pure functions. They take in an array reference and internally, they copy the array and work with the copy instead of the original. This makes it so the original is untouched, the outer scope is unaffected, and we’re returned a reference to a brand new array.

Let’s go into an example of a pure vs. impure function.

function Impure(person) {
person.age = 25;
return person;
}
var foo = {
name: 'Anil',
age: 30
};
var fooUpdated = Impure(foo);console.log(foo); // -> { name: 'Anil', age: 25 }
console.log(fooUpdated); // -> { name: 'Anil', age: 25 }

This impure function takes in an object and changes the property age on that object to be 25. Because it acts on the reference it was given, it directly changes the object foo.

Note that when it returns the person object, it is returning the exact same object that was passed in.

foo and fooUpdated contain the same reference. It’s redundant to return the person variable and to store the reference in a new variable.

Let’s look at a pure function.

function Pure(person) {
var newPersonObj = JSON.parse(JSON.stringify(person));
newPersonObj.age = 25;
return newPersonObj;
}
var foo = {
name: 'Anil',
age: 30
};
var fooUpdated = Pure(foo);console.log(foo); // -> { name: 'Anil', age: 30 }
console.log(fooUpdated); // -> { name: 'Anil', age: 25 }

In this function, we use JSON.stringify to transform the object we’re passed into a string, and then parse it back into an object with JSON.parse. By performing this transformation and storing the result in a new variable, we’ve created a new object. There are other ways to do the same thing such as looping through the original object and assigning each of its properties to a new object, but this way is simplest. The new object has the same properties as the original but it is a distinctly separate object in memory.

When we change the age property on this new object, the original is unaffected. This function is now pure. It can’t affect any object outside its own scope, not even the object that was passed in. The new object needs to be returned and stored in a new variable or else it gets garbage collected once the function completes, as the object is no longer in scope.

Test Yourself

Value vs. reference is a concept often tested in coding interviews. Try to figure out for yourself what’s logged here.

function passByReference(person) {
person.age = 25;
person = {
name: 'James',
age: 50
};

return person;
}
var foo = {
name: 'Anil',
age: 30
};
var fooUpdated = passByReference(foo);console.log(foo); // -> ?
console.log(fooUpdated); // -> ?

The function first changes the property age on the original object it was passed in. It then reassigns the variable to a brand new object and returns that object. Here’s what the two objects are logged out.

console.log(foo); // -> { name: 'Anil', age: 25 }
console.log(fooUpdated); // -> { name: 'James', age: 50 }

Remember that assignment through function parameters is essentially the same as assignment with =. The variable person in the function contains a reference to the foo object, so initially it acts directly on that object.

Once we reassign person to a new object, it stops affecting the original.

This reassignment does not change the object that foo points to in the outer scope. person has a new reference because it was reassigned but this reassignment doesn’t change foo.

An equivalent piece of code to the above block would be:

var foo = {
name: 'Anil',
age: 30
};
var person = foo;person.age = 25;person = {
name: 'James',
age: 50
};
var fooUpdated = person;console.log(foo); // -> { name: 'Anil', age: 25 }
console.log(fooUpdated); // -> { name: 'James', age: '50' }

The only difference is that when we use the function, person is no longer in scope once the function ends.

more @ https://codeburst.io/explaining-value-vs-reference-in-javascript-647a975e12a0

◉ How can we return multiple (more than 1) values from JS?

Yes, either using an array or an object or using DE-structuring.

function getValues() {
return [getFirstValue(), getSecondValue()];
}

Then you can access them like so:

var values = getValues();
var first = values[0];
var second = values[1];

With the latest ECMAScript 6 syntax*, you can also de-structure the return value more intuitively:

const [first, second] = getValues();

If you want to put “labels” on each of the returned values (easier to maintain), you can even return via an object:

function getValues() {
return {
first: getFirstValue(),
second: getSecondValue(),
};
}

And to access them:

var values = getValues();
var first = values.first;
var second = values.second;

Or with ES6 syntax:

const {first, second} = getValues();

◉ for() vs forEach() vs map()

Image for post
Image for post

create a dynamic html element and style it the JS way

var para = document.createElement("P");   // Create a <p> element
para.innerText = "This is a paragraph"; // Insert text
document.body.appendChild(para); // Append <p> to <body>

# Attach a click event to a <button>
element.document.getElementById("myBtn").addEventListener("click", function(){
document.getElementById("demo").innerHTML = "Hello World";
});
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
var div = document.createElement("div");
div.style.width = "100px";
div.style.height = "100px";
div.style.background = "red";
div.style.color = “white";
div.setAttribute('class', 'myclass');
div.innerHTML = "my <b>new</b> <large>DOM maniuplation!</large>";
document.getElementById("main").appendChild(div);

Value vs. Reference Variable Assignment

How JavaScript assigns values to variables is one of the most crucial concepts any JavaScript developer should know. If you do not know this, you might assign values to variables and accidentally change it, which will lead to unexpected bugs in the code.

JavaScript ALWAYS assigns variables by its value. However, there are two major categories. If the assigned value is of a JavaScript primitive type (boolean, null, undefined, string, number , bigint and symbol), the actual value is assigned to the variable. But, if the assigned value is an Array, Function, or Object , a reference to the object in memory is assigned instead of the actual value.

Let’s have a look at some examples to understand this concept better.

Consider the variables name1 and name2 .

let name1 = "John";
let name2 = name1;

The variable name2 has been assigned the variable name1 . Since these variables are of the primitive type, the actual value (“John”) gets assigned to both variables. Therefore, these two variables can be considered as two separate variables with the same value. Due to this reason, re-assigning the second variable makes no impact on the first variable.

name2 = "Peter";
console.log(name1, name2);
// "John", "Peter"

This is called assigning a variable by value.

The next method is assigning a variable by reference. If the variable type is either array, object, or a function, the variable is allocated a reference in memory instead of the actual value.

Let’s consider the following object assignment.

let obj1 = { name: "Lucy" }
let obj2 = obj1;

The variable obj2 gets the same memory reference as obj1 with this assignment. Therefore, mutating obj2 will also impact obj1 as they are not considered as separate variables anymore. Both of these variables have the same reference in memory.

obj2.name = "Mary";
console.log(obj1);
// { name: "Mary" }
console.log(obj2);
// { name: "Mary" }

If you need to create a copy of the same object with a different reference in memory, you can use the spread operator. Mutating the newly created object in this manner would not affect the first object as they have different references in the memory.

let object1 = { name: "Lucy" };
let object3 = {...object1}
object3.name = "Anne";
console.log(object1);
// { name: "Lucy" }
console.log(object3);
// { name: "Anne" }

Reference(s):

https://blog.bitsrc.io/understanding-javascript-mutation-and-pure-functions-7231cc2180d

http://youtube/techsith

Experience with Front-end Technologies and MERN / MEAN Stack. Working on all Major UI Frameworks like React, Angular.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store