JavaScript 🌈

Let's learn a single threaded, multiparadigm and just-in-time compiled language

1. Language Basics (syntax and stuff)

Type conversion and type coercion

Type Conversion - Manually converting from one type to another

String to Number conversion

We can only convert strings that look like numbers to a number otherwise we get Not a Number

let year = '1991'
let month = 'jan'
console.log(Number(year)) // 1991 -> Number
console.log(Number(month)) // NaN -> Not a number

Click here for -> More On NaN

Type Coercion - When JS converts the type behind the scene

Type conversion happens automatically when the operator is working with two types of values and require to convert one of the values to match the other value type

console.log("I am " + 23 + "years old") // here 23 which is a number gets converted
//to string automatically by JS. This is type coercion

// If JS didn't have type coercion then we have to do this
console.log("I am " + String(23) + "years old") // but luckily we have type coercion
circle-check

Example

type coersion

Truthy and falsy values

Any value that gets converted to either true or false can be categorized as truthy and falsy values

Apart from the below-mentioned ones, all other values get true.

Falsy values are

  • 0

  • ' '

  • undefined

  • null

  • NaN

== vs ===

=== is a strict equality operator because it doesn't perform type coercion.

It returns true if both values are exactly the same.

== is loose equality operator and does the type coercion.

Some of the confusing examples. Remember to type convert in your head and think what it should be if the given example is ==

circle-info

when given lhs == rhs, to tell the output,

First convert lhs to match the rhs type or vice versa (which ever makes sense) and then apply === in your head to find the answer.

Example

'true' == true // false - this might be confusing

'true' is a string and true is bool.

string can't be converted to bool and vice versa so it will be false

Statements vs Expressions

Any code that gives values is an expression and any code that doesn't give a value but performs some action is called a statement.

JS expects expressions in particular places so you should know that.

Conditional (Ternary) operators

Null Vs Undefined

Undefined is set generally by JS. For example, for a variable, it sets undefined when it's just declared but not initialized. Even in case of hoisting. undefined means, something exists but not defined yet or yet to be defined.

Null is generally set by the programmer and not JS. Null means nothing exists unlike undefined. Again, undefined means, something exists which is not defined yet, and null means nothing exists.

Scoping

Refer to the hand-written notes for scoping but here is the confusing part you need to be aware of about scoping.

same variable
different variable

Not defined vs not initialized

not defined vs not initialized

arguments

Javascript function defined with function keyword will have access to a special object called arguments. This is not present in arrow-function. This is Array-like structure which has access to the length variable and can be converted to array using Array.from() or spread operator -> [...arguments]. It's not an array so we can't access methods like map and forEach on this. We can do arguments[0], arguments[1]

Deep copy

How can we deep copy?

deep copy
My code for deep copy

2. Modern Javascript development

Modules

modern JS development workflow
  • Earlier we used to write all JS code into one big or multiple scripts and ship it to the browser

  • Today we write our code in modules and these modules can share data between them. We also include 3rd party modules and we call them packages as well

  • These modules are generally shared in the npm repository and then we can use them by downloading them using npm command-line-tool. Example react library, leaflet, and so on.

  • npm means Node Package Manager

  • Let's say we are done writing our code and done with the project. Now we divide that code into multiple modules and also include the 3rd party modules which were used in our project.

  • Now after building the modules, our project goes through a build process where one big final JS file is built. That is the final file that will be deployed to the web-sever for production

  • In the build process, first, we bundle all the modules into one big file. This is a complex process that will compress the code and eliminate unused code

  • we still combine into one big file because old browsers won't support modules and the module could not be executed by old browsers.

  • The second step is transpiling and polyfiling. This will convert all ES6 syntax into old syntax where the browser can understand. The transpiling/ polyfiling is done by a tool called Babel

  • After these two steps, we will finally have our JS file ready to be deployed to the server

  • We don't perform these steps manually, instead, we use build tools like Webpak or parcel. These are called Javascript bundlers as they bundle the code/modules into one single file

  • Webpack is the popular one but its a complex one as a lot of steps must be done whereas, the parcel has zero configuration to be done

Modules

Modules Overview
Modules overview continued
Native ES6 modules which was not there in JS before
How ES6 modules are imported
circle-check
Udemy 268. Exporting and Importing in ES6 Modules

Module pattern

Module pattern was used before ES6 modules. The idea was similar to modules and we used to use functions to do the same and returned values (especially objects) were exposed or returned as a public API to be used in other JS script files

module pattern

Common JS pattern

Besides the module pattern, there are other patterns. They are not native JS patterns like modules. They relied on some external implementations. Two examples are AMD modules and CommonJS modules.

CommonJS is important because they have been used for node js for almost all modules. Very recently the ES modules started to be implemented in node js. Node JS is, running JS outside of a web browser. All the common js modules which are used in node can also be used in our implementations. That's because npm was started only for node initially and has now become a module repository for the entire javascript world.

