> For the complete documentation index, see [llms.txt](https://mihai-gheorghe.gitbook.io/tic/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://mihai-gheorghe.gitbook.io/tic/advanced-javascript/es6.md).

# ES6

ES6 refers to version 6, of the ECMAScript which is a JavaScript standard meant to ensure the interoperability of Web pages across different Web browsers. It was published in June 2015, therefore you might find it renamed as ES2015.

It is a **major enhancement to the JavaScript language** and adds many more features intended to make large-scale software development easier.

Along with these, it brings a so-called **syntactical sugar**, which is syntax with the same behavior but designed to make things easier to read or to express.

## var vs. let vs. const

The main difference between `var` and `let` is that instead of being function scoped, `let` is block scoped. What that means is that a variable created with the `let` keyword is available inside the “block” that it was created in as well as any nested blocks. “Block”, means anything surrounded by a curly brace `{}` like in a `for` loop or an `if` statement.

```javascript
{
   var globalVar = "Hello globalVar"
}

{
   let globalLet = "Hello globalLet";
}

console.log(globalVar)
console.log(globalLet)
```

`let` declarations are not hoisted.

```javascript
let me = "go";  // locally scoped
var i = "able"; // globally scoped

console.log(window.me); 
console.log(window.i);
```

```javascript
//Function Scope
function loop() {
  for( var i = 0; i < 5; i++) {
    console.log(i);
  }
  console.log(i)
}

//Block Scope
function loop2() {
  for( let i = 0; i < 5; i++) {
    console.log(i);
  }
  console.log(i)
}

loop()
loop2()
```

`const` prevents redeclaring the same variable

```javascript
const x = 5
x = 8 //throws error
```

```javascript
const obj = {
	firstName: 'Mihai',
	lastName: 'Gheorghe'
}

obj.firstName = 'Bogdan'
obj //doesn't throw an error due to difference between
//pass by value vs. pass by reference
```

## === vs ==

the 3 symbol comparison operator `===` or `!==` adds a type check between the two operands

```javascript
let x = 5
x === '5' //evaluates to false
x === 5 // evaluares to true
x == '5' // evaluares to true
x == 5 // evaluares to true
```

You can find more about how comparison operators evaluate in various scenarios at the following link:&#x20;

<https://dorey.github.io/JavaScript-Equality-Table/>

## Arrow functions

Arrows functions are both syntactical sugar and have a structural impact on how the code is being executed.

An arrow function expression is a compact alternative to a traditional function expression, but is limited and can't be used in all situations.

The `{ }` and `( )` and `return` are optional, but may be required.

For example, if you have multiple arguments or no arguments, you'll need to re-introduce parentheses around the arguments.

```javascript
// Traditional Function
function simpleCalc(a){
  return a + 100;
}

// Arrow function
a => a + 100
const simpleCalc = b => b + 100 //named arrow functions
```

```javascript
var sum = (a, b) => {
  let c = 40;
  return a + b + c;
}
```

An arrow function does not have its own bindings to `this` or `super`, and **should not be used as methods.**

```javascript
var cat = {
  lives: 9,
  jumps: () => {
    this.lives--;
  }
}

cat.jumps()
cat.lives //returns 9

var cow = {
  lives: 1,
  jumps: function() {
    this.lives--;
  }
}

cow.jumps()
cow.lives //returns 0
```

## Template literals

Template literals are string literals allowing embedded expressions. You can use multi-line strings and string interpolation features with them.

They were called "template strings" in prior editions of the ES2015 specification.

```javascript
console.log(`string text line 1
string text line 2`);
```

They allow also simple evaluations using the ${ eval } syntax.

```javascript
let a = 5;
let b = 10;
console.log(`Fifteen is ${a + b}`)
```

## this keyword

In JavaScript `this` is a special keyword and it basically **refers to the object it belongs to.** It was used before ES6 too but the introduction of arrow functions changes its behavior. `this` is an implicit parameter, that is when every execution context is created, `this` is automatically added to the local scope as a parameter with the value of what is on the left-hand side of `.` - calling function. If the "dot" is missing when calling the function, it will default to the global object.

However, it has different values depending on it is used:

* In a method, `this` refers to the **owner object**. If the method's syntax is written with an ES6 arrow function, then it refers to the **enclosing scope of the owner object**. In other words, in arrow functions, `this` is lexically scoped, meaning it refers to the object where the function was declared, not the called.
* In the global scope or in a function, `this` refers to the **global object**. However, using `strict mode`, `this` is `undefined`.
* In an event, such as a DOM `onclick`, it refers to the **element** that received the event.
* Core function methods such as `call()`, `apply()` or `bind()` can refer `this` to **any object passed as an argument**.

{% tabs %}
{% tab title="this in a mehod" %}

```javascript
var person = {
  firstName: "John",
  lastName : "Doe",
  id       : 5566,
  fullName : function() { //fullName is a method of person
    return this.firstName + " " + this.lastName;
  }
};

person.fullName() //returns "John Doe"
```

{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="this in global context" %}

```javascript
// In web browsers, the window object is also the global object:
console.log(this === window); // true

a = 37;
console.log(window.a); // 37

this.b = "MDN";
console.log(window.b)  // "MDN"
console.log(b)         // "MDN"
```

{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="this in arrow functions" %}

```javascript
var globalObject = this;
var foo = (() => this);
console.log(foo() === globalObject); // true
```

{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="this in an inline event handler" %}

```javascript
<button onclick="alert(this.tagName.toLowerCase());">
  Show this
</button>
```

{% endtab %}
{% endtabs %}

### .call()

The `call()` method calls (executes) a function with a given `this` value and arguments provided. In practice, it is used to write methods that can be reused on different objects. Both `this` and additional arguments are optional.

```javascript
function Product(name, price) {
  this.name = name;
  this.price = price;
}

function Food(name, price) {
  Product.call(this, name, price); //the new Food object is called on
  this.category = 'food'; // a method of Product and args are passed as well
}

console.log(new Food('cheese', 5).name);
// expected output: "cheese"
```

{% tabs %}
{% tab title="using this with arrow functions in a method" %}

```javascript
let person = {
    age: 23,
    incrementAge: function() {
        const add1 = () => { this.age++ }
        add1.call(this) //using add1() would have set "this" to "global object"
        // this way "this" available in the incrementAge method is passed to 
        // the arrow function add1
    }
}
```

{% endtab %}
{% endtabs %}

### .apply()

This methods is similar to `call()`, the only difference is that the arguments are passed as an array. The method is quite useful even if `this` is not passed, in scenarios where we need to pass an unknown or large number of arguments.

{% tabs %}
{% tab title="a different way to concatenate" %}

```javascript
const array = ['a', 'b'];
const elements = [0, 1, 2];
array.push.apply(array, elements); //push accepts a variable number of arguments
console.info(array); // ["a", "b", 0, 1, 2]
```

{% endtab %}
{% endtabs %}

### .bind()

This method is also similar, meaning it takes `this` and additional arguments but returns a new function, which is not executed at that point.

```javascript
var person = {
    name: 'John'
    }
function sayTo(first, second){
    return first + ' ' + this.name  + second;
    }

var sayToPerson = sayTo.bind(person);

sayToPerson('Hi', '. How are you?') //Hi John. How are you?
```

## Spread operator and Rest parameters

The spread operator `...` allows an iterable variable or expression to be expanded for scenarios where more arguments are expected.

```javascript
function sum(x, y, z) {
  return x + y + z;
}

const numbers = [1, 2, 3];

console.log(sum(...numbers));
// expected output: 6


// it basically replaces the following use of apply
console.log(sum.apply(null, numbers));
// expected output: 6
```

{% tabs %}
{% tab title="Cloning or concatenating arrays" %}

```javascript
let arr = [1, 2, 3];
let arr2 = [...arr]; // like arr.slice()

arr2.push(4);
//  arr2 becomes [1, 2, 3, 4]
//  arr remains unaffected

let arr3 = [19, 15, -5];
arr = [...arr, ...arr3] //it can be used multiple times
// arr is now [1, 2, 3, 19, 15, -5]
```

{% endtab %}
{% endtabs %}

## ES6 Utility functions

ES6 introduced, among many other features, a set of utility functions that we'll find very useful for writing functional programming code. Here are some of them:

### .forEach()

This is an array method, explicitly  `Array.prototype.forEach()`, that executes a provided (callback) function once for each array element. The callback takes the following arguments in order: current value, index of the current value, and the whole array.  The last two are optional.

`forEach()` returns `undefined`

```javascript
const array1 = ['a', 'b', 'c'];

array1.forEach( (value, index, arr) => {
    console.log(`${value} pe pozitia ${index} din ${arr.length} pozitii`)
});

// expected output: "a pe pozitia 0 din 3 pozitii"
// expected output: "b pe pozitia 1 din 3 pozitii"
// expected output: "c pe pozitia 2 din 3 pozitii"
```

### .map()

This is a method similar to .forEach() but instead, it returns a new array, of the same length as the one called upon, where each element is the result of the callback function.

```javascript
let numbers = [1, 4, 9]
let roots = numbers.map(function(num) {
    return Math.sqrt(num)
})
// roots is now     [1, 2, 3]
// numbers is still [1, 4, 9]
```

Another shorthand example to create a new array containing the square of each element of the original array:

```javascript
const originalArray = [1, 2, 3, 4, 5];

const squaredArray = originalArray.map(x => x * x);

console.log(squaredArray); // [1, 4, 9, 16, 25]
```

### .filter()

`Array.prototype.filter()` also takes a callback function as an argument but this tests each element of the array. It returns a new array, possibly of smaller length, containing only the elements that passed the test.

```javascript
function isBigEnough(value) {
  return value >= 10  //although the callback returns boolean,
                      //filter returns an array of elements 
}

let filtered = [12, 5, 8, 130, 44].filter(isBigEnough)
// filtered is [12, 130, 44]
```

### .reduce()

This method is a bit more complex, but also very powerful. It accumulates the result of a callback function over each element of the array. In other words, **it reduces that array to a single value.**

The callback takes the following arguments: the accumulator, the current value being iterated, and optionally the index of the current value and the array itself.

The method takes, along the callback, an additional argument which will be considered the initial value of the accumulator.

```javascript
const array1 = [1, 2, 3, 4];
const reducer = (accumulator, currentValue) => accumulator + currentValue;

// 1 + 2 + 3 + 4
console.log(array1.reduce(reducer));
// expected output: 10

// 5 + 1 + 2 + 3 + 4
console.log(array1.reduce(reducer, 5));
// expected output: 15
```

```javascript
//under the hood basic simulation of .reduce()
const reduce = function(collection, callback, initialValue) {
    let accumulator = initialValue
    for (let i = 0; i < collection.length; i++) {
        if (i === 0 && initialValue === undefined) {
            accumulator = collection[0]
        } else {
            accumulator = callback(collection[i], accumulator)
        }
    }
    return accumulator
}

reduce([1,2,4], (v, sum) => v + sum, 0)
//in this case the arrow function is the callback
```

## More examples of ES6 utility functions

* **Array.from()** converts an iterable object to an array.
* **Array.find()** returns the first element in an array that satisfies a condition.
* **Array.findIndex()** returns the index of the first element in an array that satisfies a condition.
* **Array.includes()** checks if an array contains a specific element.
* **Array.some()** checks if any element in an array satisfies a condition.
* **Object.assign()** copies the properties of one object to another object.
* **Object.keys()** returns an array containing the names of all of the properties of an object.
* **Object.values()** returns an array containing the values of all of the properties of an object.
* **Object.entries()** returns an array containing two-element arrays, where each element is a property name and value from the original object.
* **Math.max()** returns the largest number of a given set of numbers.
* **Math.min()** returns the smallest number of a given set of numbers.
* **Math.floor()** rounds a number down to the nearest integer.
* **Math.ceil()** rounds a number up to the nearest integer.
* **Math.round()** rounds a number to the nearest integer.
