Introduction to Modern JavaScript
Tags: javascript, beginners, programming, webdev
Published: 2022-02-13 15:52:02

Today, JavaScript is one of the most widely used programming languages on the planet. It may be used to create online or mobile applications, as well as command-line utilities. The ECMAScript 6 (ES 6) standard, which was introduced in 2015 and includes numerous new capabilities for JavaScript, is used in modern JavaScript.

### ECMAScript
The European Computer Manufacturers Association (ECMA) is a standard for scripting languages such as JavaScript. Most of the time, these specs add new features to the language that improve the development experience.

Let's have a look at some of the significant changes brought about by the new ECMAScript version.

#### Variable Declaration
To declare a variable in JavaScript, there are three keywords available, each with its own set of peculiarities. **var**, **let**, and **const** are the three variables. Prior to ES6, there were only two types of scopes in JavaScript: Global Scope and Function Scope. With the ES6 version, the keywords "let" and "const" were added. In JavaScript, these two keywords give block scope variables and constants. The table below shows if these three variables can be reassigned, mutated, and the type of scope:

![Explanation of how variables are reassigned, mutated, and type of scope.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nvh6wmzkocc8y00a3ufb.jpeg)

* **var** declared variables are also hoisted during execution, which means they are pushed to the top of the scope. The following code, for example,

```javascript
console.log(myNum); // `undefined`
var myNum = 5;
```
is interpreted as follows during execution:

```javascript
var myNum;
console.log(myNum); // `undefined`
myNum = 5;
```
When a variable is defined using the **var** keyword in a function, it is function scoped, which means that it may be accessed by everything in that function. A function scoped variable generated within a function, on the other hand, cannot be accessed outside of that function. For example: 

```javascript
function myFunc () {
   var myFunc = 'Mary';
   console.log(myFunc); // `Mary`
}
myFunc();
console.log(myFunc); // [Function: myFunc] 
// myFunc can not be accessed in the outermost scope of the function.
```

* let - the assignment of let and const variables is also hoisted, but not their declarations. It may appear that there is no hoisting since they are designed to not be employed prior to assignation, but there is.
Furthermore, a **let** variable cannot be re-declared:

```javascript
let num = 2;
let num = 8; // Syntaxerror
```
Let's look at how block-scoping affects our prior example using let:
```javascript
function myFunc() {
    let myFunc = 'Mary';
    if (true) {
        let myFunc = 'Joh';
        console.log(myFunc); // 'Joh' 
        // a new variable myFunc has been created
        // the variable can not be accessed outside this scope
    }
    console.log(myFunc); // 'Mary'
    // the if statement does not affect this value since it is in a different scope
}
myFunc();
console.log(myFunc); // [Function: myFunc]
// myFunc can not be accessed in the outermost function
```
* const - variables can not be re-declared
```javascript
const name = 'Mary';
const name = 'John'; // SyntaxError
```
Re-assignment is also not allowed with **const**

```javascript
const name = 'Mary';
name = 'John'; // raises an error
```
Const variables, in a nutshell:
1. Before being assigned, it is not possible to access it.
2. It is not possible to declare it again in the same scope.
3. Are block scoped
4. Reassignment is not possible.

**const** variables are not immutable, as we well know. It does, however, imply that variables in an array or object marked as **const** can be mutated.

In the case of arrays:

```javascript
const person = [];
person.push('Mary'); // variable is mutated but not reassigned
console.log(person[0]); // 'Mary' 
person = ['Roy']// TypeError. Reassignment not allowed
```
In the case of objects:

```javascript
const person = {
  name: 'Mary'
};
person.name = 'Roy' // variable is mutated and not completely reassigned.
console.log(person.name) //
person = "Sandra" // TypeError. Reassignment not allowed
```

---
#### Arrow Function

Functions in JavaScript can be defined in the following ways:

```javascript
// Method 1
function add1(a, b) {
    return a + b; // explicit return
}

// Method 2
let add2 = (a, b) => {
    return a + b;
}

let add3 = (a, b) => a + b; // implicit return

console.log(add1(2,3)); 
console.log(add2(4,5)); 
console.log(add3(6,7));
```
Output:
```javascript
5
9
13
```
The first method is the standard JavaScript way for defining functions. The _arrow function_, which was introduced in ES6, is the alternative method 2.
The following are some of the benefits of employing arrow functions:

- enables us to define more concise function syntax with implicit return as seen above.

When the _return_ keyword is used in the body of a function, it is called an explicit return. You can use implicit return with arrow functions, which means you do not have to use the _return_ keyword to return a value.

- _this_ is a result of the surrounding.

Because _this_ is equal to the _this_ value of the surrounding execution environment, you don't need to use the "that = this" technique before executing a function within a function when using arrow functions.

```javascript
function myFunc() {
    this.num = 5;
    setTimeout(() => {
      this.num++;
      console.log(this.num) // 6
    }, 0);
  }
myFunc();
```
You can eliminate the parentheses surrounding your function if it just accepts only one parameter. For instance, have a look at the code below,