Just like ES6 modules, one file is one module in common js as well. We need to know how we export and import in common js

Exporting in a common js pattern. Note this doesn't work in a browser but it works in node js

exporting a module

Importing in a common js module.

importing a module

NPM

Node Package Manager is a repository of JS modules and also a command-line tool to manage (install and update) these packages.

Earlier, we used to download different script tags for different things in JS. We used to download many JQuery plugins from different websites with different versions inside HTML files and it was all messy.

Drawbacks of the traditional way of downloading scripts

  • We don't get every script tag in one place

  • If they update the version we can't know that and we had to install it manually or change the script tag in HTML

How to use npm?

triangle-exclamation
circle-info

Note: Lodash is a popular package/module containing useful functions for arrays, objects, dates, and so on

circle-check

Parcel - Module bundler

It's a build tool available on npm.

circle-info

Note we have two type of dependency we get from npm

  1. Normal dependency - npm i module

  2. Dev dependency - npm i module --save-dev

Dev dependency is a tool or tools used to make a developer job easy and fast but our code doesn't need that. We don't include that dependency in our code

npm i parcel --save-dev

npm in parcel@desiredVersion --save-dev for a specific version

This was locally installed now. Only installed on to the folder you were in. To run this we have two options

  • Using npx command

  • npm script

1st way : npx - Is an app built into npm to run the locally installed packages

npx parcel index.html

For this to work, remove type=module in your html script tag

index.html is where the script lies which we need to bundle. What all gets bundled?

Well, the script itself and the imports used in the scripts all get bundled and compressed giving us a dist (distribution) folder that will deploy into production. This will have bundled files. It will also give us a new development server that gets updated on a refresh.

with parcel, we can configure hot module replacement

Also, imports are now easy with parcel

import cloneDeep from 'loadsh-es'

we can now use common js modules as well because of parcel

import cloneDeep from 'loadsh'

2nd way to run locally installed packages - npm scripts

In package.json file, under scripts object, create a script for the one you need to run in the command line. For example

"scripts":{

"start" : "parcel index.html" // note we dont use npx here

}

Now to use/run this in the command line, use

npm run start

Once we are done with the development we need to build it

"scripts":{

"start" : "parcel index.html", // note we dont use npx here

"build" : "parcel build index.html"

}

After running this build, the dist folder files get compressed which can then be shipped to the browser

Babel and Polyfilling

Now that we activated bundling using parcel, it's now time to transpile (convert back) our modern es6 code to es5 code (browser understandable code)

Babel works with plugins and presets that can both be configured.

The Plugin is a specific JS we need to transpile. For example, the arrow function is a plugin we can transpile and leave the rest but that doesn't make much sense.

So instead of using plugins for transpiling each one separately, babel used Presets which is a bunch of plugins bundled together.

Transpiling

Converting the syntax of ES6 to ES5 in called transpiling. For example, converting arrow functions to regular functions, converting [...] into Array.concat and so on.

But what about new ES6 features like Promises and stuff. They don't have an ES5 equivalent syntax.

Polyfilling

Well that's when the polyfilling comes into rescue. The Promises and other modern features are converted not only syntactically but in a browser understandable way is called polyfilling.

Babel used to do polyfilling out of the box earlier but now they recommend other library for polyfilling. That is core-js package.

For polyfilling async functions we need another package called regenerator-runtime

Best practices, Functional (Declarative) and Imperative Programming

How to do(Imperative) vs What to do (Declarative)
Definitions

Immutability is the best practice where we don't mutate the original array or object but instead copy them into a new one and then work with that. But how now to modify an object?

Using Object.freeze()We can't modify its values.

object.freeze() doesn't let the value to be changed

Limitation of Object.freeze()

Object.freeze() limitation

Vite

Why did we need a bundler such as Parcel or Webpack?

Browsers didn't support modules so we had to convert our code written in modules to browser understandable code using a bundler.

This might slow down the performance and load time. How?

Let's say you have a thousand modules and the bundler will bundle everything before shown on the browser. If you try to edit a line of code and save it again, the bundler needs to re-bundle everything which is time-consuming. What if there was a way where, on saving it again after editing a line of code, only that part will be included in the pre-existing bundle. That's exactly what Vite does.

3. About JavaScript

History of JavaScript

history
Modern JS support in old browser through transpiling and babel

Is JS pass by value or pass by reference?

Objects and arrays pass by reference, everything else, pass by value

Objects and arrays pass by reference, everything else pass by value

4. Debouncing

Debouncing is a technique used in JS to reduce the number of function calls or API calls in order to improve performance (performance-optimization). Let's consider a few scenarios.

In a search box like the Amazon website, when you search for a product, notice that it won't suggest you the items present for every key you type. Suggestions appear only when you pause or stop typing. This is important because you avoid making unnecessary calls to API to fetch products matching the current keyword search.

Scroll

Consider a scroll behavior where you console log something at every point you scroll. This is not efficient. You might want to log only when you stop or pause scrolling.

