JavaScript is widely used in web development to bring web pages to life. It’s not limited to web browsers anymore and can be used for server-side applications too.
This versatility makes it highly sought after in the tech industry, where there’s a huge demand for skilled JavaScript developers. Learning JavaScript can be a great way to kick-start your career in web development.
JavaScript tops the Stack Overflow Developer Survey 2019 as the #1 programming language, used by 95% of all websites. Regardless of company size, a good grasp of this language is essential for creating websites and apps.
Although various frameworks and libraries exist, it’s crucial to have a clear understanding of JavaScript fundamentals. A strong foundation in these concepts will benefit developers in the long run, as they remain constant even as frameworks and libraries evolve.
By mastering the basics, developers can easily learn new frameworks and libraries and perform well in interviews.
1. Scope
Scope refers to variable accessibility during code execution. By default, the root scope (i.e., the window scope) is always active.
Scope is like a box that contains variables, functions, and objects, setting boundaries that restrict variable access.
Understanding scope is crucial for separating code logic and improving readability, as it limits variable visibility to specific parts of the code.
Scope can be defined in two ways.
- Local scope grants access to all entities within its boundaries (inside the box).
- Global scope encompasses everything outside of the boundaries (i.e., outside the box). Variables defined within local scope cannot be accessed by the global scope, as they are enclosed from the outer world, unless they are returned.
Example: The code below will trigger an error since “name” is defined within the boundary (local scope) of the showName() function. Accessing this variable outside the function is not possible.
NOTE: The code below contains an error due to a typo in the function call, resulting in an error before the intended scoping error is raised by the console.log call.
function showName() {
var name = “Javascript is JS”;
}
showName()
console.log(name);
Observe the code below to see how you can access the “name” variable defined in the local scope.
function showName() {
var name = “Javascript is JS”;
}
showName();
Javascript is JS
Learn more about Software Development Services in SHIFT ASIA
2. IIFE (Immediately Invoked Function Expression)
An IIFE (Immediately Invoked Function Expression) is a JavaScript function that is executed immediately as soon as it is defined.
By declaring variables within the IIFE, they cannot be accessed by the outside world, thus avoiding polluting the global scope. The primary purpose of using an IIFE is to achieve immediate code execution and maintain data privacy.
let paintColor = ‘red’
const paint = (() => {
return {
changeColorToBlue: () => {
paintColor: ‘Blue’;
return paintColor;
},
changeColorToGreen: () => {
paintColor: ‘Green’;
return paintColor;
}
}
})();
console.log(paint.changerColorToBlue());
Learn more about What is Javascript
3. Hoisting
Many developers encounter unexpected results when they are unsure about the concept of hoisting in JavaScript. JavaScript allows you to call a function before it is defined, without throwing an error such as “Uncaught ReferenceError”.
This behavior is due to hoisting, whereby the JavaScript interpreter moves variable and function declarations to the top of the current scope (function or global scope) before code execution.
Example: Take a look at the code given below.
function cowSays(sound) {
console.log(sound);
}
cowSays(‘moo’);
moo
What occurs when we call our function before declaring it, utilizing hoisting?
cowSays(‘moo’);
function cowSays(sound) {
console.log(sound);
}
moo
The preceding code does not generate an error, and the output ‘moo’ is displayed in the console. This is an example of hoisting in JavaScript.
Example 2:
var a = 5;
console.log(5);
output: // 5
The preceding code, with hoisting, will produce the same output.
a = 5;
console.log(5);
var a;
output // 5
4. Closures
A closure is a function that is nested inside another function and has access to the outer function’s variables. While this definition may seem straightforward, the real power lies in the concept of scope.
The inner function (closure) can access variables defined in its own scope (between its curly brackets), in the scope of its parent function, and global variables.
It’s important to note that the outer function cannot access variables defined within the inner function (as we discussed in the scope concept earlier). Let’s take an example to better understand this concept.
Example:
const first = () => {
const greet = ‘Hi’;
const second = () => {
const name = ‘John’;
console.log(greet);
}
return second;
}
const newFunc = first();
In the preceding example, the inner function ‘second()’ is a closure. This closure can access the ‘greet’ variable, which is part of the scope of the outer function ‘first()’. It’s important to note that the parent scope cannot access the ‘name’ variable of the child scope.
Why do we need to learn about closures and what are their uses? Closures are utilized when you want to extend behavior by passing variables, methods, or arrays from an outer function to an inner function.
In the preceding example, ‘second()’ extends the behavior of the ‘first()’ function and has access to the ‘greet’ variable.
Although JavaScript is not a pure object-oriented language, closures can be used to achieve object-oriented behavior. In the above example, you can consider the const ‘newFunc’ as an object with properties such as ‘greet’ and a method ‘second()’, similar to an object in an object-oriented programming language.
It’s worth noting that once the ‘first()’ function is executed, variables within it will not be destroyed (even if there is a ‘return’ statement), due to closures. The scope is kept alive, allowing the child function to continue accessing the properties of the parent function.
To put it simply, closures can be defined as follows:
“A function runs and is executed. It will never run again, but it will remember that there are references to its variables, so the child scope will always have access to the parent scope.”
5. Callbacks
In JavaScript, a callback is a function that is passed as a parameter to another function and is invoked or executed within that function.
In this scenario, a function must wait for another function to execute or return a value, resulting in a chain of functionalities (when X is completed, Y is executed, and so on).
As a result, callbacks are commonly used in JavaScript’s asynchronous operations to provide the synchronous capability.
Example:
const greeting = (name) => {
console.log(‘Hello ‘ + name);
}
const processUserName = (callback) => {
name = ‘John’;
callback(name)’
}
processUserName(greeting);
In the example given above, it is worth noting that the greeting is passed as an argument (callback) to the ‘processUserName’ function. Before the ‘greeting’ function is executed, it must wait for the ‘processUserName’ event to execute first.
6. Promises
The concept of callback is well understood, but what happens when your code contains callbacks within callbacks within callbacks, and so on? This recursive structure of callbacks is called ‘callback hell,’ and Promises can help to solve this kind of issue.
Promises are useful in asynchronous JavaScript operations when we need to execute two or more back-to-back operations (or chaining callbacks), where each subsequent function starts when the previous one is completed.
A promise is an object that may produce a single value sometime in the future, either a resolved value or a reason why it’s not resolved (rejected). According to developer.mozilla,
“A Promise is an object representing the eventual completion or failure of an asynchronous operation. Essentially, a promise is a returned object to which you attach callbacks, instead of passing callbacks into a function.” – Developer.mozilla
Promises resolve the issue of ‘callback hell,’ which is nothing but a recursive structure of callbacks within callbacks within callbacks, and so forth.
A promise in JavaScript can be in one of three possible states:
- Pending: The initial state. The promise is neither fulfilled nor rejected.
- Fulfilled: The promise is successful and a result is available. This state is also known as resolved.
- Rejected: The promise has failed and a reason for the failure is available.
Example:
const promise = new Promise((resolve, reject) => {
isNameExist = true;
if(isNameExist) {
resolve(“User name exist”)
} else {
reject (“error”)
}
})
promise.then(result => console.log(result)).catch(() => {
console.log(‘error !’)
})
The code above shows an example of using a Promise object to perform an asynchronous operation, specifically an ‘isNameExist’ check. The Promise object takes two functions as arguments: resolve and reject.
If the operation is successful and ‘isNameExist’ is true, it will be resolved and output the message “User name exist”. Otherwise, the operation will fail or be rejected and the message “error!” will be displayed. Promises also allow for the chaining of operations, where the result of one operation is passed to the next operation in the chain.
7. Async & Await
In JavaScript, there are different ways to handle asynchronous operations. One of them is using Async/await, which is just syntactic sugar on top of Promises.
Async/await provides a more synchronous way of handling asynchronous operations. It allows you to stop and wait until something is resolved before moving on to the next operation.
To use Async/await, you need to mark your function as “async”. This allows you to use the “await” keyword inside that function. The “await” keyword is used to wait for a Promise to resolve before moving on to the next line of code.
Here’s an example of how to use Async/await:
async function fetchData() {
try { const response = await fetch(‘https://example.com/data’);
const data = await response.json();
console.log(data); }
catch (error) {
console.log(error); } }
In the above example, the “fetchData” function is marked as “async”. Inside the function, we use the “await” keyword to wait for the response of the fetch request.
Once we have the response, we can use the “await” keyword again to wait for the response to be converted to JSON format. Finally, we log the data to the console.
If there’s an error during the fetch request, the “catch” block will catch the error and log it to the console.
Async/await makes the code look more synchronous and easier to read, especially when dealing with complex asynchronous operations.
Conclusion
In conclusion, mastering these 7 concepts in JavaScript can significantly improve your ability to develop web applications. Understanding the concept of scope, hoisting, IIFE, closures, callbacks, promises, async & await is essential for any web developer to write efficient and effective code.
By leveraging these concepts, you can write code that is more maintainable, scalable, and optimized for performance. So, keep practicing and exploring these concepts to enhance your skills as a web developer.