Concepts of JavaScript — Part 1

📄 Table of Contents

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

◉ so, Javascript What are you ?

  • 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

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.

  • console.log(false == “0”); #true coz of implicit type co-ersion.
  • console.log(false === “0”); # no implicit type co-ersion.

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.

◉ == and ===

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

Simply put, the inner variable shadows the outer.

◉ Scoping

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

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.

Primitive Values

Example

const PI = 3.141592653589793;
PI = 3.14; // This will give an error
PI = PI + 10; // This will also give an error

Constant Objects can Change

// You can create a const object:
const car = {type:”Fiat”, model:”500", color:”white”};
// You can change a property:
car.color = “red”;
// You can add a property:
car.owner = “Johnson”;

But you can NOT reassign a constant object:

const car = {type:”Fiat”, model:”500", color:”white”};
car = {type:”Volvo”, model:”EX60", color:”red”}; // ERROR

Constant Arrays can Change

Example

// You can create a constant array:
const cars = [“Saab”, “Volvo”, “BMW”];
// You can change an element:
cars[0] = “Toyota”;
// You can add an element:
cars.push(“Audi”);

But you can NOT reassign a constant array:

Example

const cars = [“Saab”, “Volvo”, “BMW”];
cars = [“Toyota”, “Volvo”, “Audi”]; // ERROR

◉ Hoisting

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

  • 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.
var vs let vs const

◉ typeof (BS²NU-OF)

typeof present in Javascript

Errors while checking typeof

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 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)

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.

◉ JSArray Methods: Mutating vs. Non-Mutating

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'

◉ call, apply & bind

call, apply and bind with example
call, apply vs Bind

IIFE (Immediately Invoked Function Expression)

(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?

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 NodeJS, 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)

Examples: map, filter, reduce, forEach, sort 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 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

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);
//ORvar 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

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

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

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

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?

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()

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);

Pass by Value vs. Reference Variable Assignment

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