circle-info

This type of behavior can be achieved through debouncing.

To get to know about Debouncing, we need to know about setTimeout and clearTimeout. SetTimeout takes two parameters, a callback function and a timer in milliseconds. The callback gets executed when the timer expires. Also, the setTimeOut returns a number generally called timerID. This can be used to clear the timer and its callback function later before/after the timer has expired using clearTimeOut(). clearTimeOut(timerID) accepts timerID which was returned by setTimeout. Note that, the same timerID must be passed to clearTimeOut in order to clear out that particular setTimeOut().

How do we achieve debouncing with this? Our motive is to log, only when we stop pressing the button for 2000 milliseconds (2 sec).

triangle-exclamation

We can achieve this when we clear the previous timeout every time and call new setTimeOut. The new setTimeOut will execute only when we stop, meaning it will execute only when we don't clear it anymore when stop clicking it as shown below.

This is actually debouncing. But there is a problem here. We have defined, timerID as a global variable. Generally, we need a debounce function as a higher-order function that we can use for different purposes like scroll or button clicks and so on. Closure makes it possible.

How can we now generalize debounce HOC for scroll and click events?

We have one problem though. In debounce function, the context (this) is different in return function from fn

circle-check

5. Throttling

Throttling is another technique similar to debouncing used in JS to reduce the number of function calls or API calls in order to improve performance (performance-optimization). Let's consider a few scenarios.

Shooting game

Let's suppose you are playing a shooting game and you got a machine gun. Machine gun needs some time to load the bullet and can't shoot again immediately after shooting once. It takes some time to shoot again even though you continuosly press the trigger or button. This delay from one shoot to next shoot is called throttling.

Google Map resize

Consider you're using a map and then you zoom in the window to see where you are and zoom out again. Zooming from point A to point B can make some hundreds of API calls to get the location at every intermediate point which is not necessary. So we can implement throttling here as well so that once the map is resized it takes some time to get the location and not immediately.

circle-info

Debouncing vs Throttling

Debouncing - Executing / calling some function or API only when the action is paused

Throttling - Executing / calling some function or API only in certain intervals of time where there will be specified time gap between consecutive function calls

The implementation is similar to debouncing but with little implementatiion changes, so please first refer debouncing to understand how setTimeout works and why we call a function that returns another function.

Let's now implement throttling for a shooting game (button click).

Simple btn-click without throttling

Now we introduce a function that limits the shooting and this function is the core of throttling behavior

circle-exclamation

Generalizing this function by passing the parameters so that we can use throttle hoc for some other implementations

Passing the context from throttle hoc to fn

Throttling function

6. Numbers, Dates, and so on

JS is a base 2 system. So 0.1+0.2 is 0.30000000000000004

circle-exclamation

Convert String to Number

  1. 1st way -> Number('23')

  2. 2nd way -> +'23' -> Type coersion happens here

Not a number (NaN)

NaN -> Not a number

Number.isNaN(x) gives the result which is unpredictable. To predict that first put x into Number(x) and then you put that inside isNaN(x)

https://www.youtube.com/watch?v=GW6Qiblc5JQ&ab_channel=SteveGriffith

Check if something is a Number or not (better than isNan())

Use isFinite() to check if it's number or not (even strings that look like numbers can be tested

Use isInteger() to check if the number is a whole num of not

circle-check

Read a integer out of a string

Read a float out of a string

Rounding integers

4 ways to round the numbers - round, floor,ceil,trunc

Rounding decimals

toFixed() rounds the decimal (above 5 will round to the next number) and returns a string. We can add + to convert to a number or by Number(...)

7. DOM

Once you are familiar with theoritical concepts, you can do these excersises mentioned below

Excercises - Do once you study theory starting from DOM Nodes

circle-check
circle-check
circle-check
circle-check
circle-check
circle-check

Theory

Document Object Modal

DOM is an interface bw JS and Browser
  • DOM is an API between Browser and JS. JS can also work independently of DOM. (NODE JS)

  • DOM is a tree like structure formed by HTML elements. DOM is an Object Modal of HTML

  • In the DOM there are different types of nodes. Example, some nodes are html elements while others are just text while others are just comments

What is Node?

  • Every single thing in the DOM (visualise as HTML Page) is of type Node.

  • Like everything else in JS, DOM node is represented by an Object.

  • This Node object gets access to methods and props such as

    • textContent -> Property

    • childNodes -> Property

    • parentNode -> Property

    • cloneNode() -> Method

  • The Node is classified into different types

    • Element -> has props like innerHTML, classList and so on (see below image)

    • Text

    • Comment

    • Document

  • The Element can be <p>, <h1>, <img> <a> and so on.

  • The Text can be the text inisde Element

  • The Comment is HTML comment

  • The Document contains methods like querySelector, createlement and so on to help other Nodes like Element, Text and so on.

  • The Element is again classified as different HTML elements like HTMLButtonElement for button, HTMLDivElement for div and so on. This is necessary because each HTML Element has different attributes such as src which is present only on the img and nothing else. Also, href present only on anchor tag.

  • EventTarget is a special Node type which is a parent of both Node and Window. EventTarget is the one that provides addEventListener() and removeEventListener() methods on each Node. Note that we never create EventTarget object manually. This all happens behind the scenes.

  • Window object is the global object having lots of methods and properties, lot are unrelated to DOM.

  • Children (Lower level elements) can inherit methods and properties from higher ups because of which all of these are possible.

