
Javascript has been a very popular language for a long time and is widely used around the community.
Here is a summary of some useful advanced concepts.
Let’s get started.
Stop the habit of wishful thinking and start the habit of thoughtful wishes with Justly.
Hoisting means to pull something up, javascript also does the same.
JavaScript has a mechanism that moves all declarations of variables and functions to the top of their scope before code execution.
You can use
varorfunctionbefore declaring it. It’s useful when you know requirements, but don’t know that how to implement it?
Hoisting performs differently for var, let, and const.
x = 5;
console.log(x); //outputs 5
var x;It runs successfully and logs 5 as output as a declaration of x moved on top of that block.
Also, another example of var
console.log(x); //outputs undefined
var x = 5;It logs undefined as we have initialized (assigned a value) x at the time of declaration, that’s after printing its value.
Hoisting performs the same in the case of let and const .
Consider the same example as var
// let
x = 5;
console.log(x); // ReferenceError: Cannot access 'x' before initialization
let x;
// const
console.log(x); // ReferenceError: Cannot access 'x' before initialization
const x = 5;let and const are not declared or initialized before output, it gives a Reference Error like the above code.
Hoisting can be used for functions too
myFunc("John");
function myFunc(name) {
console.log("My name is " + name); //My name is John
}We all are using var, let, and const to initialize variables in javascript commonly. Let’s know the difference between them.
Before the ES6 module, var was used everywhere to declare variables. But due to some cons of var , Javascript has defined another way that is let . And const are used to maintain constant values.
We can differentiate them in the following manners.
var have global(If they have declared globally) or functional(if they have declared inside functions) scope.var x = "hello from x"; //globally scoped
function newFunction() {
var y = "hello from y"; //scoped in function
}let is block-scoped. Block means anything that resides in curly braces {} . It can be function or if condition or switch case.let x = "hello from x";
let count = 3;
if (count > 2) {
let x = "hello from x inside block";
console.log(x);// "hello from x inside block"
}
console.log(x) // x is not definedconst is block-scoped same as let .We can define const as belowconst x = "hello from x";var can be updated or redeclared like belowvar x = "hello from x";
x = "hello from x again";
// or
var x = "hello from x";
var x = "hello from x again";let can be updated but not redeclaredlet x = "hello from x";
x = "hello from x again";
// but not
let x = "hello from x";
let x = "hello from x again"; // error: Identifier 'x' has already been declaredconst can not be updated or declaredconst X= "HELLO FROM X"
X = "HELLO AGAIN" //error: Assignment to constant variable.
// and
const X = "HELLO FROM X";
const X = "HELLO FROM X AGAIN"; // error: Identifier 'X' has already been declaredlet instead of varConsider the following example using var
var x = "hello from x";
var count = 3;
if (count > 2) {
var x = "hello from x inside condition";
}
console.log(x) // "hello from x inside condition"In the above example, if you intentionally want to change the value of x , then there will be no issue with the code.
It becomes a problem when you are working with large code, and don’t know that var x has been already declared, it will cause lots of bugs in code.
here is the same example using let ,
let x = "hello from x";
let count = 3;
if (count > 2) {
let x = "hello from x inside condition";
}
console.log(x) // "hello from x"Since let is block-scoped, both variables are treated as different variables. For this reason let is a better choice than var if variables using let are not declared more than one time in the block.
In a simple javascript function, we can add any number of parameters in the function definition if we know them.
function myFunc(var1, var2)But if we don’t know the number of parameters for the functions, then we can use the rest parameters. They can be written by prefixing … like below,
function sum(...args) {
let sum = 0;
for (let arg of args) {
sum += arg;
}
return sum;
}
// can pass any number of arguments
sum(1,2,3); //6
sum(1,2,3,4,5,6); //21
sum(1,2,3,4,5,6,7,8,9,10); //55We know that variables are only accessible within scopes, but what if we want block-scoped variables outside that scope??
Closures are functions that are defined inside other functions and execute locally scoped variables and allow them to access outside of scope.
function myFunc() {
let message = 'hello';
function insideMyFunc() {
console.log(message);
}
insideMyFunc();
}
myFunc();The output will be hello in that case.
function myFunc() {
let message = 'hello';
function insideMyFunc() {
console.log(message);
}
return insideMyFunc;
}
var myFunc1 = myFunc();
myFunc1();In this case, you can not execute myFunc() directly as it returns insideMyFunc . You have to assign it to another var that is myFunc1 and can execute myFunc1() later.
output: hellofunction myFunc(x) {
function insideMyFunc(y) {
console.log(x + " " + y);
}
return insideMyFunc;
}
var func1 = myFunc("Hi");
var func2 = myFunc("Hello");
func1("there");
func2("there");myFunc takes Hi as x and returns insideMyFunc to func1. func1 takes there as yand finally prints Hi there .
The output will look like this,
output of func1 : Hi there
output of func2 : Hello thereSymbols are used to define unique names even if they are the same.
let x = Symbol("id");
let y = Symbol("id");
console.log(x == y) //falseThey are new primitive types like Number, String, and Boolean serving object properties. It is used to create a totally unique identifier.
Suppose you are using a third-party library that returns some random user object, and you want to add unique id on that object, but that should not be accessible outside of the object, then you can use Symbol .
Here is an example of using a symbol and the way it can be accessible,
let user = { name: "lorem" };
let id = Symbol("id");
user[id] = 1;
console.log(JSON.stringify(user)); //{"name":"lorem"}
console.log(user); //{ name: 'lorem', [Symbol(id)]: 1 }
console.log(user[id]); // 1
console.log(user.id); // undefined
console.log(Object.getOwnPropertyNames(user)) //[ 'name' ]
for (let key in user){
console.log(key) // name
}You can explore more about symbols from this article.
Javascript defines two protocols iterables and iterators for iterations of data structure over for…of loop. Instead of for , for…of reduces the complexity of the indices when using nested loops.
Data structures like array, string and Sets have symbol.iterator() method. They are known as iterables.
const array = [1,2,3];
// or it can be written as for (let x of array){
for (let x of array[Symbol.iterator]()){
console.log(x);
}The output of it should be,
1
2
3They are objects returned by symbol.iterator() method.
They use next() method which returns the next item in the sequence. It contains mainly two properties,
const array = [1,2,3];
let arrIterator = array[Symbol.iterator]();
console.log(arrIterator.next()); // {value: 1, done: false}
console.log(arrIterator.next()); // {value: 2, done: false}
console.log(arrIterator.next()); // {value: 3, done: false}
console.log(arrIterator.next()); // {value: undefined, done: true}Above is the default iterator, but they also can be defined manually like below
function iterator(index = 0, end, step = 1) {
const rangeIterator = {
next() {
if (index < end) {
let result = { value: index, done: false }
index += step;
return result;
}
return { value: undefined, done: true }
}
};
return rangeIterator;
}The example simply ranges over the index till the end increasing by step value. Once iteration will over, it will set done to true and will terminate the loop. It can be accessed as below
const it = iterator(1, 5, 1);
let result = it.next();
while (!result.done) {
console.log(result.value); // 1 2 3 4
result = it.next();
}Generators return multiple values using yield . They conform iterators and iterables protocol, but unlike them, we don’t have to maintain the internal state of variables using generators.
Generator functions can be written using function*. They do not initially execute their code. Instead, they return a special type of iterator. When we call the generator’s built-in next() method, the Generator function executes until it encounters the yield keyword.
Here is an example of a generator function,
function* iterator(index = 0, end, step = 1) {
while (index < end) {
yield index;
index += step;
}
}
// can call generator like below
const generator = iterator(1,5,1);
for (let value of generator) {
console.log(value);
}The output of it is,
1
2
3
4We’re Grateful to have you with us on this journey!
Suggestions and feedback are more than welcome!
Please reach us at Canopas Twitter handle @canopassoftware with your content or feedback. Your input enriches our content and fuels our motivation to create more valuable and informative articles for you.



Whether you need...