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
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
Using + JS type coercion happens from Number
to String
Using - or any other operator except + JS type coercion happens from String
to Number
Example
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 ==
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.
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?
2. Modern Javascript development
Modules
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 usingnpm 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
andpolyfiling
. This will convert all ES6 syntax into old syntax where the browser can understand. Thetranspiling
/polyfiling
is done by a tool called BabelAfter 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
Review Udemy 268. Exporting and Importing in ES6 Modules for the above info
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
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
Importing in a common js 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?
The point is, we can't use common js modules without using a module bundler. Few packages have Es6 version as well as common js version. We can install like this
npm i lodash-es
//not common js moudle but es6
this does not require a module bundler
Note: Lodash is a popular package/module containing useful functions for arrays, objects, dates, and so on
Example of using loadsh-es6
Let's use lodash to deepClone an object.
Without lodash it will be really difficult to implement it on our own in JS. We will later talk about how to implement this ourself but now let's use lodash to do this task
Deep copy is difficult so let's use lodash to do that
Parcel - Module bundler
It's a build tool available on npm.
Note we have two type of dependency we get from npm
Normal dependency -
npm i module
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
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.
Limitation of Object.freeze()
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
Is JS pass by value or pass by reference?
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.
Search Box
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.
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).
The problem with the above code is that, when the button is clicked for the first time, it waits for 2 seconds before it prints. But the subsequent calls wouldn't wait so then it's called continuously as we press.
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
Now we have achieved debouncing and the full code is below
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.
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
Notice that the flag and betterShoot() are in global scope which is not what we want. We need to wrap up in a function and still need to get this functionality working
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
6. Numbers, Dates, and so on
JS is a base 2 system. So 0.1+0.2 is 0.30000000000000004
Be careful
if(0.1+0.2 === 0.3) -> false
Convert String to Number
1st way -> Number('23')
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)
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
3 Ways to check if number or not in JS
isNaN
isFinite
typeof somenumber === 'number' -> True means number else false
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
If you can do the Tabbed Components then you're good until DOM traversing concepts
Udemy 191. Building a Tabbed Component
Build Hover handler by referring to
Udemy 192. Passing Arguments to an event handler
Sticky navigation
Udemy 193. Implementing sticky navigation
Sticky Navigation in a better way using Intersection Observer API
Udemy 194: A Better Way: The Intersection Observer API
Performance optimization of images - Lazy loading
Udemy 196. Lazy loading of images
Implement Slider
Udemy 197. and 198. Building a slider component Part 1 and 2
Theory
Document Object Modal
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.
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 elements
Add to / remove from DOM (2 ways)
.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.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 theelement
itself.'afterbegin'
: Just inside theelement
, before its first child.'beforeend'
: Just inside theelement
, after its last child.'afterend'
: After theelement
itself.
Example 1
Example 2
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
Remove a DOM element
remove() only removes the original message and not the clone of the message.
Not sure how to remove the clone node at this point yet.
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
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
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
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
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.
addEventListener()
onEventName = function()
-- examples:onmouseenter
,onclick
and so on.
2. onEventName
is 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 eventListener
when we implement 1st method.
Removing Event Listener ( It can't be done in 2 - onmouseenter
one)
onmouseenter
one)Event-Propagation (Capturing and Bubbling)
3 phases exist for most of the events.
Capturing phase
Target phase
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').
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.
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.
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 true
inside addEventListener()
which is false by default.
This entire process is called event propagation, as events are propagating from one place to another.
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
Target
Current Target
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.
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
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
With 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
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
To get the first and the last child
Going upwards - Selecting Parents
To select the immediate parent
To select the parent which is far away (multiple levels higher)
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.
Summary of dom traversing methods
Children Selectors
element.querySelector/All('.someclass')
// Selects one or all children having someclasselement.children
// Direct childrenelement.childNodes
// Direct child nodes - not used muchelement.firstElementChild
// first childelement.lastElementChild
// last child
Parent Selectors
element.parentElement
// get direct/immediate parentelement.parentNode
// get direct/immediate parent node - not used muchelement.closest('.someclass')
// get parent which is multiple levels up having someclasselement.closest('element')
// get that element itself
Sibling Selectors
element.previousElementSibling
// get the previous sibling elementelement.nextElementSibling
// get the previous sibling elementelement.previousSibling
// get the previous sibling node - not used muchelement.nextSibling
// get the next sibling node - not used muchelement.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
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 defaultThe only argument we can manually pass is '
this
' keyword by usingbind
methodIf 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
TODO
Implementing Sticky Navigation
TODO
Implementing Sticky NavigationWith window scroll
With Intersection Observer API
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.
"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.
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.
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.
I have a little confusion in DOMContentLoaded. If script is first exectued and DCL is loaded next then how is that possible to get our JS code working? Need to dig deeper.
TODO: Understanding DCL a bit clearly
Found the answer to the above question.
See you have to understand one thing. DCL event is executed once HTML is completely parsed. That doesn't mean that, JS can't be executed before DCL.
JS scripts can be executed before or after DCL (it doesn't matter). DOM gives us a method called DCL (Dom Content Loaded) just to tell us that "HTML is completely paresed, now do whatever you want". JS can be executed even before HTML is parsed (i.e., even before DCL is fired)
Useful article about async and defer
Note: To submit a form on enter
button, you can do two ways:
Use event listener on form and then listen for
keydown
and then if the key in the event is "Enter" then it means it'ssubmit
The more elegant way is, the form which is
document.qs('form')
will have an event calledsubmit
. Make use of that as shown below
Note: When select
HTML element is changed, the 'change' event occurs on which we can listen to
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)
.
Local storage is blocking. Meaning, it's synchronous. So we need to set only small data here. Otherwise, it slows down the browser.
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.
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
Sort example
Array destructuring
For Each loop vs for of loop
For each on maps
for each on 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
A simple coding challenge I failed to solve which is straight forward.
const array = ["sandeep","vijay","naveen"]
// print -> sandeep and vijay and naveen are friends
I failed to do in simple way but used all unncessary methods. the simple way to go here with is
join()
method.
// So the solution is,
console.log(array.join(" and ") + " are friends")
10. Objects
Objects are Key-value pairs
Two ways to access the values - Dot notation and the 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.
We get undefined
when we try to access the property that's not present in the object.
Useful Object methods
Object method
Description
Object destructuring
Method Borrowing
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
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)
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
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.
const str = 'sandeep'
gets converted by JS (boxing) to new String(str)
Some more string methods 🤨
Some more string methods please🤔
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
Note that there is not reverse
method on string. You can convert into array by splitting and then reverse
it as array supports reverse() and then join
back to string
16. Regular expressions in JS 😎
17. Functions
Default parameters
value vs reference types inside a function
First-class functions/citizens and Higher-Order functions
Call-back functions
The functions passed as values and are called at a later stage by another function are called callback functions.
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
Apply
Exactly like call method but the second parameter onwards we pass array instead of strings. That is the only difference.
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.
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.
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?
When you need to avoid polluting global scope and have the code run only once
When you want to use async, await feature
Closures
A function bundled with its lexical environment is called a closure.
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.
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
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.
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.
Interview Questions on Promises and SetTimeout
Last updated