DOM Node classified

Selecting document elements

  • Document (html) can be selected by document.documentElement

  • body can be selected by document.body

  • head can be selected by document.head

We can attach event liseners on these 3 directly without using querySelector. But attaching event listener to body makes sense and not other two.

selecting body

Selecting elements

Add to / remove from DOM (2 ways)

  1. .insertAdjacentHTML() method -> We write the HTML string and then pass it as a second param. 1st param will be the position to insert this string.

  2. document.createElement() method -> We get an object back and can set properties to it

1. insertAdjacentHTML(param1,param2) -

position (param1) will be one of the following

  • 'beforebegin': Before the element itself.

  • 'afterbegin': Just inside the element, before its first child.

  • 'beforeend': Just inside the element, after its last child.

  • 'afterend': After the element itself.

Example 1

Example 2

insertAdjacentHTML

2. document.createElement(htmlElement)

now we can set the properties on this message like classList and so on. But how do we add this message to the DOM and where to add it?

Different places where we can put html elements in the DOM

insertAdjacentElement()

document.createElement()

beforebegin

before

afterbegin

prepend

beforeend

append

afterend

after

To insert multiple copies of same element

cloning an element

Remove a DOM element

remove() only removes the original message and not the clone of the message.

triangle-exclamation

the old way of deleting an element from dom. This is done using dom traversal. First, we select the parent of the element to be deleted and then delete the element

Dom traversal removing - Old way of removing
Ways to remove elements

Get styles of a DOM element

We can set the style like this-> message.style.color = 'red'. This will be set as inline style for this message div. We can then select the inline style by using same message.style.color shown above.

But this will select only inline style color. If color is set in the CSS file then that will not get selected. To select that we use getComputedStyle(message). This is an object that has all the CSS styles written in style sheets (If not written in the style sheet, then also we get it becasue browser will assign default styles to that element and we get that). To get the height, for example, we can do getComputedStyle(message).height

Working with CSS Custom properties

The styles/variables defined inside the root so that they can be used later are called custom properties of CSS. Changing these values will change all the CSS where these are used

CSS Custom properties

To select the entire document in JavaScript we use document.doucmentElement

console.log(document.doucmentElement) // will give us entire html document

With this, we can select CSS root element's style and change like this

HTML Attributes - Get and Set Attributes

Anything like src, alt, class, id etc defined inside the HTML element (example <img> ) are attributes.

Even though designer is defined in HTML, it still gives undefined. If we define designer in HTML as an attribute and want to access it in JS then we can use logo.getAttribute('designer')

Classes add and remove

Data Attributes

We can set an attribute that starts with data-. This can be useful in defining prop

Implement Smooth scroll

Smooth scrolling means, scrolling automatically to a particular position itself, maybe on a button click or some other event.

getBoundingClientRect() method is used to get the properties of an element like the exact position of that element in the browser

getBoundingClientRect

Get scroll position of the page

window.pageXOffset and window.pageYOffset gives the current scroll position of the page. If not scrolled then it will be 0 and 0

Get the width and height of the viewport

After learning these, now you can understand how to Scroll to a particular section

Smooth scroll

Summary of getting element postion on page

How to get

Here's how to get

Get the entire document

document.documentElement

Get the document body

document.body (NOT document.documentElement.body)

Get the document head

document.head

Get pre-defined attributes

document.qs(img).src (This is an example)

Get user-defined attributes

document.qs(img).getAttribute('design') (pre-defined can also be got this way. document.qs(img).getAttribute('src') )

Get data attributes

document.qs(img).dataset.versionNumber

Get position (co-ordinates) of an element on the page

document.qs(img).getBoundingClientRect()

Get scroll position

window.pageXOffset, window.pageYOffset

Get height and width of the view-port

document.documentElement.clientHeight document.documentElement.clientWidth

Scroll to a particular section (OLD WAY)

window.scrollTo(section.getBoundingClientRect().left + window.pageXOffset, section.getBoundingClientRect().top + window.pageYOffset)

Smooth scroll (OLD WAY)

window.scrollTo({left : same left above, top: same top above, behavior : smooth})

Scroll to a particular section (MODERN WAY)

section.scrollIntoView()

Smooth scroll (MODERN WAY)

section.scrollIntoView({behavior:smooth})

Adding/Removing Event Listeners

Two ways to add an event listener.

  1. addEventListener()

  2. onEventName = function() -- examples: onmouseenter, onclick and so on.

