91 |
92 | ## # 2.1. let
93 |
94 | ES6 provides a new way of declaring a variable by using the `let` keyword. The `let` keyword is similar to the `var` keyword, except that these variables are **blocked-scope**.
95 |
96 | **Syntax:**
97 |
98 | ```js
99 | let variable_name;
100 | ```
101 |
102 | In JavaScript, blocks are denoted by curly braces `{}` , for example, the `if else`, `for`, `do while`, `while`, `try catch` and so on.
103 |
104 | **Example:**
105 |
106 | ```js
107 | let x = 10;
108 | if (x === 10) {
109 | let x = 20;
110 | console.log(x); // 20: reference x inside the block
111 | }
112 | console.log(x); // 10: reference at the begining of the script
113 |
114 | // Output:
115 | 20
116 | 10
117 | ```
118 |
119 | Because the `let` keyword declares a block-scoped variable, the `x` variable inside the `if` block is a **new variable** and it shadows the `x` variable declared at the top of the script. Therefore, the value of `x` in the console is 20.
120 |
121 | **⚝ [Try this example on CodeSandbox](https://codesandbox.io/s/es6-let-zikqqb?file=/src/index.js)**
122 |
123 |
126 |
127 | ## # 2.2. let vs var
128 |
129 | The `var` variables belong to the global scope when you define them outside a function. When you declare a variable inside a function using the `var` keyword, the scope of the variable is local. For example:
130 |
131 | ```js
132 | function increase() {
133 | var counter = 10;
134 | }
135 | console.log(counter); // cannot access the counter variable here
136 |
137 | // Output
138 | // ReferenceError: counter is not defined
139 | ```
140 |
141 | Here, the counter variable is local to the increase() function. It cannot be accessible outside of the function.
142 |
143 | The following example displays four numbers from 0 to 4 inside the loop and the number 5 outside the loop.
144 |
145 | ```js
146 | for (var i = 0; i < 3; i++) {
147 | console.log("Inside the loop:", i);
148 | }
149 |
150 | console.log("Outside the loop:", i);
151 |
152 | // Output:
153 | Inside the loop: 0
154 | Inside the loop: 1
155 | Inside the loop: 2
156 | Outside the loop: 3
157 | ```
158 |
159 | Here, the i variable is a global variable. Therefore, it can be accessed from both inside and after the for loop.
160 |
161 | The following example uses the `let` keyword instead of the `var` keyword:
162 |
163 | ```js
164 | for (let i = 0; i < 3; i++) {
165 | console.log("Inside the loop:", i);
166 | }
167 |
168 | console.log("Outside the loop:", i);
169 |
170 | // Output
171 | Inside the loop: 0
172 | Inside the loop: 1
173 | Inside the loop: 2
174 |
175 | Uncaught ReferenceError: i is not defined
176 | ```
177 |
178 | Here, the variable **i** is blocked scope. It means that the variable **i** only exists and can be accessible inside the for loop block.
179 |
180 | **⚝ [Try this example on CodeSandbox](https://codesandbox.io/s/es6-let-vs-var-yvu11z)**
181 |
182 |
185 |
186 | ## # 2.3. const
187 |
188 | ES6 provides a new way of declaring a constant by using the `const` keyword. The `const` keyword creates a **read-only** reference to a value.
189 |
190 | ```js
191 | const CONSTANT_NAME = value;
192 | ```
193 |
194 | Like the `let` keyword, the `const` keyword declares **blocked-scope** variables. However, the **block-scoped** variables declared by the `const` keyword can\'t be **reassigned**.
195 |
196 | ```js
197 | const RATE = 0.1;
198 | RATE = 0.2; // TypeError
199 | ```
200 |
201 | The const keyword ensures that the variable it creates is read-only. However, it doesn’t mean that the actual value to which the const variable reference is immutable. For example:
202 |
203 | ```js
204 | const person = { age: 20 };
205 | console.log(person.age); // 20
206 |
207 | person.age = 30; // OK
208 | console.log(person.age); // 30
209 | ```
210 |
211 | Even though the person variable is a constant, you can change the value of its property.
212 |
213 | However, you cannot reassign a different value to the person constant like this:
214 |
215 | ```js
216 | person = { age: 40 }; // TypeError
217 | ```
218 |
219 | If you want the value of the person object to be immutable, you have to freeze it by using the Object.freeze() method:
220 |
221 | ```js
222 | const person = Object.freeze({age: 20});
223 | person.age = 30; // TypeError
224 | ```
225 |
226 | *Note: `Object.freeze()` is shallow, meaning that it can freeze the properties of the object, not the objects referenced by the properties.*
227 |
228 | **⚝ [Try this example on CodeSandbox](https://codesandbox.io/s/es6-const-prej6w)**
229 |
230 |
233 |
234 | ## # 2.4. Arrow Function
235 |
236 | The Arrow function provides a more concise syntax for writing function expressions by opting out the function and return keywords using fat arrow(`=>`) notation.
237 |
238 | **Syntax:**
239 |
240 | ```js
241 | let myFunction = (arg1, arg2, ...argN) => expression
242 | ```
243 |
244 | ```js
245 | // Function Expression
246 | let add = function (x, y) {
247 | return x + y;
248 | };
249 | console.log(add(10, 20)); // 30
250 | ```
251 |
252 | The above function can be written as
253 |
254 | ```js
255 | // Arrow functions
256 | let add = (x, y) => x + y;
257 | console.log(add(10, 20)); // 30
258 | ```
259 |
260 | **Example 01:** Arrow Function with No Argument
261 |
262 | If a function doesn\'t take any argument, then you should use empty parentheses.
263 |
264 | ```js
265 | let greet = () => console.log('Hello');
266 | greet(); // Hello
267 | ```
268 |
269 | **Example 02:** Arrow Function with One Argument
270 |
271 | If a function has only one argument, you can omit the parentheses.
272 |
273 | ```js
274 | let greet = x => console.log(x);
275 | greet('Hello'); // Hello
276 | ```
277 |
278 | **Example 03:** Arrow Function as an Expression
279 |
280 | You can also dynamically create a function and use it as an expression.
281 |
282 | ```js
283 | let age = 25;
284 |
285 | let welcome = (age < 18) ?
286 | () => console.log('Baby') :
287 | () => console.log('Adult');
288 |
289 | welcome(); // Adult
290 | ```
291 |
292 | **Example 04:** Multiline Arrow Functions
293 |
294 | If a function body has multiple statements, you need to put them inside curly brackets `{}`.
295 |
296 | ```js
297 | let area = (r) => {
298 | const pi = 3.14;
299 | return pi * r * r;
300 | }
301 |
302 | let result = area(10);
303 | console.log(result); // 314
304 | ```
305 |
306 | *Note: Unlike regular functions, arrow functions do not have their own `this`. The value of `this` inside an arrow function remains the same throughout the lifecycle of the function and is always bound to the value of `this` in the closest non-arrow parent function.*
307 |
308 | **⚝ [Try this example on CodeSandbox](https://codesandbox.io/s/es6-arrow-function-yl7oqo?file=/src/index.js)**
309 |
310 |
313 |
314 | ## # 2.5. Default Function Parameters
315 |
316 | Default parameters allow named parameters of a function to be initialized with default values if no value or undefined is passed.
317 |
318 | **Syntax:**
319 |
320 | ```js
321 | function fn(param1=default1, param2=default2,..) {
322 | // ...
323 | }
324 | ```
325 |
326 | Prior to ES6, you need check for undefined values and provide the default value for undefined values using if/else or ternary operator
327 |
328 | ```js
329 | function sum(a, b) {
330 | a = (typeof a !== 'undefined') ? a : 10;
331 | b = (typeof b !== 'undefined') ? b : 20;
332 | return a + b;
333 | }
334 |
335 | console.log(sum()); // 30
336 | console.log(sum(20)); // 40
337 | ```
338 |
339 | In ES6, these checks can be avoided using default parameters
340 |
341 | ```js
342 | function sum(a = 10, b = 20) {
343 | return a + b;
344 | }
345 |
346 | console.log(sum()); // 30
347 | console.log(sum(20)); // 40
348 | console.log(sum(20, 30)); // 50
349 | ```
350 |
351 | **⚝ [Try this example on CodeSandbox](https://codesandbox.io/s/es6-default-parameters-8uu0m8?file=/src/index.js)**
352 |
353 |
356 |
357 | ## # 2.6. Rest Parameter
358 |
359 | The rest parameter is used to represent an indefinite number of arguments as an array. The important point here is only the function\'s last parameter can be a "rest parameter".
360 |
361 | This feature has been introduced to reduce the boilerplate code that was induced by the arguments.
362 |
363 | **Example:**
364 |
365 | ```js
366 | function sum(...args) {
367 | return args.reduce((previous, current) => {
368 | return previous + current;
369 | });
370 | }
371 |
372 | console.log(sum(10)); // 10
373 | console.log(sum(10, 20)); // 30
374 | console.log(sum(10, 20, 30)); // 60
375 | ```
376 |
377 | **⚝ [Try this example on CodeSandbox](https://codesandbox.io/s/es6-rest-parameters-w8zy28?file=/src/index.js)**
378 |
379 |
515 |
516 | ## # 2.9. Binary and Octal literals
517 |
518 | ES5 provided numeric literals in octal (prefix 0), decimal (no prefix), and hexadecimal ( 0x) representation. ES6 added support for binary literals and improvements on octal literals.
519 |
520 | **1. Binary literals:**
521 |
522 | Prior to ES5, JavaScript didn\'t provide any literal form of binary numbers. So you need to use a binary string with the help of `parseInt()`
523 |
524 | ```js
525 | const num = parseInt('111', 2);
526 | console.log(num); // 7
527 | ```
528 |
529 | Whereas ES6 added support for binary literals using the **0b** prefix followed by a sequence of binary numbers (i.e, 0 and 1).
530 |
531 | ```js
532 | const num = 0b111;
533 | console.log(num); // 7
534 | ```
535 |
536 | **2. Octal literals:**
537 |
538 | In ES5, to represent an octal literal, you use the zero prefix (0) followed by a sequence of octal digits (from 0 to 7).
539 |
540 | ```js
541 | const num = 055;
542 | console.log(num); // 45
543 |
544 | // Note: Legacy octal literals are not allowed in strict mode
545 | ```
546 |
547 | ES6 represents the octal literal by using the prefix **0o** followed by a sequence of octal digits from 0 through 7.
548 |
549 | ```js
550 | let num = 0o10;
551 | console.log(num); // 8
552 | ```
553 |
554 | **⚝ [Try this example on CodeSandbox](https://codesandbox.io/s/es6-binary-literals-mllutq?file=/src/index.js)**
555 |
556 |
559 |
560 | ## # 2.10. Template literals
561 |
562 | Template literals allows you to work with strings in a new way compared to ES5. These are just string literals allowing embedded expressions denoted by the dollar sign and curly braces **${expression}**. These literals are enclosed by the backtick character instead of double or single quotes.
563 |
564 | **Example:**
565 |
566 | ```js
567 | let str = "World";
568 | let message = `Hello ${str}`;
569 |
570 | console.log(message); // Hello World
571 | ```
572 |
573 | **Multiline Strings:**
574 |
575 | ```js
576 | let msg = 'Multiline \n\
577 | string';
578 |
579 | console.log(msg);
580 |
581 | // Multiline
582 | // string
583 | ```
584 |
585 | **Tagged Templates:**
586 |
587 | A template tag carries the transformation on the template literal and returns the result string.
588 |
589 | It can be used in creating components in `CSS-In-JS` styled components to use across the application
590 |
591 | ```js
592 | function tag(strings) {
593 | console.log(strings.raw[0]);
594 | }
595 |
596 | tag`Hello World`;
597 |
598 | // Output
599 | Hello World
600 | ```
601 |
602 | **⚝ [Try this example on CodeSandbox](https://codesandbox.io/s/es6-template-literals-d005t4?file=/src/index.js)**
603 |
604 |
879 |
880 | ## # 4. ES6 modules
881 |
882 | An ES6 module is a JavaScript file that executes in strict mode only. It means that any variables or functions declared in the module won\'t be added automatically to the global scope.
883 |
884 | ES6 has provided the built-in support for modules. Everything inside a module is private by default. Public variables, functions and classes are exposed using `export` statement and import the same using `import` statement.
885 |
886 | **Export Statement:**
887 |
888 | There are two types of exports:
889 |
890 | **1. Named Exports:**
891 |
892 | You can export each element or a single export statement to export all the elements at once
893 |
894 | ```js
895 | // module "my-module.js"
896 | const PI = Math.PI;
897 |
898 | function add(...args) {
899 | return args.reduce((num, tot) => tot + num);
900 | }
901 |
902 | function multiply(...args) {
903 | return args.reduce((num, tot) => tot * num);
904 | }
905 |
906 | // private function
907 | function print(msg) {
908 | console.log(msg);
909 | }
910 |
911 | export { PI, add, multiply };
912 | ```
913 |
914 | **2. Default Exports:**
915 |
916 | If we want to export a single value, you could use a default export
917 |
918 | ```js
919 | // module "my-module.js"
920 | export default function add(...args) {
921 | return args.reduce((num, tot) => tot + num);
922 | }
923 | ```
924 |
925 | **Import Statement:**
926 |
927 | The static import statement is used to import read only live bindings which are exported by another module.
928 |
929 | There are many variations of import scenarios as below,
930 |
931 | ```js
932 | // 1. Import an entire module's contents
933 | import * as name from "my-module";
934 |
935 | //2.Import a single export from a module
936 | import { export1 } from "my-module";
937 |
938 | //3.Import multiple exports from a module
939 | import { export1 , export2 } from "my-module";
940 |
941 | //4.Import default export from a module
942 | import defaultExport from "my-module";
943 |
944 | //5.Import an export with an alias
945 | import { export1 as alias1 } from "my-module";
946 | ```
947 |
948 |
980 |
981 | ## # 5.2. Getters and Setters
982 |
983 | The accessor properties are methods that get or set the value of an object. For that, we use these two keywords:
984 |
985 | * `get` - to define a getter method to get the property value
986 | * `set` - to define a setter method to set the property value
987 |
988 | **Example:**
989 |
990 | ```js
991 | /**
992 | * Getters and Setters
993 | */
994 | class Person {
995 | constructor(name) {
996 | this.name = name;
997 | }
998 | get name() {
999 | return this._name;
1000 | }
1001 | set name(name) {
1002 | this._name = name;
1003 | }
1004 | }
1005 |
1006 | let person = new Person("Mala Amar");
1007 | console.log(person.name); // "Mala Amar"
1008 | ```
1009 |
1010 | *Note: These setter and getter allow you to use the properties directly ( without using the parenthesis )*
1011 |
1012 | **⚝ [Try this example on CodeSandbox](https://codesandbox.io/s/es6-getters-and-setters-ugb9jx?file=/src/index.js)**
1013 |
1014 |
1017 |
1018 | ## # 5.3. Class expressions
1019 |
1020 | A class expression is another way to define a class. Class expressions can be named or unnamed. The name given to a named class expression is local to the class\'s body. However, it can be accessed via the name property.
1021 |
1022 | **Example:**
1023 |
1024 | ```js
1025 | /**
1026 | * Unnamed Class
1027 | */
1028 | let Person = class {
1029 | constructor(name) {
1030 | this.name = name;
1031 | }
1032 | getName() {
1033 | return this.name;
1034 | }
1035 | };
1036 |
1037 | let person = new Person("Anjali Durga");
1038 | console.log(person.getName()); // "Anjali Durga"
1039 | ```
1040 |
1041 | **⚝ [Try this example on CodeSandbox](https://codesandbox.io/s/es6-class-expressions-ofbg0i)**
1042 |
1043 |
1046 |
1047 | ## # 5.4. Static methods
1048 |
1049 | The static keyword defines a static method or property for a class, or a class static initialization block. Neither static methods nor static properties can be called on instances of the class. Instead, they\'re called on the class itself.
1050 |
1051 | Static methods are often utility functions, such as functions to create or clone objects, whereas static properties are useful for caches, fixed-configuration, or any other data you don't need to be replicated across instances.
1052 |
1053 | **Example:**
1054 |
1055 | ```js
1056 | /**
1057 | * Static methods
1058 | */
1059 | class Person {
1060 | constructor(name) {
1061 | this.name = name;
1062 | }
1063 |
1064 | static staticMethod(gender) {
1065 | let name = gender === "male" ? "Aryan Sarin" : "Manju Soni";
1066 | return new Person(name);
1067 | }
1068 | }
1069 |
1070 | let anonymous = Person.staticMethod("male");
1071 | console.log(anonymous); // Person {name: "Aryan Sarin"}
1072 | ```
1073 |
1074 | **⚝ [Try this example on CodeSandbox](https://codesandbox.io/s/es6-static-methods-3tf3li?file=/src/index.js)**
1075 |
1076 |
1116 |
1117 | ## # 5.6. Computed Property
1118 |
1119 | ES6 "Computed Property" feature allows you to have an expression in brackets `[]` (a piece of code that results in a single value like a variable or function invocation) be computed as a property name on an object.
1120 |
1121 | **Example:**
1122 |
1123 | ```js
1124 | /**
1125 | * Computed Property
1126 | */
1127 | let propName = "fullName";
1128 |
1129 | class Person {
1130 | constructor(firstName, lastName) {
1131 | this.firstName = firstName;
1132 | this.lastName = lastName;
1133 | }
1134 | get [propName]() {
1135 | return `${this.firstName} ${this.lastName}`;
1136 | }
1137 | }
1138 |
1139 | let person = new Person("Sharma", "Peri");
1140 | console.log(person.fullName);
1141 |
1142 | // Output
1143 | Sharma Peri
1144 | ```
1145 |
1146 | **⚝ [Try this example on CodeSandbox](https://codesandbox.io/s/es6-computed-property-ns52qr?file=/src/index.js)**
1147 |
1148 |
1151 |
1152 | ## # 5.7. Inheritance
1153 |
1154 | To create a class inheritance, use the `extends` keyword. A class created with a class inheritance inherits all the methods from another class. The `super()` method in the constructor is used to access all parent\'s properties and methods that are used by the derived class.
1155 |
1156 | **Example:**
1157 |
1158 | ```js
1159 | /**
1160 | * Inheritance
1161 | */
1162 |
1163 | // Parent Class
1164 | class Vehicle {
1165 | constructor(name, type) {
1166 | this.name = name;
1167 | this.type = type;
1168 | }
1169 |
1170 | getName() {
1171 | return this.name;
1172 | }
1173 |
1174 | getType() {
1175 | return this.type;
1176 | }
1177 | }
1178 | // Child Class
1179 | class Car extends Vehicle {
1180 | constructor(name) {
1181 | super(name, "car");
1182 | }
1183 |
1184 | getName() {
1185 | return "It is a car: " + super.getName();
1186 | }
1187 | }
1188 | let car = new Car("Tesla");
1189 |
1190 | console.log(car.getName()); // It is a car: Tesla
1191 | console.log(car.getType()); // car
1192 | ```
1193 |
1194 | **⚝ [Try this example on CodeSandbox](https://codesandbox.io/s/es6-inheritance-vposow)**
1195 |
1196 |
1199 |
1200 | ## # 5.8. new.target
1201 |
1202 | The `new.target` pseudo-property lets you detect whether a function or constructor was called using the **new** operator. In constructors and functions invoked using the new operator, `new.target` returns a reference to the constructor or function. In normal function calls, `new.target` is undefined.
1203 |
1204 | **Example:**
1205 |
1206 | ```js
1207 | /**
1208 | * new.target
1209 | */
1210 | class Person {
1211 | constructor(name) {
1212 | this.name = name;
1213 | console.log(new.target.name);
1214 | }
1215 | }
1216 |
1217 | class Employee extends Person {
1218 | constructor(name, title) {
1219 | super(name);
1220 | this.title = title;
1221 | }
1222 | }
1223 |
1224 | let person = new Person("Tara Mishra"); // Person
1225 | let employee = new Employee("Aditya Kala", "Programmer"); // Employee
1226 | ```
1227 |
1228 | **⚝ [Try this example on CodeSandbox](https://codesandbox.io/s/es6-new-target-dzurzb?file=/src/index.js)**
1229 |
1230 |
1233 |
1234 | ## # 6. Symbol
1235 |
1236 | The JavaScript ES6 introduced a new primitive data type called Symbol. Symbols are immutable (cannot be changed) and are unique.
1237 | Symbols are often used to add unique property keys to an object that won\'t collide with keys any other code might add to the object
1238 |
1239 | **Creating Symbols:**
1240 |
1241 | **Example 01:**
1242 |
1243 | ```js
1244 | // Two symbols with the same description
1245 |
1246 | const sym1 = Symbol("Hi");
1247 | const sym2 = Symbol("Hi");
1248 |
1249 | console.log(sym1 === sym2); // false
1250 | ```
1251 |
1252 | **Sharing Symbols:**
1253 |
1254 | ES6 provides you with the global symbol registry that allows you to share symbols globally. If you want to create a symbol that will be shared, you use the `Symbol.for()` method instead of calling the `Symbol()` function.
1255 |
1256 | The `Symbol.for()` method accepts a single parameter that can be used for symbol\'s description
1257 |
1258 | **Example 02:**
1259 |
1260 | ```js
1261 | let ssn = Symbol.for("ssn");
1262 | let citizenID = Symbol.for("ssn");
1263 | console.log(ssn === citizenID); // true
1264 | console.log(Symbol.keyFor(ssn)); // ssn
1265 | ```
1266 |
1267 | The `Symbol.for()` method first searches for the symbol with the ssn key in the global symbol registry. It returns the existing symbol if there is one. Otherwise, the `Symbol.for()` method creates a new symbol, registers it to the global symbol registry with the specified key, and returns the symbol.
1268 |
1269 | **Add Symbol as an Object Key:**
1270 |
1271 | You can add symbols as a key in an object using square brackets `[]`.
1272 |
1273 | **Example 03:**
1274 |
1275 | ```js
1276 | let id = Symbol("id");
1277 | let person = {
1278 | name: "Anjali Mistry",
1279 | // adding symbol as a key
1280 | [id]: 10 // not "id": 10
1281 | };
1282 | console.log(person); // {name: 'Anjali Mistry', Symbol(id): 10}
1283 | ```
1284 |
1285 | **Symbol Methods:**
1286 |
1287 | |Method |Description
1288 | |-----------|------------------------|
1289 | |for() |Searches for existing symbols|
1290 | |keyFor() |Returns a shared symbol key from the global symbol registry.|
1291 | |toSource() |Returns a string containing the source of the Symbol object|
1292 | |toString() |Returns a string containing the description of the Symbol|
1293 | |valueOf() |Returns the primitive value of the Symbol object.|
1294 |
1295 | **⚝ [Try this example on CodeSandbox](https://codesandbox.io/s/es6-symbol-xweo3x?file=/src/index.js)**
1296 |
1297 |
1300 |
1301 | ## # 7.1. Iterators
1302 |
1303 | Iterator is an object which allows us to access a collection of objects one at a time. ES6 provides built-in iterators for the collection types `String`, `Array`, `Set`, and `Map`.
1304 |
1305 | In JavaScript an iterator is an object which defines a sequence and potentially a return value upon its termination.Specifically, an iterator is any object which implements the Iterator protocol by having a `next()` method that returns an object with two properties:
1306 |
1307 | * `value`: The next value in the iteration sequence.
1308 | * `done`: This is true if the last value in the sequence has already been consumed. If value is present alongside done, it is the iterator's return value.
1309 |
1310 | **Example:**
1311 |
1312 | The following code creates a Sequence object that returns a list of numbers in the range of ( start, end) with an interval between subsequent numbers.
1313 |
1314 | ```js
1315 | /**
1316 | * Iterators
1317 | */
1318 | class Sequence {
1319 | constructor(start = 0, end = Infinity, interval = 1) {
1320 | this.start = start;
1321 | this.end = end;
1322 | this.interval = interval;
1323 | }
1324 | [Symbol.iterator]() {
1325 | let counter = 0;
1326 | let nextIndex = this.start;
1327 | return {
1328 | next: () => {
1329 | if (nextIndex <= this.end) {
1330 | let result = { value: nextIndex, done: false };
1331 | nextIndex += this.interval;
1332 | counter++;
1333 | return result;
1334 | }
1335 | return { value: counter, done: true };
1336 | }
1337 | };
1338 | }
1339 | }
1340 | ```
1341 |
1342 | The following code uses the Sequence iterator in a `for...of` loop:
1343 |
1344 | ```js
1345 | let evenNumbers = new Sequence(2, 10, 2);
1346 | for (const num of evenNumbers) {
1347 | console.log(num);
1348 | }
1349 |
1350 | // Output
1351 | 2
1352 | 4
1353 | 6
1354 | 8
1355 | 10
1356 | ```
1357 |
1358 | **⚝ [Try this example on CodeSandbox](https://codesandbox.io/s/es6-iterators-jx1led?file=/src/index.js)**
1359 |
1360 |
1363 |
1364 | ## # 7.2. Generators
1365 |
1366 | A generator is a function that can stop or suspend midway and then continue from where it stopped while maintaining the context(saved across re-entrances). It can be defined using a function keyword followed by an asterisk (`function* ()`).
1367 |
1368 | This function returns an iterator object and this iterator\'s **next()** method returns an object with a value property containing the yielded value and a done property which indicates whether the generator has yielded its last value.
1369 |
1370 | ```js
1371 | /**
1372 | * Generators
1373 | */
1374 | function* myGenerator(i) {
1375 | yield i + 10;
1376 | return i + 20;
1377 | }
1378 | const myGenObj = myGenerator(10);
1379 |
1380 | console.log(myGenObj.next().value); // 20
1381 | console.log(myGenObj.next().value); // 30
1382 | console.log(myGenObj.next().value); // undefined
1383 | ```
1384 |
1385 | **⚝ [Try this example on CodeSandbox](https://codesandbox.io/s/compassionate-lederberg-dxi5kw?file=/src/index.js)**
1386 |
1387 |
1390 |
1391 | ## # 7.3. Yield
1392 |
1393 | The `yield` keyword allows you to pause and resume a generator function (`function*`).
1394 |
1395 | The `yield` keyword causes the call to the generator\'s `next()` method to return an `IteratorResult` object with two properties: `value` and `done`. The `value` property is the result of evaluating the `yield` expression, and `done` is `false`, indicating that the generator function has not fully completed.
1396 |
1397 | ```js
1398 | /**
1399 | * Yield
1400 | */
1401 | function* counter() {
1402 | yield 1;
1403 | yield 2;
1404 | yield 3;
1405 | }
1406 | let count = counter();
1407 |
1408 | console.log(count.next()); // {value: 1, done: false}
1409 | console.log(count.next()); // {value: 1, done: false}
1410 | console.log(count.next()); // {value: 1, done: false}
1411 | ```
1412 |
1413 | **⚝ [Try this example on CodeSandbox](https://codesandbox.io/s/es6-yield-zo5i9j?file=/src/index.js)**
1414 |
1415 |
1418 |
1419 | ## # 8.1. Promises
1420 |
1421 | A promise is an object which represent the eventual completion or failure of an **asynchronous** operation.
1422 |
1423 | It is in one of these states:
1424 |
1425 | * **pending:** Represents initial state, neither fulfilled nor rejected.
1426 | * **fulfilled:** Indicates that the operation is completed successfully.
1427 | * **rejected:** Indicates that the operation is failed.
1428 |
1429 | In the beginning, the state of a promise is pending, indicating that the asynchronous operation is in progress. Depending on the result of the asynchronous operation, the state changes to either fulfilled or rejected.
1430 |
1431 | The fulfilled state indicates that the asynchronous operation was completed successfully:
1432 |
1433 |
1434 |
1435 |
1436 |
1437 | The rejected state indicates that the asynchronous operation failed.
1438 |
1439 |
1440 |
1441 |
1442 |
1443 | **Creating a Promise:**
1444 |
1445 | The promise constructor accepts a callback function that typically performs an asynchronous operation. This function is often referred to as an executor.
1446 |
1447 | In turn, the executor accepts two callback functions with the name `resolve` and `reject`.
1448 |
1449 | ```js
1450 | /**
1451 | * Promise
1452 | */
1453 | const promise = new Promise((resolve, reject) => {
1454 | // contain an operation
1455 | // ...
1456 |
1457 | // return the state
1458 | if (success) {
1459 | resolve(value);
1460 | } else {
1461 | reject(error);
1462 | }
1463 | });
1464 | ```
1465 |
1466 | If the asynchronous operation completes successfully, the executor will call the `resolve()` function to change the state of the promise from pending to fulfilled with a value.
1467 |
1468 | In case of an error, the executor will call `reject()` function to change the state of the promise from pending to rejected with the error reason.
1469 |
1470 | **1. The then() method:**
1471 |
1472 | The `then()` method accepts two callback functions: `onFulfilled` and `onRejected`.
1473 |
1474 | The `then()` method calls the `onFulfilled()` with a value, if the promise is fulfilled or the `onRejected()` with an error if the promise is rejected.
1475 |
1476 | ```js
1477 | promise.then(onFulfilled,onRejected);
1478 | ```
1479 |
1480 | **2. The catch() method:**
1481 |
1482 | If you want to get the error only when the state of the promise is rejected, you can use the `catch()` method of the Promise object. Internally, the `catch()` method invokes the `.then(undefined, onRejected)` method.
1483 |
1484 | ```js
1485 | promise.catch(onRejected);
1486 | ```
1487 |
1488 | **3. The finally() method:**
1489 |
1490 | The `.finally()` method returns a Promise. When the promise is finally either fulfilled or rejected, the specified callback function is executed. This provides a way for code to be run whether the promise was fulfilled successfully, or instead rejected.
1491 |
1492 | This helps to avoid duplicating code in both the promise's `.then()` and `.catch()` handlers.
1493 |
1494 | ```js
1495 | const render = () => {
1496 | //...
1497 | };
1498 |
1499 | getUsers()
1500 | .then((users) => {
1501 | console.log(users);
1502 | render(); // Duplicate Method
1503 | })
1504 | .catch((error) => {
1505 | console.log(error);
1506 | render(); // Duplicate Method
1507 | });
1508 | ```
1509 |
1510 | This can be avoided by using `.finally()`
1511 |
1512 | ```js
1513 | const render = () => {
1514 | //...
1515 | };
1516 |
1517 | getUsers()
1518 | .then((users) => {
1519 | console.log(users);
1520 | })
1521 | .catch((error) => {
1522 | console.log(error);
1523 | })
1524 | .finally(() => {
1525 | render();
1526 | });
1527 | ```
1528 |
1529 | **Example:**
1530 |
1531 | ```js
1532 | let promise = new Promise((resolve, reject) => {
1533 | setTimeout(() => {
1534 | console.log("Promise started...");
1535 | resolve("Promise resolved");
1536 | }, 300);
1537 | })
1538 | .then((value) => {
1539 | console.log("OK: " + value);
1540 | })
1541 | .catch((value) => {
1542 | console.log("ERROR: " + value);
1543 | })
1544 | .finally(() => {
1545 | console.log("Final Block");
1546 | });
1547 | ```
1548 |
1549 | **⚝ [Try this example on CodeSandbox](https://codesandbox.io/s/es6-promise-pqn0xf?file=/src/index.js)**
1550 |
1551 |
1554 |
1555 | ## # 8.2. Promise Chaining
1556 |
1557 | A common need is to execute two or more asynchronous operations back to back, where each subsequent operation starts when the previous operation succeeds, with the result from the previous step. We accomplish this by creating a **promise chain**.
1558 |
1559 | **Example:**
1560 |
1561 | ```js
1562 | /**
1563 | * Promise Chaining
1564 | */
1565 | new Promise(function(resolve, reject) {
1566 |
1567 | setTimeout(() => resolve(1), 1000); // (*)
1568 |
1569 | }).then(function(result) { // (**)
1570 |
1571 | alert(result); // 1
1572 | return result * 2;
1573 |
1574 | }).then(function(result) { // (***)
1575 |
1576 | alert(result); // 2
1577 | return result * 2;
1578 |
1579 | }).then(function(result) {
1580 |
1581 | alert(result); // 4
1582 | return result * 2;
1583 |
1584 | });
1585 | ```
1586 |
1587 | The idea is that the result is passed through the chain of .then handlers.
1588 |
1589 | Here the flow is:
1590 |
1591 | 1. The initial promise resolves in 1 second (*),
1592 | 1. Then the .then handler is called (**), which in turn creates a new promise (resolved with 2 value).
1593 | 1. The next then (***) gets the result of the previous one, processes it (doubles) and passes it to the next handler.
1594 | 1. …and so on.
1595 |
1596 | As the result is passed along the chain of handlers, we can see a sequence of alert calls: `1 → 2 → 4`.
1597 |
1598 |
1599 |
1600 |
1601 |
1602 | **⚝ [Try this example on CodeSandbox](https://codesandbox.io/s/es6-promise-chaining-zmxbb5?file=/src/index.js)**
1603 |
1604 |
1726 |
1727 | ## # 9.1. Set
1728 |
1729 | ES6 provides a new type named `Set` that stores a collection of **unique** values of any type.
1730 |
1731 | The `Set` constructor also accepts an optional **iterable object**. If you pass an iterable object to the `Set` constructor, all the elements of the iterable object will be added to the new set:
1732 |
1733 | **Syntax:**
1734 |
1735 | ```js
1736 | let setObject = new Set(iterableObject);
1737 | ```
1738 |
1739 | **Set Methods:**
1740 |
1741 | The Set object provides the following useful methods:
1742 |
1743 | |Sl.No.| Methods |Description |
1744 | |------|-------------|-----------------------------------------|
1745 | | 01. |add(value) |appends a new element with a specified value to the set. It returns the Set object, therefore, you can chain this method with another Set method.|
1746 | | 02. |clear() |removes all elements from the Set object.|
1747 | | 03. |delete(value) | deletes an element specified by the value.|
1748 | | 04. |entries() | returns a new Iterator that contains an array of [value, value] .|
1749 | | 05. |forEach(callback [, thisArg]) | invokes a callback on each element of the Set with the this value sets to thisArg in each call.|
1750 | | 06. |has(value) | returns true if an element with a given value is in the set, or false if it is not.|
1751 | | 07. |keys() | is the same as values() function.|
1752 | | 08. |[@@iterator] | returns a new Iterator object that contains values of all elements stored in the insertion order.|
1753 |
1754 | **Example 01:** Create a new Set from an Array
1755 |
1756 | ```js
1757 | let numbers = new Set([10, 20, 20, 30, 40, 50]);
1758 |
1759 | console.log(numbers); // Set(5) {10, 20, 30, 40, 50}
1760 | console.log(typeof numbers); // Object
1761 | ```
1762 |
1763 | **Example 02:** Get the size of a Set
1764 |
1765 | ```js
1766 | let size = numbers.size;
1767 |
1768 | console.log(size); // 5
1769 | ```
1770 |
1771 | **Example 03:** Add elements to a Set
1772 |
1773 | ```js
1774 | numbers.add(60);
1775 |
1776 | console.log(numbers); // Set(6) {10, 20, 30, 40, 50, 60}
1777 | ```
1778 |
1779 | **Example 04:** Check if a value is in the Set
1780 |
1781 | ```js
1782 | let isTrue = numbers.has(10);
1783 |
1784 | console.log(isTrue); // true
1785 | ```
1786 |
1787 | **Example 05:** Remove elements from a set
1788 |
1789 | ```js
1790 | numbers.delete(60);
1791 |
1792 | console.log(numbers); // Set(5) {10, 20, 30, 40, 50}
1793 | ```
1794 |
1795 | **Example 06:** Looping the elements of a Set
1796 |
1797 | ```js
1798 | for (let number of numbers) {
1799 | console.log(number);
1800 | }
1801 |
1802 | // Output
1803 | 10
1804 | 20
1805 | 30
1806 | 40
1807 | 50
1808 | ```
1809 |
1810 | **⚝ [Try this example on CodeSandbox](https://codesandbox.io/s/es6-set-dho0q8?file=/src/index.js)**
1811 |
1812 |
1815 |
1816 | ## # 9.2. Weakset
1817 |
1818 | The `WeakSet` store a collection of objects. It adapts the same properties of that of a `set` i.e. does not store duplicates. However, `WeakSet` can only contain **objects** whereas a `Set` can contain any data types such as strings, numbers, objects, etc. Since objects in a `WeakSet` may be automatically **garbage-collected**, a `WeakSet` does not have size property.
1819 |
1820 | **Example:** WeakSet Methods
1821 |
1822 | WeakSets have methods `add()`, `delete()`, and `has()`.
1823 |
1824 | ```js
1825 | /**
1826 | * Weakset
1827 | */
1828 | const weakSet = new WeakSet();
1829 |
1830 | let obj = {
1831 | message: 'Hi',
1832 | sendMessage: true
1833 | }
1834 |
1835 | // adding object (element) to WeakSet
1836 | weakSet.add(obj);
1837 |
1838 | // check if an element is in Set
1839 | console.log(weakSet.has(obj)); // true
1840 |
1841 | console.log(weakSet); // WeakSet {{message: "Hi", sendMessage: true}}
1842 |
1843 | // delete elements
1844 | weakSet.delete(obj);
1845 | console.log(weakSet); // WeakSet {}
1846 | ```
1847 |
1848 | **⚝ [Try this example on CodeSandbox](https://codesandbox.io/s/es6-weakset-cg86e3?file=/src/index.js)**
1849 |
1850 |
1853 |
1854 | ## # 9.3. Map
1855 |
1856 | A `Map` object holds **key-value** pairs where values of any type can be used as either `keys` or `values`. In addition, a `Map` object remembers the original insertion order of the `keys`.
1857 |
1858 | **Syntax:**
1859 |
1860 | ```js
1861 | let map = new Map([iterable]);
1862 | ```
1863 |
1864 | **Map methods:**
1865 |
1866 | |Sl.No.| Methods | Description |
1867 | |------|------------|--------------------------|
1868 | | 01. |clear() | removes all elements from the map object.|
1869 | | 02. |delete(key) | removes an element specified by the key. It returns if the element is in the map, or false if it does not.|
1870 | | 03. |entries() |returns a new Iterator object that contains an array of [key, value] for each element in the map object. The order of objects in the map is the same as the insertion order.|
1871 | | 04. |forEach(callback[, thisArg]) | invokes a callback for each key-value pair in the map in the insertion order. The optional thisArg parameter sets the this value for each callback.|
1872 | | 05. |get(key) | returns the value associated with the key. If the key does not exist, it returns undefined.|
1873 | | 06. |has(key) | returns true if a value associated with the key exists, otherwise, return false.|
1874 | | 07. |keys() |returns a new Iterator that contains the keys for elements in insertion order.|
1875 | | 08. |set(key, value) | sets the value for the key in the map object. It returns the map object itself therefore you can chain this method with other methods.|
1876 | | 09. |values() |returns a new iterator object that contains values for each element in insertion order.|
1877 |
1878 | **Initialize a map with an iterable object:**
1879 |
1880 | ```js
1881 | let lalit = { name: 'Lalit Ranganathan' },
1882 | jayesh = { name: 'Jayesh Ray' },
1883 | sarvesh = { name: 'Sarvesh Tripathi' };
1884 |
1885 | let userRoles = new Map([
1886 | [lalit, 'admin'],
1887 | [jayesh, 'editor'],
1888 | [sarvesh, 'subscriber'],
1889 | ]);
1890 |
1891 | // Get an element from a map by key
1892 | userRoles.get(john); // admin
1893 |
1894 | // Check the existence of an element by key
1895 | userRoles.has(lily); // true
1896 | ```
1897 |
1898 | **Iterate over map keys:**
1899 |
1900 | ```js
1901 | /**
1902 | * Map
1903 | */
1904 | let lalit = { name: 'Lalit Ranganathan' },
1905 | jayesh = { name: 'Jayesh Ray' },
1906 | sarvesh = { name: 'Sarvesh Tripathi' };
1907 |
1908 | let userRoles = new Map([
1909 | [lalit, 'admin'],
1910 | [jayesh, 'editor'],
1911 | [sarvesh, 'subscriber'],
1912 | ]);
1913 |
1914 | for (const user of userRoles.keys()) {
1915 | console.log(user.name);
1916 | }
1917 | ```
1918 |
1919 | **⚝ [Try this example on CodeSandbox](https://codesandbox.io/s/es6-map-w6i921?file=/src/index.js)**
1920 |
1921 |
1924 |
1925 | ## # 9.4. Weakmap
1926 |
1927 | A `WeakMap` is similar to a `Map` except the `keys` of a `WeakMap` must be **objects**. It means that when a reference to a key (an object) is out of scope, the corresponding value is automatically released from the memory.
1928 |
1929 | A `WeakMap` only has subset methods of a `Map` object:
1930 |
1931 | * get(key)
1932 | * set(key, value)
1933 | * has(key)
1934 | * delete(key)
1935 |
1936 | ```js
1937 | /**
1938 | * Weakmap
1939 | */
1940 | var weakMap = new WeakMap();
1941 | var obj1 = {};
1942 | var obj2 = {};
1943 |
1944 | weakMap.set(obj1, 10);
1945 | weakMap.set(obj2, 20);
1946 | weakMap.set({}, { four: 4 });
1947 |
1948 | console.log(weakMap.get(obj2)); // 20
1949 |
1950 | /**
1951 | * Return false even though empty object exists as key.
1952 | * Because the keys have different references
1953 | * */
1954 | console.log(weakMap.has({}));
1955 |
1956 | weakMap.delete(obj1);
1957 | console.log(weakMap.get(obj1)); //undefined
1958 | ```
1959 |
1960 | **⚝ [Try this example on CodeSandbox](https://codesandbox.io/s/es6-weakmap-mexzxg?file=/src/index.js)**
1961 |
1962 |
1965 |
1966 | ## # 10.1. Array.of()
1967 |
1968 | The `Array.of()` method creates a new Array instance from a variable number of arguments, regardless of number or type of the arguments.
1969 |
1970 | The difference between `Array.of()` and the `Array` constructor is in the handling of integer arguments: Array.of(3) creates an array with a single element, 3, whereas Array(3) creates an empty array with a length property of 3 (Note: this implies an array of 3 empty slots, not slots with actual `undefined` values).
1971 |
1972 | **Syntax:**
1973 |
1974 | ```js
1975 | Array.of(element0, element1, /* ... ,*/ elementN)
1976 | ```
1977 |
1978 | **Example:**
1979 |
1980 | ```js
1981 | /**
1982 | * Array.of()
1983 | */
1984 |
1985 | // Array Method
1986 | let number = Array(3);
1987 | console.log(number.length); // 3
1988 | console.log(number[0]); // undefined
1989 | console.log("Array of Method");
1990 |
1991 | // Array of() Method
1992 | let numbers = Array.of(3);
1993 | console.log(numbers.length); // 1
1994 | console.log(numbers[0]); // 3
1995 | ```
1996 |
1997 | **⚝ [Try this example on CodeSandbox](https://codesandbox.io/s/es6-array-of-7xgzm6?file=/src/index.js)**
1998 |
1999 |
2272 |
2273 | ## # 13.1. Proxies
2274 |
2275 | The `Proxy` object allows you to create an object that can be used in place of the original object, but which may redefine fundamental `Object` operations like getting, setting, and defining properties. Proxy objects are commonly used to log property accesses, validate, format, or sanitize inputs, and so on.
2276 |
2277 | The proxy object is created with two parameters:
2278 |
2279 | ```js
2280 | let proxy = new Proxy(target, handler)
2281 | ```
2282 |
2283 | * **target:** the original object which you want to proxy
2284 | * **handler:** an object that defines which operations will be intercepted and how to redefine intercepted operations.
2285 |
2286 | **Example:**
2287 |
2288 | ```js
2289 | /**
2290 | * Proxy object
2291 | */
2292 | const user = {
2293 | name: "Rishima Karpe",
2294 | email: "rishima.karpe@email.com"
2295 | };
2296 |
2297 | const handler = {
2298 | get(target, property) {
2299 | console.log(`Property ${property}:`);
2300 | return target[property];
2301 | }
2302 | };
2303 |
2304 | const proxyUser = new Proxy(user, handler);
2305 | console.log(proxyUser.name);
2306 | console.log(proxyUser.email);
2307 |
2308 | // Output
2309 | Property name: Rishima Karpe
2310 | Property email: rishima.karpe@email.com
2311 | ```
2312 |
2313 | **⚝ [Try this example on CodeSandbox](https://codesandbox.io/s/es6-proxies-u9tgie?file=/src/index.js)**
2314 |
2315 |
2384 |
2385 | ## # 14.1. Unicode
2386 |
2387 | Prior to ES6, JavaScript strings are represented by 16-bit character encoding (UTF-16). Each character is represented by 16-bit sequence known as code unit.
2388 |
2389 | ECMAScript 6 added full support for UTF-16 within strings and regular expressions. It introduces new Unicode literal form in strings and new RegExp flag **\u** mode to handle code points, as well as new APIs(codePointAt, fromCodePoint) to process strings.
2390 |
2391 | **Example:**
2392 |
2393 | ```js
2394 | /**
2395 | * Unicode
2396 | */
2397 | let str = '𠮷';
2398 |
2399 | // new string form
2400 | console.log('\u{20BB7}'); // "𠮷"
2401 |
2402 | // new RegExp u mode
2403 | console.log(new RegExp('\u{20BB7}', 'u'));
2404 | console.log(/^.$/u.test(str)); // true
2405 |
2406 | //API methods
2407 | console.log(str.codePointAt(0)); // 134071
2408 | console.log(str.codePointAt(1)); // 57271
2409 |
2410 | console.log(String.fromCodePoint(134071)); // "𠮷"
2411 | ```
2412 |
2413 | **⚝ [Try this example on CodeSandbox](https://codesandbox.io/s/es6-unicode-ve845j?file=/src/index.js)**
2414 |
2415 |
2418 |
2419 | ## # 14.2. Proper Tail Calls
2420 |
2421 | **Proper tail call ( PTC )** is a technique where the program or code will not create additional stack frames for a recursion when the function call is a tail call.
2422 |
2423 | For example, the below classic or head recursion of factorial function relies on stack for each step. Each step need to be processed upto `n * factorial(n - 1)`
2424 |
2425 | ```js
2426 | function factorial(n) {
2427 | if (n === 0) {
2428 | return 1
2429 | }
2430 | return n * factorial(n - 1)
2431 | }
2432 |
2433 | console.log(factorial(5)); //120
2434 | ```
2435 |
2436 | But if you use **Tail recursion functions**, they keep passing all the necessary data it needs down the recursion without relying on the stack.
2437 |
2438 | The browsers which supports PTC do not generate stack overflow instead shows Infinity with below inputs,
2439 |
2440 | ```js
2441 | function factorial(n, acc = 1) {
2442 | if (n === 0) {
2443 | return acc;
2444 | }
2445 | return factorial(n - 1, n * acc);
2446 | }
2447 |
2448 | console.log(factorial(5)); //120
2449 | console.log(factorial(10)); // 3628800
2450 | console.log(factorial(100)); // 9.332621544394418e+157
2451 | console.log(factorial(1000)); // Infinity
2452 | ```
2453 |
2454 | **⚝ [Try this example on CodeSandbox](https://codesandbox.io/s/es6-proper-tail-calls-qmptbq?file=/src/index.js)**
2455 |
2456 |