```javascript
const mul = (x) => x * 2;
```
without the use of parentheses:

```javascript
const mul = x => x * 2;
```

---
#### Object handling

If the variable name is the same as the property name, you can perform the following when assigning a variable to an object property:
```javascript
const num = 10;
const myObj = { num };
console.log(myObj.num);
```
Output:
```javascript
10
```

Before ES6, code was written like this when you wanted to define a new object literal and utilize variables as object property values:
```javascript
const num1 = 10;
const num2 = 20;

const myObj = {
  num1: num1,
  num2: num2,
};

console.log(myObj.num1)
console.log(myObj.num2)
```
Output:
```javascript
10
20
```
Because the names of the properties of myObj are the same as the variable names, this is somewhat redundant. Therefore, when the variable name and the property name are the same in ES2015, you may use the following syntax:
```javascript
const num1 = 20;
const num2 = 30;

const myObj = {
  num1,
  num2,
};

console.log(myObj.num1) 
console.log(myObj.num2)
```
Output:
```javascript
20
30
```

##### Object.freeze() method

This technique is used to freeze an item. This indicates that if we apply this method on an object, we will not be able to update or tamper with it.
```javascript
let person = {
    name: 'Mary',
    contacts: {
        mobile: '123',
        home: '456'
    }
}
Object.freeze(person);

person.name = 'Nick';
console.log(person);

person.contacts.mobile = '789';
console.log(person);
```
Output:
```javascript
{ name: 'Mary', contacts: { mobile: '123', home: '456' } }
{ name: 'Mary', contacts: { mobile: '789', home: '456' } }
```

I tried to set a new value to the "name" property after using the ".freeze()" method on the object, but as the output indicates, it is unaffected. However, even though I used the ".freeze()" method to provide a new value to the "mobile" field of the "contacts" object, it was assigned. This is because when the ".freeze()" function is used to an object, it only freezes the object's first-level value. That is, it has no effect on items on the second or inner levels.

---
#### Promises

In JavaScript, promises are the most prevalent approach to handle asynchronous tasks. Developers utilized callbacks to manage asynchronous activities before promises. As a result, many callbacks within callbacks were constructed, eventually leading to "The Callback Hell."

A promise established in JavaScript is primarily built on the idea that when the time comes, it will either be resolved or rejected. A promise exists in the following three states:

- **Fulfilled** - the promised task was completed successfully.

- **Rejected** - the promised task was not completed or is unsuccessful.

- **Pending** - the promised task has neither been fulfilled nor rejected.

In a basic JavaScript example, let's utilize Promises.

```javascript
function work() {
    return true;
}
const myPromise = new Promise((resolve, reject) => {
    let isopen = work();

    if(isopen === true) {
        resolve('Shop is open!')
    }
    else {
        reject('Shop is closed now!')
    }
});

myPromise.then((order) => {
    console.log(order);
}).catch((order) => {
    console.log(order);
});
```
Output:
```javascript
Shop is open!
```
As you can see, I've generated a promise called "myPromise" with two parameters: one for success (resolve) and one for failure (reject). The return value of the "work()" function is sent to the "isopen" variable.

The result of the "task()" function is always "true." As a result, the Promise will verify the condition and, if it is true, resolve the Promise; else, it will be rejected. When we use the Promise, the "then()" function is invoked once the Promise has been resolved. Then we may determine what to do with the Promise that has been resolved. However, if the Promise fails, we must call the "catch()" function. In this case, it fulfilled the promise's requirement, and as a consequence, Promise will be resolved.

---
#### Classes
A JavaScript class is just a template for constructing objects. Because JavaScript is a prototype-based language, ES6 has introduced JavaScript classes as a prototype inheritance with syntactic sugar rather than a new class-based inheritance mechanism. If you are familiar with the C# or Java programming languages, you are probably already aware of the concept of classes.

Prototype syntax prior to ES6:

```javascript
var User = function(name, age) {
    this.name = name;
    this.age = age;
  }
  User.prototype.stringSentence = function() {
    return "Hello, my name is " + this.name + " and I'm " + this.age;
  }
  var newUser = new User("Mary", 20);
  console.log(newUser.name)
  console.log(newUser.age)
  console.log(newUser.stringSentence());
```
Output:
```javascript
Mary
20
Hello, my name is Mary and I'm 20
```
The following is the syntax for ES6 classes:

```javascript
class User {
    constructor(name, age) {
      this.name = name;
      this.age = age;
    }
    stringSentence() {
      return `Hello, my name is ${this.name} and I am ${this.age}`;
    }
  }
  const newUser = new User("Kim", 25);
  console.log(newUser.name)
  console.log(newUser.age)
  console.log(newUser.stringSentence()) 
```
Output:
```javascript
Kim
25
Hello, my name is Kim and I am 25
```
That brings me to the end of my article; I hope you enjoyed it. Happy Coding 👨‍💻!