2. onEventNameis the old school method and is not used much. The most used is addEventListener and the reason is, in theaddEL, we can attach multiple events one after the other and the second callback will be different from the first one so both of them get executed one after the other, whereas, in onEventName = function, we can't do that. The second one overwrites the first one.

Also, we can remove the eventListenerwhen we implement 1st method.

Removing Event Listener ( It can't be done in 2 - onmouseenter one)

Event-Propagation (Capturing and Bubbling)

3 phases exist for most of the events.

  1. Capturing phase

  2. Target phase

  3. Bubbling phase

When an event happens like 'click', the capture phase begins. From document to the child element the event propagates before reaching the target (child where the event happens like 'click').

1. Capture Phase

The target phase happens when the event reaches the child at which click happens. Here the event can be handled, meaning the event listener code attached to that child gets executed.

2. Target phase

After the event reaches and executed in the target phase, the bubbling phase begins. Bubbling means, that event will now pass from the target (child at which the event happened) to the top-level parent through all the parents not reaching the siblings of course.

As the event bubbles up, the event is handled by every parent also. This means that if we attach an event listener to the section element (which is one of the parents of the target child), then that event at the section also gets executed as shown in the image below.

We would handle the event at each and every parent in the bubbling phase.

3. Bubbling phase

By default, the event could only be handled in the target phase and the bubbling phase. But we can set up the event handlers to handle events in capturing phase instead. For this, we set the third param to trueinside addEventListener() which is false by default.

This entire process is called event propagation, as events are propagating from one place to another.

circle-info

With the event bubbling, we could implement really powerful patterns

Not all types of events will have the capturing and bubbling phase. Some events are created at the target itself and handled there

Example of Event-bubbling

Bubbling

Target

event.target

Current Target

event.currentTarget

Stopping the event propagation

In practice, it might not be a good idea to stop the event propagation but it might be a good idea sometimes depending on the use case but it's really rare.

stopping the event propagation
circle-info

Event bubbling is very useful for event delegation (next topic) but capturing phase might not be that useful. But still, we can handle events in capturing phase by setting the third param to true

Event handling in capturing phase

capturing phase event handling instead of bubbling phase

Event Delegation

Event delegation is possible because of event bubbling. Instead of targeting the particular nav__link, we target the common parent (common to all nav__link elements) and then we can attach a single event listener to the common parent which is nav__links, and then use target (clicked nav__link) to select the one clicked and apply the functionality on to that.

Without event delegation

Necessity for Event delegation

With event delegation

Event Delegation

DOM traversing

Walking through the DOM is called DOM traversing. Meaning we can select an element based on another element. We have parents, children, and siblings on which we can select.

Going downwards - Selecting Children

1st way of selecting children is by using querySelector and querySelectorAll on the element (not on document). It will select children - deeper children also get selected

element queryselector to select children - deeper level possible

If there are other elements with highlight class, they wont be selected. Only the children of h1 having this class gets selected

To get the direct children we use .children

get direct children

To get the first and the last child

first and last child

Going upwards - Selecting Parents

To select the immediate parent

Immediate parent

To select the parent which is far away (multiple levels higher)

Selecting not immedidate parent
selecting same element with closest

Going sideways - Selecting Siblings

In Js we can only select direct siblings (previous and next sibling), but if we want all siblings then the trick is to point to direct parent and get all direct children except the child we are on.

selecting siblings

Summary of dom traversing methods

circle-info

Children Selectors

  • element.querySelector/All('.someclass') // Selects one or all children having someclass

  • element.children // Direct children

  • element.childNodes // Direct child nodes - not used much

  • element.firstElementChild // first child

  • element.lastElementChild // last child

Parent Selectors

  • element.parentElement // get direct/immediate parent

  • element.parentNode // get direct/immediate parent node - not used much

  • element.closest('.someclass') // get parent which is multiple levels up having someclass

  • element.closest('element') // get that element itself

Sibling Selectors

  • element.previousElementSibling // get the previous sibling element

  • element.nextElementSibling // get the previous sibling element

  • element.previousSibling // get the previous sibling node - not used much

  • element.nextSibling // get the next sibling node - not used much

  • element.parentElement.children // get all children and then if loop to unselect the element to get all siblings

Build tabbed component by referring to udemy 191. Building a Tabbed Component

Build Hover handler by referring to udemy 192. Passing Arguments to event handler

circle-info

The biggest take away from Udemy 192 is -

  • We cannot pass multiple arguments into an event handler function. It can have only one argument which is the event by default. If we need to have a second param, then we. need to use the arrow function to call this event handler function.

  • The default argument that is passed (we don't need to pass as a parameter) into the event handler function is "event" argument. That is present by default

  • The only argument we can manually pass is 'this' keyword by using bind method

  • If we really want to pass multiple arguments then use the arrow function inside addEL. In this case, we need to manually pass event as shown in the image below

passing event and other params inside handler using arrow function

TODO Implementing Sticky Navigation

With window scroll

With Intersection Observer API

circle-info

Note: To select the src (img) we do document.querySelector('#id').src and not

document.querySelector('#id').style.src

The scroll event is on window and not document.

window.addEventListener('scroll',function(){

console.log('Fired each time you scroll') // we don't need event

})

Lazy loading of images - Udemy 196. Lazy loading of images

Implement Slider - Udemy 197. and 198. Building a slider component Part 1 and 2

Webpage lifecycle DOM Events

3 different DOM events that occur in a webpage's lifecycle - right from the page is accessed until the page is closed by the user

1. DOMContentLoaded Event - (Fired on document object)

This event is loaded by the DOM as soon as the HTML is completely parsed. HMTL parsing means downloading the HTML and converting it to the DOM tree.

Also, all scripts must be downloaded and executed before this event happens.

This doesn't wait for images and other resources to load. Just HTML and JS need to be loaded.

DOMContentLoaded

"We want all our JS code to be executed only after the HTML is downloaded and available. Does that mean we need to wrap our entire JS code with this event?

Actually not required because our javascript is imported into HTML at the end of the HTML file inside script tag. By the time we import the script, we would have downloaded and parsed the HTML and have the DOMContentLoaded fired-of.

There are other ways of loading javascript with the script tag but more in the next section about it.

2. Load Event - (Fired on window object)

The load event is fired by the window as soon as not only after HTML is parsed but also after images, CSS and other resources are loaded. Meaning when the complete page is loaded this event is fired.

Window load event

3. Before Unload Event - (Fired on window object)

Created immediately before the user is about to leave the page. Useful in the case when the user has forgotten to save his data. It gives a prompt (warning) if we try to leave.

window beforeunload event

Async vs Defer script loading (Async and Defer is HTML5 feature and not the JS feature)

The biggest point to remember is that the DOMContentLoaded will only fire-of after HTML is completely parsed. But, DCL might be loaded before JS is executed or after JS is executed depending on the situation.

regular vs async vs defer at beginning and end of html pages
Async vs Defer vs normal
triangle-exclamation
circle-check

Useful article about async and defer

DOMContentLoaded event
circle-info

Note: To submit a form on enter button, you can do two ways:

  1. Use event listener on form and then listen for keydown and then if the key in the event is "Enter" then it means it's submit

  2. The more elegant way is, the form which is document.qs('form') will have an event called submit. Make use of that as shown below

form submitting ways event listener
circle-info

Note: When select HTML element is changed, the 'change' event occurs on which we can listen to

change event occurs on select element
circle-info

Note: How to listen to URL changes? -> Using hashchange event

8. Local storage

Local storage stores key, value pairs. Both key and value must be a string. If you want to store an object as a value then convert the object to the string by doing JSON.stringify(myobject).

circle-info

Local storage is blocking. Meaning, it's synchronous. So we need to set only small data here. Otherwise, it slows down the browser.

circle-info

Object to string ----> JSON.stringify(object)

String to Object -----> JSON.parses(string)

There is one problem converting an object to string from local storage. That is the prototype chain. The prototype chain will be lost and does not work. The problem occurs when converting an object to string back to an object.

circle-info

How to reload a page?

location.reload().

9. Arrays

Useful array methods

Array Method

Description

Returns new / modify the same array

push(ele)

Add to the end

Modifies the same - returns length of arr

unshift(ele)

Adds to the beginning

Modifies the same - returns length of arr

pop()

Removes at the end

Modifies the same - returns removed element

shift()

Removes at the start

Modifies the same - returns removed element

indexOf(ele)

Gives the index of first element

we pass. -1 if element

not present

Doesn't modify

includes(ele)

Gives true if present else false

Doesn't modify

slice(begin)

Starts on begin index and gives until end

Gives new array. Doesn't modify original

slice(begin,end)

Starts on begin index and gives all the way until but not including end

Gives new array. Doesn't modify original

slice(-2)

Gives last two elements

Gives new array. Doesn't modify original

slice(0)

gives back all elements. Same as slice()

Gives new array. Doesn't modify original

splice(begin)

starts on begin index and gives until the end. Imagine taking original array elements and giving it in an array

Changes the original array

splice() is generally used for deleting elements and inserting elements of original array

splice(begin) works similar to slice(begin) except it changes the original array

splice(begin,deleteCount)

From begin, it deletes "deleteCount" number of elements

Changes the original array

splice(begin,deleteCount,insert)

From begin, it deletes "deleteCount" number of elements and inserts element or elements at insert. We can have multiple values inserted as shown in the next one below.

Changes the original array

splice(begin,deleteCount,insert1,insert2)

Changes the original array

splice(4,0,"a","b")

From position 4, it deletes 0 elements and inserts a and b. So a,b will be inserted at 4th and 5th index

Changes the original array

splice(0, 0, "x", "m")

inserts two elements at the beginning

Changes the original array

reverse()

reverses the original array

Changes the original array

arr.concat(arr2)

gives same as [...arr,...arr2]

Gives new array. Doesn't modify original

split(splitter)

splits the string on splitter

gives array. Doesn't modify original

join(joiner)

joins all array elements into a string by joiner

Gives new array. Doesn't modify original

[1,2,3,4].join('/')

1/2/3/4

[1,2,3,4].join('')

1234

[1,2,3,4].join()

1,2,3,4

same as join(',')

find(callback)

arr.find(el => el <0)

gives the first negative element. Same like filter but gives one single element unlike filter

findIndex(callback)

same as find but returns index instead of element

Let's say you want to delete an element using it's index and you define certain condition to identify that element, then this method is useful.

some(callback)

includes(something) is used to check if something which is an element exists. If we want to check if an element with certain conditions exist then we use some(). arr.some(ele=>ele>100)

Doesnt mutate original array. Instead returns a single element that matches the callback condition. THIS RETURNS true or false

every(callback)

Same as some() but only returns true if all of the elements match the callback condition

THIS RETURNS true or false

flat()

flattens the array one level deep. flat() is same as flat(1)

Gives new array. Doesn't modify original

flat(depthLevel)

Let's say we have array inside an array inside an. array (nested arrays) then we can pass a number -> flat(3) to unflatten the array

Gives new array. Doesn't modify original

flatMap(depthLevel)

When we chain methods, we end up using map().flat(). flatMap() combines these two. Please refer EXAMPLE BELOW.

sort()

sorts the array in alphabetical order. Works on strings. Even numbers are treated as strings.

Changes the original array

sort(callback)

sorts the array based on callback. If callback returns >0 then the elements gets sorted in ascending order. If a<b then array gets sorted in descending order. If returns 0 then it remains as is.

Changes the original array.

EXAMPLE BELOW

fill(element,start,endButNotIncluded)

Replaces elements from start to end but not including end with the element specified

Changes the original array.

Fill is the only method works on array created with new Array(length)

from({},())

Used to create arrays. EXAMPLE BELOW

Creates new array

Flat and FlatMap example

flatMap

Sort example

Array destructuring

For Each loop vs for of loop

forEach vs for of

For each on maps

for each for maps

for each on set

for of and for each for set

Map, Filter, and Reduce

Reduce

find method

Just like the filter method but gives the single element, unlike the filter which gives the array.

fill method

This is the only method that fills the empty array which is created with new Array(length)

Ways to create Array

Array.from()

Another way of creating a larger array holding continuos elements

Ways to check if it's an array or not

Summary Of Array Methods

Array methods categories
triangle-exclamation

10. Objects

Objects are Key-value pairs

Two ways to access the values - Dot notation and the Bracket notation

Dot and Bracket notation

In bracket notation, we can compute values and then get it from object whereas in dot notation we can just get the values directly from object without computing.

Bracket notation - computed

We get undefined when we try to access the property that's not present in the object.

Methods

Useful Object methods

Object method

Description

Object destructuring

Method Borrowing

We can borrow methods from one object to another to avoid re-writing it

11. Modern Operators

Spread and Rest operators (Expand and Compress)

Spread operator - ON RIGHT-HAND SIDE OF =

Used to unpack the array, string and object elements

Rest pattern/rest operator - ON LEFT-HAND SIDE OF =

Looks exactly like spread operator but works in an opposite way

Spread - Unpacks or spreads elements

Rest - Packs or collects the elements into array.

Rest is used with destructuring generally and must be the placed at last of destructuring

circle-info

There can be many spread but one single rest

Short circuit operator (&& and ||)

||

&& (opposite to ||)

Nullish coalescing operator (??)

Optional chaining (?.)

Used to check if a property on an object exists or not

for-of loop

continue and break can be used unlike forEach loop

Works for Iterables - array, string, maps, sets

We can use it on objects as well in an indirect way

For objects, we can use Object.keys(object) This gives an array of all keys. To get values we can do Object.values(object). This gives values.

To loop over the keys and values of an object we can make use of Object.entries(object)

circle-info

Note the difference of entries() method between arrays and objects

for-in loop (old method- use for -of now)

Works for objects - maps over keys of objects

12. Sets

  • Set is used to return an object containing unique elements

  • Only iterables can be passed into set. We can pass array, string, map, set but not object

The main use-case of a set is to eliminate repeated values in an array and give back only the unique elements

The other use-case might be to check how many unique letters are there in a string

13. Maps

Key-value pair like object but one of the differences is, the map can have keys which are functions, strings, maps and so on.

Building a simple quiz app using Map

14 Summary of JS datastructures and when to use them

Array vs Set and OObject Vs Map

15. Strings in Javascript

Strings are primitives, so how does it have methods? Only objects contain methods right?

That's right. Behind the scenes, the string primitive gets converted into string object (this process of converting primitive to object is called boxing and the reverse in unboxing). On that string object we have all these methods.

circle-info

const str = 'sandeep'

gets converted by JS (boxing) to new String(str)

Some more string methods 🀨

Some more string methods pleaseπŸ€”

circle-info

List of string methods we went through

  • indexOf(str)

  • lastIndexOf(str)

  • slice(startIndex) // includes startIndex

  • slice(startIndex,endIndex) // doesnt include end

  • slice(-index) // from last

  • toUpperCase()

  • toLowerCase()

  • includes()

  • startsWith()

  • endsWith()

  • trim()

  • trimStart()

  • trimEnd()

  • replace(element, replaceWithElement) // replaces first occurance

  • replaceAll(element, replaceWithElement) // replaces all occurances

  • split(char) // converts into array seperated when char is found

  • join(char) // convers into string and seperates arr elements by char

  • padStart(count,char)

  • padEnd(count,char)

  • concat(str1,str2...) // similar to + of strings

circle-exclamation

16. Regular expressions in JS 😎

17. Functions

Default parameters

Default parameters

value vs reference types inside a function

First-class functions/citizens and Higher-Order functions

HOC

Call-back functions

The functions passed as values and are called at a later stage by another function are called callback functions.

higher order function and call-back function

Let's take another example for call-back functions

We know that callback is the function calling another function. Also, we can say, two functions are dependant on each other.

Let's say we have ice-cream shop. To produce the ice-cream, we need to first order the ice-cream. So to production depends on order

PRODUCTION --> Depends on --> ORDER

Call

Call
Call with arguments

Apply

Exactly like call method but the second parameter onwards we pass array instead of strings. That is the only difference.

Apply method

Bind

Similar to call method but, in call method, it directly calls/invokes the method by passing this keyword as first argument. In bind, the function is not called but called-for-later when invoked again. Meaning it returns a function after binding the parameters, so that it can be called at later point in time.

Call, apply and bind have this as the first parameter.

bind

Polyfill bind method

Polyfilling means supporting new features in old browsers. So converting new code/features to the code where old browsers can understand. This conversion is called polyfilling. Now, in the interviews you may be asked to polyfill bind, meaning to implement your own bind method where old browsers can understand.

A must watch video for polyfill bind

Immediately invoked functions (IIFE)

The functions that are invoked immediately are IIFEs.

Normal function vs IIFE

Why do we need IIFE

To make the variables private. In the below example, we see that isPrivate can be accessed only inside the IIFEE but not outside.

In modern JS, we use a block {} instead of IIFE and we can use block because of let and const. let and const are block-scoped whereas var is not. So var cannot be used inside a block.

When to use IIFE?

  1. When you need to avoid polluting global scope and have the code run only once

  2. When you want to use async, await feature

Closures

A function bundled with its lexical environment is called a closure.

Closure = Function + Lexical environment

A function when returned from another function, even though the outer function's execution context is no longer in the call stack, the returned function still has access to all the variables and functions that existed inside the function before the outer function was removed from call-stack. This is held in a closure.

Using console.dir() we can see closure in console. It is present in [[scopes]]. [[ represents internal property that we.can't access. So we can't access scopes and closure.

see closure in console
circle-info

A closure is also called a Stateful function because it's the function returned from another function that remembers the state of itself when it was originally present in the function before returning,

Advantages of closures

1. Closures can be used for function currying

Currying can be done in two ways

  • Using bind

  • Using closures

function currying

2. Closure can be used for data hiding and encapsulation

3. Closure can be used for Function once ( calling a function only once )

4. Closure can be used for memoization (to keep the time consuming computation in memory and returning it)

Disadvantages of closures

Overconsumption of memory because the closed-over variables are not garbage collected. This might lead to memory leaks and might freeze the browser if not handled properly.

What is a garbage collector?

A program in JS engine that removes or frees up space for unused variables.

Some of the modern browsers like v8 in chrome have smart garbage collection mechanisms. In closed-over-environment, it identifies which variables are not being used by the function which is been returned and has them garbage collected.

circle-info

Watch crazy Javascript Interview video above and practice the advantages and disadvantages of closures

18 . Async Javascript

What is callback hell, & promises? Resolve CB Hell with promise chaining (.then) and async await. All this you will learn by building an ice-cream shop project.

Reference - https://www.youtube.com/redirect?event=video_description&redir_token=QUFFLUhqa3RydmFHTXdjTnVBRjlsYi1STDBzc2s2T0J0UXxBQ3Jtc0tsdDMydDFZNnVSTkJiajZyY1c4SjdHWERzdFh0VV9YVFpwby1xeTZvU0RubGY0T1NTdkRXT0hLeGowc3U4RVlkRmVxcTVsSWFvZHNjbjhuMDBVSjFVdmFCdlVVMUhoSV80SWF4c0cwYkxmOV9vMGQ3QQ&q=https%3A%2F%2Fwww.freecodecamp.org%2Fnews%2Fjavascript-async-await-tutorial-learn-callbacks-promises-async-await-by-making-icecream%2Farrow-up-right

Interview Questions on Promises and SetTimeout

Interview question

Last updated