10 Things Every JavaScript Developer Should Know
What to look for in a JavaScript Developer
JavaScript is one of the most popular and widely used programming languages.
When asked what JavaScript does 20 years ago, a JavaScript developer would simply say that it is used to script websites. Now he will say that he can use JavaScript for frontend development, backend development, mobile application development, desktop application development, game development, AR/VR development, and more.
A lot of frameworks and libraries are there for JavaScript. These frameworks and libraries are just a piece of cake if your Javascript fundamentals are strong and clear. A lot of concepts are a bit overwhelming for developers, but a good knowledge of these concepts will help you in the long run.
Frameworks and libraries come and go, but the fundamentals always remain the same. It’s easy to build any kind of application and learn to use any framework and library if the fundamentals are clear. Despite JavaScript's importance for web and mobile development, many hiring managers aren't aware of what they need in a JavaScript developer.
If you are an aspiring JavaScript developer, here are 10 important and must-know topics for you that will be handy.
Scope
Scope defines whether you can access or reference a particular value or expression. We are unable to use a declared variable if it is not included in the current scope. This idea is crucial to understand because it makes it easier to separate logic in your code and makes it easier to read.
In JavaScript, we have 3 types of scopes:
Global scope: Variables and expressions can be referred to anywhere in a global scope. This is the default scope.
Local scope: Variables and expressions can be referenced only within the boundary.
We declare variables with the let, const, and var keywords. If we declare variables outside of blocks or functions, they will always be global variables.
//global scope example
var nameis = "John"; //global scope
let age = 30; //global scope
const isMarried = false; //global scope
{
console.log(nameis, age, isMarried);
//some useful code
}
function someUsefullfunction() {
console.log(nameis, age, isMarried);
}
someUsefullfunction();
The var keyword declares a function-scoped or globally-scoped variable, optionally initializing it to a value.
//global scope example of var
var nameis = "John";
var age = 30;
var isMarried = false;
function printName() {
console.log(nameis, age, isMarried); //outputs John 30 false
}
printName()
//local scope example of var
function printName() {
var nameis = "John";
var age = 30;
var isMarried = false;
}
console.log(nameis)//this will show an error
var cannot have block scope, i.e., sections created with a pair of curly braces.
{
var x = 1;
}
console.log(x); // 1; x can be accessed here
The let and const keywords can belong to the block scope if they are inside a block or boundary. They cannot be accessed outside the block as a global variable.
{
const x = 1;
let y=x*x
}
console.log(y); // ReferenceError: y is not defined
Hoisting
When developers are unclear about the concept of hoisting in JavaScript, they frequently encounter unexpected outcomes. Before the execution of the code, the interpreter appears to move the declaration of functions, variables, or classes to the top of their scope. This process is known as JavaScript hoisting. Hoisting allows functions to be safely used in code before they are declared.
Hoisting lets you use a function before you declare it in your code.
FavoriteYoutuber("Mr. Beast");
function FavoriteYoutuber(name) {
console.log("My Favorite Youtuber is " + name);
}
You can also hoist your variables, but keep in mind that if you declare them with var, the default initialization of the var is undefined, whereas let and const are also hoisted but, unlike var, are not initialized with a default value.
console.log(num); // Returns 'undefined' from hoisted var declaration
var num = 6; // Initialization and declaration.
console.log(num); // Returns 6 after the line with initialization is executed.
console.log(numberofsix); // Throws ReferenceError exception as the variable value is uninitialized
let numberofsix = 6; // Initialization
Callbacks
You'll get much better at JavaScript once you comprehend how callback functions function, which is a crucial component of the language. A callback is a function that is passed as an argument to another function, and its execution is delayed until that function to which it is passed is executed.
//callback function with parameters and return value
function greeting(parameter) {
console.log(parameter);
}
function callBackFunction(callback) {
callback("Hello World");
console.log("I am in a callback function");
}
callBackFunction(greeting);
Here you can see the greeting function is passed as an argument to the callBackFunction, and it executes the greeting function and then the calling function.
Callback functions are beneficial for brief asynchronous operations with two to three nested callbacks or less, but not for longer operations. Imagine a callback having many nested callbacks inside of it; it would appear like this.
This issue is resolved and made easier for programmers by promises and async/await.
Promises
A promise is an object that has the potential to produce only one value in the future: either a resolved value or an explanation for why it cannot be resolved (such as a network error). There are three possible states for a promise: fulfilled, rejected, or pending.
Pending state: The initial stage in which the result is unknown because the operation has not yet been completed.
Fulfilled state: Promises that have been successfully carried out and have produced value are said to be in a fulfilled state.
Rejected state: The condition of a promise signifying a failed operation along with the cause of the failure.
Let's look at an example.
let promise = new Promise(function (resolve, reject) {
if (0 == 1) {
resolve("Heyyy") //This will not work 0!=1
}
else reject("0 is not equal to 1"); // This will work
});
promise
.then(function (data) {
console.log(data);
})
.catch(function (error) {
console.log(error);
});
Here, promises are demonstrated clearly. It promises to return something resolved or else rejected. In this situation, it is rejected because of 0!=1 and returns with a message “0 is not equal to 1”.
We use .then () and .catch() methods so that we can return the value of the called handler, i.e., resolve or reject it.
Async & Await
Asynchronous JavaScript has never been easy. We have used callbacks for a while. Then, we employed promises. We use asynchronous functions most of the time now.
Asynchronous programming is handy when it is required that a certain piece of code should be executed only after the code above it is successfully executed. To handle this scenario, JavaScript introduced async/await in ECMAScript 2017.
Asynchronous functions contain the async keyword. You can use it in a normal function declaration:
async function functionName (arguments) {
// asynchronously do something here
}
//Arrow functions also work fine with async
const functionName = async (arguments) => {
// Do something asynchronous
}
Let's look at an example where we need things to work asynchronously.
function shootVideo() {
setTimeout(() => {
console.log("Shooting video...");
},2000);
}
function editVideo(){
setTimeout(() => {
console.log("Editing video...");
},1000)
}
function uploadOnYoutube(){
console.log("Uploading video on youtube...");
}
function uploadVideo() {
shootVideo();
editVideo();
uploadOnYoutube();
}
uploadVideo()
Here the output will be:
This is not the intended way to upload a video. Now with the help of asynchronous JavaScript, let's see how things work.
function shootVideo() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(console.log("Shooting video..."));
}, 2000);
});
}
function editVideo() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(console.log("Editing video..."));
}, 1000);
});
}
function uploadOnYoutube() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(console.log("Uploading video on youtube..."));
}, 1000);
});
}
async function uploadVideo() {
await shootVideo();
await editVideo();
await uploadOnYoutube();
}
uploadVideo();
Now our output is as expected:
Closure
A closure is a feature in JavaScript where an inner function has access to the outer (enclosing) function’s variables.
The inner function can access the variables defined in its scope, the scope of its parent functions, or even its grandparent functions, and the global variables. But the outer function can not have access to the inner function variable (Scoping).
Let's now see a visual example of this:
//javascript closure grandparent example
function grandparent() {
let car="BMW"
var parent = function () {
let house="4BHK"
var child = function () {
return ("The child gets " + car + " and " + house);
};
return child;
};
return parent;
}
console.log( grandparent()()()) //The Child gets BMW and 4BHK house
In this case, the grandparent is unable to access anything that belongs to either the parent or the child, whereas the child can access it from both of them.
Immediately Invoked Function Expression
An Immediately Invoked Function Expression is a JavaScript function that runs as soon as it is defined.
IIFEs are incredibly helpful because they can be used to easily isolate variable declarations while not contaminating the global object. Therefore, the primary reason to use IIFE is to immediately execute the code and obtain data privacy.
(function(){
console.log('IIFE');
}
)();
The wrapping parentheses ( / function / )() are actually what make our function, internally, be considered as an expression.
Modular Programming
A module is a compact piece of independent, reusable code in JavaScript. Any non-trivial JavaScript-based application must be built using modules, which are the cornerstone of many JavaScript design patterns.
Using modules has the benefit of making it more maintainable, among other things. When a module is isolated from other pieces of code, updating that module is much simpler.
Reusability is another advantage of using modules, which can reduce code redundancy.
Let's make a module for finding out if a year is a leap or not:
IsLeapYear.js
//module for checking if a year is a leap
export default function isLeapYear(year) {
return (year % 4 == 0 && year % 100 != 0)
|| year % 400 == 0;
}
For determining whether a given year is a leap year or not, we now have a standalone module. Importing the file and calling the function are the only steps left to take.
//import is leap year
import isLeapYear from './isLeapYear.js';
isLeapYear(2000)
Memoization & Performance Enhancing
The programming technique called Memoization improves function performance by caching previously computed results. JavaScript objects make excellent candidates for caches because of how they behave like associative arrays.
Each time a memoized function is called, its parameters are used to index the cache.
If the data is present, then it can be returned without executing the entire function. However, if the data is not cached, then the function is executed, and the result is added to the cache.
Let's look at an example:
console.time("Fibonacci");
function Fibonacci(n) {
if (n < 2) {
return 1;
} else
return Fibonacci(n - 1) + Fibonacci(n - 2);
}
console.log(Fibonacci(40));
console.timeEnd("Fibonacci");
This simple program to find the 40th Fibonacci number took almost 2 seconds to complete.
The search for the 100th Fibonacci term would be extremely time-consuming if we clung to this method.
Now let's just include our memoization technique here:
console.time("Fibonacci");
function Fibonacci(n, memo = {}) {
if (n in the memo) {
return memo[n];
}
if (n < 2) {
return 1;
} else memo[n] = Fibonacci(n - 1, memo) + Fibonacci(n - 2, memo);
return memo[n];
}
console.log(Fibonacci(40));
console.timeEnd("Fibonacci");
Including a simple memo variable just increased the performance of the function 10x, from 2 seconds to 12 ms.
This time, our function ran in just 12.957 milliseconds. What happens is that a memoized function "remembers" the outputs associated with a particular set of inputs. The result is returned rather than being recalculated in subsequent calls with remembered inputs.
Error Handling
There is a possibility that while writing code you will reach a point where a bug prevents you from continuing, so you must be a problem solver here.
No matter if you are working on the front end or the back end, for 'handling' errors in the first year or so, you will probably use console.log or possibly console.error.
You need to change that if you want to write good applications and swap out your lazy logs for properly handled errors. You might want to look into creating your own Error constructor, learning how to catch errors properly, and displaying errors to users.
No matter how good a programmer we are, occasionally our scripts contain errors.
They might happen as a result of mistakes we make, resulting in unexpected user input, an incorrect server response, or for a myriad of other reasons. Typically, when a script encounters an error, it immediately terminates and prints the error to the console.
However, there is a syntax construct known as try...catch that enables us to catch errors and prevent the script from terminating prematurely.
try {
// code...
} catch (err) {
// error handling
}
Conclusion
It's a strange language, JavaScript. But when you look closer, you typically understand why things operate that way.
I sincerely hope that this list will help you understand some of the crucial JavaScript concepts that you should know, and if you are aware of any additional concepts that are noteworthy, please mention them in the comments section :)