├── .babelrc ├── es2020 ├── import-meta │ ├── my-module.js │ └── import-meta.js ├── dynamic-import │ ├── message.js │ ├── async-dynamic-import.js │ └── dynamic-import.js ├── for-in-order.js ├── promise-allsettled.js ├── nullish-coalescing-operator.js ├── string.matchAll.js ├── optional-chaining.js ├── globalThis.js └── bigint.js ├── .github ├── FUNDING.yml └── workflows │ └── main.yaml ├── images └── promises.png ├── es2021 ├── logical-assignment-operators │ ├── and.js │ ├── nullish.js │ └── or.js ├── replace-all.js ├── numeric-separator │ ├── binary-hex.js │ └── big-integers.js ├── weakref │ ├── weakref.js │ └── finalizer.js └── promise-any │ ├── promise-any-error.js │ └── promise-any.js ├── es2022 ├── hasOwn │ ├── hasOwn-create.js │ └── hasOwn-overwritten.js ├── array-at.js ├── regex-indices.js ├── class fields │ ├── private-fields-methods.js │ └── static-fields-methods.js └── error-cause.js ├── es2023 ├── 2.hashbang-syntax.js ├── 3.symbols-as-weakmap-keys.js ├── 1.array-from-last.js └── 4.change-array-by-copy.js ├── es2015 ├── 7.default-parameters.js ├── 22.binary-octal.js ├── 8.rest-parameter.js ├── 9.spread-operator.js ├── 5.template-literals.js ├── 11.generators.js ├── 13.set.js ├── 23.proper-tail-calls.js ├── 1.let-const-variables.js ├── 17.unicode.js ├── 14.weakset.js ├── 16.weakmap.js ├── 20.promises.js ├── 4.enhanced-object-literals.js ├── 15.map.js ├── 10.iterators-forof.js ├── 18.symbols.js ├── 3.classes.js ├── 12.modules.js ├── 19.proxies.js ├── 21.reflect.js ├── 2.arrow-functions.js └── 6.destructuring.js ├── .gitignore ├── es2019 ├── 3.trimstart-trimend.js ├── 6.json-improvements.js ├── 5.optional-catch-binding.js ├── 4.symbol-description.js ├── 8.function-tostring.js ├── 2.object-fromentries.js ├── 7.array-stable-sort.js └── 1.array-flat-flatmap.js ├── es2017 ├── 1.async-functions.js ├── 4.object-property-descriptors.js ├── 2.object-values.js ├── 7.trailing-commas.js ├── 3.object-entries.js ├── 5.string-padding.js └── 6.shared-memory-atomics.js ├── es2016 ├── exponential-operator.js └── array-includes.js ├── es2018 ├── 3.promise-finally.js ├── 1.async-iterators.js └── 2.object-rest-spread-operators.js ├── es2024 ├── 6.promise-withResolvers.js ├── 4.atomics-waitasync.js ├── 3.well-formed-unicode-strings.js └── 1.groupby-objects-maps.js ├── package.json ├── es2025 ├── 1.set-methods.js ├── 2.iterator-helpers.js ├── 3.temporal-api.js └── 4.decorator-metadata.js └── README.md /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env"] 3 | } -------------------------------------------------------------------------------- /es2020/import-meta/my-module.js: -------------------------------------------------------------------------------- 1 | console.log("'I'm a module"); -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [sudheerj] 2 | custom: https://buymeacoffee.com/sudheerj 3 | -------------------------------------------------------------------------------- /es2020/import-meta/import-meta.js: -------------------------------------------------------------------------------- 1 | console.log(import.meta); // { url: "file:///home/user/my-module.js" } -------------------------------------------------------------------------------- /images/promises.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sudheerj/ECMAScript-features/HEAD/images/promises.png -------------------------------------------------------------------------------- /es2021/logical-assignment-operators/and.js: -------------------------------------------------------------------------------- 1 | let x = 10; 2 | let y = 20; 3 | x &&= y; 4 | console.log(x); // 20 -------------------------------------------------------------------------------- /es2021/logical-assignment-operators/nullish.js: -------------------------------------------------------------------------------- 1 | let x; 2 | let y = 1; 3 | x ??= y; 4 | console.log(x); // 1 -------------------------------------------------------------------------------- /es2021/logical-assignment-operators/or.js: -------------------------------------------------------------------------------- 1 | let x = 0; 2 | let y = 20; 3 | x ||= y; 4 | console.log(x); // 20 -------------------------------------------------------------------------------- /es2022/hasOwn/hasOwn-create.js: -------------------------------------------------------------------------------- 1 | const user = Object.create(null); 2 | user.age = 35; 3 | user.hasOwn('age'); // true -------------------------------------------------------------------------------- /es2023/2.hashbang-syntax.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict'; 3 | console.log("Hello world from hashbang syntax"); -------------------------------------------------------------------------------- /es2015/7.default-parameters.js: -------------------------------------------------------------------------------- 1 | function add(a = 10, b = 20) { 2 | return a + b; 3 | } 4 | add(20); // 40 5 | add(); // 30 -------------------------------------------------------------------------------- /es2021/replace-all.js: -------------------------------------------------------------------------------- 1 | console.log('10101010'.replaceAll('0', '1')); // 11111111 2 | console.log('01010101'.replaceAll('0', '1')); // 11111111 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Cruft 2 | .DS_Store 3 | npm-debug.log 4 | .idea 5 | *.idea/ 6 | .idea/workspace.xml 7 | 8 | # Dependency directories 9 | node_modules/ -------------------------------------------------------------------------------- /es2022/array-at.js: -------------------------------------------------------------------------------- 1 | const array = [1, 2, 3, 4, 5]; 2 | console.log(array.at(-2)); // 4 3 | 4 | const string = '12345'; 5 | console.log(string.at(-2)); -------------------------------------------------------------------------------- /es2023/3.symbols-as-weakmap-keys.js: -------------------------------------------------------------------------------- 1 | const weak = new WeakMap(); 2 | const key = Symbol("ref"); 3 | weak.set(key, "ES2023"); 4 | 5 | console.log(weak.get(key)); //ES2023 -------------------------------------------------------------------------------- /es2021/numeric-separator/binary-hex.js: -------------------------------------------------------------------------------- 1 | const binaryLiteral = 0b1010_1010; 2 | console.log(binaryLiteral); 3 | const hexLiteral = 0xFF_FF_FF_FF; 4 | console.log(hexLiteral); -------------------------------------------------------------------------------- /es2020/dynamic-import/message.js: -------------------------------------------------------------------------------- 1 | export default () => { 2 | return "Hello, default export"; 3 | } 4 | export const sayGoodBye = () => { 5 | return "Bye, named export" 6 | } 7 | -------------------------------------------------------------------------------- /es2020/for-in-order.js: -------------------------------------------------------------------------------- 1 | var object = { 2 | 'a': 2, 3 | 'b': 3, 4 | 'c': 4 5 | } 6 | 7 | 8 | for(let key in object) { 9 | console.log(key); // a b c 10 | } -------------------------------------------------------------------------------- /es2022/hasOwn/hasOwn-overwritten.js: -------------------------------------------------------------------------------- 1 | const user = { 2 | age: 35, 3 | hasOwnProperty: ()=> { 4 | return false; 5 | } 6 | }; 7 | 8 | user.hasOwn('age') // true -------------------------------------------------------------------------------- /es2019/3.trimstart-trimend.js: -------------------------------------------------------------------------------- 1 | let messageTwo = " Hello World!! "; 2 | console.log(messageTwo.trimStart()); //Hello World!! 3 | console.log(messageTwo.trimEnd()); // Hello World!! -------------------------------------------------------------------------------- /es2019/6.json-improvements.js: -------------------------------------------------------------------------------- 1 | // JSON Superset 2 | console.log(JSON.parse('"\u2028"')); // '' 3 | 4 | // Well Formed JSON.Stringify 5 | console.log(JSON.stringify("\uD800")); // '"\ud800"' -------------------------------------------------------------------------------- /es2017/1.async-functions.js: -------------------------------------------------------------------------------- 1 | async function logger() { 2 | 3 | let data = await fetch('http://someapi.com/users'); // pause until fetch returns 4 | console.log(data) 5 | } 6 | logger(); -------------------------------------------------------------------------------- /es2019/5.optional-catch-binding.js: -------------------------------------------------------------------------------- 1 | let isTheFeatureImplemented = false; 2 | try { 3 | if(isFeatureSupported()) { 4 | isTheFeatureImplemented = true; 5 | } 6 | } catch (unused) {} -------------------------------------------------------------------------------- /es2016/exponential-operator.js: -------------------------------------------------------------------------------- 1 | //Prior ES7 2 | const cube = x => Math.pow(x, 3); 3 | console.log(cube(3)); // 27 4 | 5 | //Using ES7 6 | const cube1 = x => x ** 3; 7 | console.log(cube1(3)); // 27 -------------------------------------------------------------------------------- /es2023/1.array-from-last.js: -------------------------------------------------------------------------------- 1 | const isOdd = (number) => number % 2 === 1; 2 | const numbers = [1, 2, 3, 4, 5]; 3 | 4 | console.log(numbers.findLast(isOdd)); // 5 5 | console.log(numbers.findLastIndex(isOdd)); // 4 -------------------------------------------------------------------------------- /es2021/numeric-separator/big-integers.js: -------------------------------------------------------------------------------- 1 | const billion = 1000_000_000; 2 | console.log(billion); // 1000000000 3 | 4 | const trillion = 1000_000_000_000n; // BigInt number 5 | console.log(trillion); // 1000000000000 -------------------------------------------------------------------------------- /es2021/weakref/weakref.js: -------------------------------------------------------------------------------- 1 | const myObject = new WeakRef({ 2 | name: ‘Sudheer’, 3 | age: 34 4 | }); 5 | console.log(myObject.deref()); //output: {name: “Sudheer”, age: 35} 6 | console.log(myObject.deref().name); //output: Sudheer -------------------------------------------------------------------------------- /es2017/4.object-property-descriptors.js: -------------------------------------------------------------------------------- 1 | const profile = { 2 | age: 42 3 | }; 4 | 5 | const descriptors = Object.getOwnPropertyDescriptors(profile); 6 | console.log(descriptors); // {age: {configurable: true, enumerable: true, writable: true }} -------------------------------------------------------------------------------- /es2015/22.binary-octal.js: -------------------------------------------------------------------------------- 1 | //Binary literals 2 | const num = 0b110; 3 | console.log(num); // 6 4 | 5 | //Octal literals 6 | const num = 055; 7 | console.log(num); // 45 8 | 9 | const invalidNum = 028; 10 | console.log(invalidNum); // treated as decimal 28 11 | -------------------------------------------------------------------------------- /es2016/array-includes.js: -------------------------------------------------------------------------------- 1 | const array = [1,2,3,4,5,6]; 2 | if(array.includes(5)){ 3 | console.log("Found an element"); 4 | } 5 | 6 | let numbers = [1, 2, 3, 4, NaN, ,]; 7 | console.log(numbers.includes(NaN)); // true 8 | console.log(numbers.includes(undefined)); // true -------------------------------------------------------------------------------- /es2018/3.promise-finally.js: -------------------------------------------------------------------------------- 1 | let isLoading = true; 2 | fetch('http://somesite.com/users') 3 | .then(data => data.json()) 4 | .catch(err => console.error(err)) 5 | .finally(() => { 6 | isLoading = false; 7 | console.log('Finished loading!!'); 8 | }) -------------------------------------------------------------------------------- /es2024/6.promise-withResolvers.js: -------------------------------------------------------------------------------- 1 | const { promise, resolve, reject} = Promise.withResolvers(); 2 | 3 | setTimeout(() => { Math.random() > 0.5 ? resolve("Success") : reject("Error")},1000); 4 | promise.then(result => console.log(result)).catch(error => console.error(error)); -------------------------------------------------------------------------------- /es2015/8.rest-parameter.js: -------------------------------------------------------------------------------- 1 | function sum(...args) { 2 | return args.reduce((previous, current) => { 3 | return previous + current; 4 | }); 5 | } 6 | 7 | console.log(sum(1, 2, 3)); // 6 8 | console.log(sum(1, 2, 3, 4)); // 10 9 | console.log(sum(1, 2, 3, 4, 5)); // 15 -------------------------------------------------------------------------------- /es2015/9.spread-operator.js: -------------------------------------------------------------------------------- 1 | // In function and constructor calls 2 | console.log(Math.max(...[-10, 30, 10, 20])); //30 3 | console.log(Math.max(-10, ...[-50, 10], 30)); //30 4 | 5 | // In Array literals and strings 6 | console.log([1, ...[2,3], 4, ...[5, 6, 7]]); // 1, 2, 3, 4, 5, 6, 7 -------------------------------------------------------------------------------- /es2015/5.template-literals.js: -------------------------------------------------------------------------------- 1 | // Template literals 2 | const firstName = 'John'; 3 | console.log(`Hello ${firstName}! 4 | Good morning!`); 5 | 6 | // Tagged template literals 7 | const Button = styled.a` 8 | display: inline-block; 9 | border-radius: 3px; 10 | ` -------------------------------------------------------------------------------- /es2020/dynamic-import/async-dynamic-import.js: -------------------------------------------------------------------------------- 1 | (async function() { 2 | const moduleSpecifier = './message.js'; 3 | const messageModule = await import(moduleSpecifier); 4 | messageModule.default(); // Hello, default export 5 | messageModule.sayGoodBye(); //Bye, named export 6 | })(); -------------------------------------------------------------------------------- /es2022/regex-indices.js: -------------------------------------------------------------------------------- 1 | const regexPatter = /(Jack)/gd; 2 | const input = 'Authos: Jack, Alexander and Jacky'; 3 | const result = [...input.matchAll(regexPatter)]; 4 | console.log(result[0]); // ['Jack', 'Jack', index: 8, input: 'Authos: Jack, Alexander and Jacky', groups: undefined, indices: Array(2)] -------------------------------------------------------------------------------- /es2024/4.atomics-waitasync.js: -------------------------------------------------------------------------------- 1 | const arrayBuffer = new SharedArrayBuffer(1024); 2 | const arr = new Int32Array(arrayBuffer); 3 | 4 | Atomics.waitAsync(arr, 0 , 0 , 500); // { async: true, value: Promise {}} 5 | 6 | Atomics.notify(arr, 0); // { async: true, value: Promise {: 'ok'} } -------------------------------------------------------------------------------- /es2020/dynamic-import/dynamic-import.js: -------------------------------------------------------------------------------- 1 | const moduleSpecifier = './message.js'; 2 | import(moduleSpecifier) 3 | .then((module) => { 4 | module.default(); // Hello, default export 5 | module.sayGoodBye(); //Bye, named export 6 | }) 7 | .catch( err => console.log('loading error')); -------------------------------------------------------------------------------- /es2019/4.symbol-description.js: -------------------------------------------------------------------------------- 1 | console.log(Symbol('one').description); // one 2 | 3 | console.log(Symbol.for('one').description); // "one" 4 | 5 | console.log(Symbol('').description); // '' 6 | 7 | console.log(Symbol().description); // unefined 8 | 9 | console.log(Symbol.iterator.description); // "Symbol.iterator" -------------------------------------------------------------------------------- /es2015/11.generators.js: -------------------------------------------------------------------------------- 1 | function* myGenerator(i) { 2 | yield i + 10; 3 | yield i + 20; 4 | return i + 30; 5 | } 6 | 7 | const myGenObj = myGenerator(10); 8 | 9 | console.log(myGenObj.next().value); // 20 10 | console.log(myGenObj.next().value); // 30 11 | console.log(myGenObj.next().value); // 40 -------------------------------------------------------------------------------- /es2015/13.set.js: -------------------------------------------------------------------------------- 1 | let mySet = new Set() 2 | 3 | mySet.add(1); 4 | mySet.add(2); 5 | mySet.add(2); 6 | mySet.add('some text here'); 7 | mySet.add({one: 1, two: 2 , three: 3}); 8 | console.log(mySet); // Set [ 1, 2, 'some text here', {one: 1, two: 2 , three: 3} ] 9 | console.log(mySet.size) // 4 10 | console.log(mySet.has(2)); // true -------------------------------------------------------------------------------- /es2022/class fields/private-fields-methods.js: -------------------------------------------------------------------------------- 1 | class Employee { 2 | name = "John"; 3 | #age=35; 4 | constructor() { 5 | } 6 | 7 | #getAge() { 8 | return #age 9 | } 10 | 11 | } 12 | 13 | const employee = new Employee(); 14 | employee.name = "Jack"; 15 | employee.#age = 35; // Throws an error -------------------------------------------------------------------------------- /es2022/class fields/static-fields-methods.js: -------------------------------------------------------------------------------- 1 | class Employee{ 2 | name = "John"; 3 | static #employerName="Github" 4 | 5 | static #getEmployerName() { 6 | return #employerName 7 | } 8 | } 9 | const employee = new Employee(); 10 | employee.emp = "Jack"; 11 | employee.#employerName = 35; // Throws an error -------------------------------------------------------------------------------- /es2015/23.proper-tail-calls.js: -------------------------------------------------------------------------------- 1 | function factorial(n, acc = 1) { 2 | if (n === 0) { 3 | return acc 4 | } 5 | return factorial(n - 1, n * acc) 6 | } 7 | console.log(factorial(5)); //120 8 | 9 | console.log(factorial(10)); 10 | console.log(factorial(100)); 11 | console.log(factorial(1000)); 12 | console.log(factorial(10000)); -------------------------------------------------------------------------------- /es2019/8.function-tostring.js: -------------------------------------------------------------------------------- 1 | function sayHello(message) { 2 | let msg = message; 3 | //Print message 4 | console.log(`Hello, ${msg}`); 5 | } 6 | 7 | console.log(sayHello.toString()); 8 | // function sayHello(message) { 9 | // let msg = message; 10 | // //Print message 11 | // console.log(`Hello, ${msg}`); 12 | // } -------------------------------------------------------------------------------- /es2015/1.let-const-variables.js: -------------------------------------------------------------------------------- 1 | let a = 1; 2 | 3 | if (a === 1) { 4 | let a = 2; 5 | 6 | console.log(a); //2 7 | } 8 | 9 | console.log(a); //1 10 | 11 | const x = 1; 12 | 13 | if (x === 1) { 14 | const y = 2; // You cannot re-assign the value similar to let variable 15 | 16 | console.log(y); //2 17 | } 18 | 19 | console.log(x); //1 -------------------------------------------------------------------------------- /es2017/2.object-values.js: -------------------------------------------------------------------------------- 1 | // Object argument 2 | const countries = { 3 | IN: 'India', 4 | SG: 'Singapore', 5 | } 6 | Object.values(countries) // ['India', 'Singapore'] 7 | 8 | // Non-object argument 9 | console.log(Object.values(['India', 'Singapore'])); // ['India', 'Singapore'] 10 | console.log(Object.values('India')); // ['I', 'n', 'd', 'i', 'a'] -------------------------------------------------------------------------------- /es2020/promise-allsettled.js: -------------------------------------------------------------------------------- 1 | const promise1 = new Promise((resolve, reject) => setTimeout(() => resolve(100), 1000)); 2 | 3 | const promise2 = new Promise((resolve, reject) => setTimeout(reject, 1000)); 4 | 5 | Promise.allSettled([promise1, promise2]).then(data => console.log(data)); // [ Object { status: "fulfilled", value: 100}, Object { status: "rejected", reason: undefined}] -------------------------------------------------------------------------------- /es2017/7.trailing-commas.js: -------------------------------------------------------------------------------- 1 | // Params include trailing comma 2 | function func(a,b,) { // declaration 3 | console.log(a, b); 4 | } 5 | func(1,2,); // invocation 6 | 7 | // function call only contains a comma 8 | function func1(,) { // SyntaxError: missing formal parameter 9 | console.log('no args'); 10 | }; 11 | func1(,); // SyntaxError: expected expression, got ',' -------------------------------------------------------------------------------- /es2015/17.unicode.js: -------------------------------------------------------------------------------- 1 | let str = '𠮷'; 2 | 3 | // new string form 4 | console.log('\u{20BB7}'); // "𠮷" 5 | 6 | // new RegExp u mode 7 | console.log(new RegExp('\u{20BB7}', 'u')); 8 | console.log(/^.$/u.test(str)); // true 9 | 10 | //API methods 11 | console.log(str.codePointAt(0)); // 134071 12 | console.log(str.codePointAt(1)); // 57271 13 | 14 | console.log(String.fromCodePoint(134071)); // "𠮷" -------------------------------------------------------------------------------- /es2020/nullish-coalescing-operator.js: -------------------------------------------------------------------------------- 1 | let vehicle = { 2 | car: { 3 | name: "", 4 | speed: 0 5 | } 6 | }; 7 | 8 | console.log(vehicle.car.name || "Unknown"); // Unknown 9 | console.log(vehicle.car.speed || 90); // 90 10 | 11 | console.log(vehicle.car.name ?? "Unknown"); // ""(empty is valid case for name) 12 | console.log(vehicle.car.speed ?? 90); // 0(zero is valid case for speed) -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ECMAScript-cheatsheet", 3 | "version": "1.0.0", 4 | "description": "ECMAScript features with list of examples.", 5 | "main": "index.js", 6 | "author": "Sudheer Jonna ", 7 | "license": "MIT", 8 | "devDependencies": { 9 | "@babel/core": "^7.9.0", 10 | "@babel/node": "^7.8.7", 11 | "@babel/preset-env": "^7.9.5" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /es2019/2.object-fromentries.js: -------------------------------------------------------------------------------- 1 | // Iterable to objects 2 | const arr = [ ['a', '1'], ['b', '2'], ['c', '3'] ]; 3 | const obj = Object.fromEntries(arr); 4 | console.log(obj); // { a: "1", b: "2", c: "3" } 5 | 6 | // URLParams 7 | const paramsString = 'param1=foo¶m2=baz'; 8 | const searchParams = new URLSearchParams(paramsString); 9 | 10 | Object.fromEntries(searchParams); // => {param1: "foo", param2: "baz"} -------------------------------------------------------------------------------- /es2022/error-cause.js: -------------------------------------------------------------------------------- 1 | function processData(arrayData) { 2 | return arrayData.map(data => { 3 | try { 4 | const json = JSON.parse(data); 5 | return json; 6 | } catch (err) { 7 | throw new Error( 8 | `Data processing failed`, 9 | {cause: err} 10 | ); 11 | } 12 | }); 13 | } 14 | 15 | processData({"one": 1, "two": 2}); -------------------------------------------------------------------------------- /es2020/string.matchAll.js: -------------------------------------------------------------------------------- 1 | const regex = /t(e)(st(\d?))/g; 2 | const string = 'test1test2'; 3 | const matchesIterator = string.matchAll(regex); 4 | Array.from(matchesIterator, result => console.log(result)); // ["test1", "e", "st1", "1", index: 0, input: "test1test2", groups: undefined] 5 | //["test2", "e", "st2", "2", index: 5, input: "test1test2", groups: undefined] -------------------------------------------------------------------------------- /es2021/promise-any/promise-any-error.js: -------------------------------------------------------------------------------- 1 | (async () => { 2 | try { 3 | const output = await Promise.any([ 4 | Promise.reject('Error 1'), 5 | Promise.reject('Error 2'), 6 | Promise.reject('Error 3'), 7 | ]); 8 | console.log(`Output: ${output}`); 9 | } catch (err) { 10 | console.log(`Error: ${err.errors}`); 11 | } 12 | })(); 13 | // Error: Error1,Error2,Error3 -------------------------------------------------------------------------------- /es2021/promise-any/promise-any.js: -------------------------------------------------------------------------------- 1 | let promise1 = new Promise((resolve) => setTimeout(resolve, 100, 'Resolves after 100ms')); 2 | let promise2 = new Promise((resolve) => setTimeout(resolve, 200, 'Resolves after 200ms')); 3 | let promise3 = new Promise((resolve, reject) => setTimeout(reject, 0) ); 4 | 5 | let promises = [promise1, promise2, promise3]; 6 | 7 | Promise.any(promises) 8 | .then( value => console.log(value)); // Resolves after 100ms -------------------------------------------------------------------------------- /es2018/1.async-iterators.js: -------------------------------------------------------------------------------- 1 | const arr = ['a', 'b', 'c', 'd']; 2 | const syncIterator = arr[Symbol.iterator](); 3 | 4 | console.log(syncIterator.next()); //{value: a, done: false} 5 | console.log(syncIterator.next()); //{value: b, done: false} 6 | console.log(syncIterator.next()); //{value: c, done: false} 7 | console.log(syncIterator.next()); //{value: d, done: false} 8 | console.log(syncIterator.next()); //{value: undefined, done: true} -------------------------------------------------------------------------------- /es2021/weakref/finalizer.js: -------------------------------------------------------------------------------- 1 | // Create new FinalizationRegistry: 2 | const reg = new FinalizationRegistry((val) => { 3 | console.log(val); 4 | }); 5 | 6 | (() => { 7 | // Create new object: 8 | const obj = {} 9 | 10 | // Register finalizer for the "obj" as first argument and value for callback function as second argument: 11 | reg.register(obj, 'obj has been garbage-collected.') 12 | })(); -------------------------------------------------------------------------------- /es2020/optional-chaining.js: -------------------------------------------------------------------------------- 1 | let vehicle = { 2 | }; 3 | 4 | let vehicle1 = { 5 | car: { 6 | name: 'ABC', 7 | speed: 90 8 | } 9 | }; 10 | 11 | 12 | console.log(vehicle.car?.name); // Undefined 13 | console.log(vehicle.car?.speed); // Undefined 14 | 15 | console.log(vehicle1.car?.name); // ABC 16 | console.log(vehicle1.car?.speed); // 90 17 | 18 | console.log(vehicle.car?.name ?? "Unknown"); // Unknown 19 | console.log(vehicle.car?.speed ?? 90); // 90 -------------------------------------------------------------------------------- /es2015/14.weakset.js: -------------------------------------------------------------------------------- 1 | let myUserSet = new WeakSet(); 2 | 3 | let john = { name: "John" }; 4 | let rocky = { name: "Rocky" }; 5 | let alex = { name: "Alex" }; 6 | let nick = { name: "Nick" }; 7 | 8 | myUserSet.add(john); 9 | myUserSet.add(rocky); 10 | myUserSet.add(john); 11 | myUserSet.add(nick); 12 | 13 | console.log(myUserSet.has(john)); // true 14 | console.log(myUserSet.has(alex)); // false 15 | console.log(myUserSet.delete(nick)); 16 | console.log(myUserSet.has(nick)); // false 17 | 18 | john = null; -------------------------------------------------------------------------------- /es2015/16.weakmap.js: -------------------------------------------------------------------------------- 1 | var weakMap = new WeakMap(); 2 | 3 | var obj1 = {} 4 | var obj2 = {} 5 | 6 | 7 | weakMap.set(obj1, 1); 8 | weakMap.set(obj2, 2); 9 | weakMap.set({}, {"four": 4}); 10 | 11 | console.log(weakMap.get(obj2)); // 2 12 | console.log(weakMap.has({})); // return false even though empty object exists as key. Because the keys have different references 13 | 14 | delete obj2; 15 | console.log(weakMap.get(obj2)); // 2 16 | weakMap.delete(obj1) 17 | console.log(weakMap.get(obj1)); //undefined -------------------------------------------------------------------------------- /es2019/7.array-stable-sort.js: -------------------------------------------------------------------------------- 1 | const users = [ 2 | { name: "Albert", age: 30 }, 3 | { name: "Bravo", age: 30 }, 4 | { name: "Colin", age: 30 }, 5 | { name: "Rock", age: 50 }, 6 | { name: "Sunny", age: 50 }, 7 | { name: "Talor", age: 50 }, 8 | { name: "John", age: 25 }, 9 | { name: "Kindo", age: 25 }, 10 | { name: "Lary", age: 25 }, 11 | { name: "Minjie", age: 25 }, 12 | { name: "Nova", age: 25 } 13 | ] 14 | users.sort((a, b) => a.age - b.age); -------------------------------------------------------------------------------- /es2015/20.promises.js: -------------------------------------------------------------------------------- 1 | const promise = new Promise(function(resolve, reject) { 2 | setTimeout(() => resolve(1), 1000); 3 | }); 4 | 5 | promise.then(function(result) { 6 | 7 | console.log(result); // 1 8 | return result * 2; 9 | 10 | }).then(function(result) { 11 | 12 | console.log(result); // 2 13 | return result * 3; 14 | 15 | }).then(function(result) { 16 | 17 | console.log(result); // 6 18 | return result * 4; 19 | 20 | }).catch(function(error){ 21 | console.log(error); 22 | }); -------------------------------------------------------------------------------- /es2015/4.enhanced-object-literals.js: -------------------------------------------------------------------------------- 1 | // Property shorthand 2 | var a = 1, b = 2, c = 3; 3 | obj = { 4 | a, 5 | b, 6 | c 7 | }; 8 | console.log(obj); 9 | 10 | // Method shorthand 11 | var calculation = { 12 | sum(a, b) { return a + b; }, 13 | multiply(a, b) { return a * b; } 14 | }; 15 | 16 | console.log( calculation.sum(5, 3) ); // 15 17 | console.log( calculation.multiply(5, 3) ); // 15 18 | 19 | // Computed Property Names 20 | const 21 | key = 'three', 22 | computedObj = { 23 | one: 1, 24 | two: 2, 25 | [key]: 3 26 | }; -------------------------------------------------------------------------------- /es2020/globalThis.js: -------------------------------------------------------------------------------- 1 | var getGlobal = function () { 2 | if (typeof self !== 'undefined') { return self; } 3 | if (typeof window !== 'undefined') { return window; } 4 | if (typeof global !== 'undefined') { return global; } 5 | throw new Error('unable to locate global object'); 6 | }; 7 | 8 | var globals = getGlobal(); 9 | 10 | if (typeof globals.setTimeout !== 'function') { 11 | console.log('no setTimeout in this environment or runtime'); 12 | } 13 | 14 | if (typeof globalThis.setTimeout !== 'function') { 15 | console.log('no setTimeout in this environment or runtime'); 16 | } -------------------------------------------------------------------------------- /es2017/3.object-entries.js: -------------------------------------------------------------------------------- 1 | // Object argument 2 | const countries = { 3 | IN: 'India', 4 | SG: 'Singapore', 5 | } 6 | Object.entries(countries) // [["IN", "India"], ["SG", "Singapore"]] 7 | 8 | // Non-object argument 9 | const countriesArr = ['India', 'Singapore']; 10 | console.log(Object.entries(countriesArr)); // [ ['0', 'India'], ['1', 'Singapore']] 11 | 12 | const country = 'India'; 13 | console.log(Object.entries(country)); // [["0", "I"], ["1", "n"], ["2", "d"], ["3", "i"], ["4", "a"]] 14 | 15 | console.log(Object.entries(100)); // [], an empty array for any primitive type because it won't have any own properties -------------------------------------------------------------------------------- /es2017/5.string-padding.js: -------------------------------------------------------------------------------- 1 | // Pad start 2 | const cardNumber = '01234567891234'; 3 | const lastFourDigits = cardNumber.slice(-4); 4 | const maskedCardNumber = lastFourDigits.padStart(cardNumber.length, '*'); 5 | console.log(maskedCardNumber); // expected output: "**********1234" 6 | 7 | // Pad End 8 | const label1 = "Name"; 9 | const label2 = "Phone Number"; 10 | const value1 = "John" 11 | const value2 = "(222)-333-3456"; 12 | 13 | console.log((label1 + ': ').padEnd(20, ' ') + value1); 14 | console.log(label2 + ": " + value2); // Name: John 15 | // Phone Number: (222)-333-3456 -------------------------------------------------------------------------------- /es2024/3.well-formed-unicode-strings.js: -------------------------------------------------------------------------------- 1 | const str1 = "Hello World \uD815"; 2 | const str2 = "Welcome to ES2024"; 3 | const str3 = "Welcome to ES2024 😀"; 4 | 5 | console.log(str1.isWellFormed()); // false 6 | console.log(str2.isWellFormed()); // true 7 | console.log(str2.isWellFormed()); // true 8 | 9 | console.log(str1.toWellFormed()); // Hello World � 10 | console.log(str2.toWellFormed()); // Welcome to ES2024 11 | 12 | const url = "https://somedomain.com/query=\uD423"; 13 | try { 14 | console.log(encodeURI(url.toWellFormed())); // https://somedomain.com/query=%ED%90%A3 15 | } catch (e) { 16 | console.log('Error:', e.message); 17 | } -------------------------------------------------------------------------------- /es2015/15.map.js: -------------------------------------------------------------------------------- 1 | let typeMap = new Map(); 2 | 3 | var keyObj = {'one': 1} 4 | 5 | typeMap.set('10', 'string'); // a string key 6 | typeMap.set(10, 'number'); // a numeric key 7 | typeMap.set(true, 'boolean'); // a boolean key 8 | typeMap.set(keyObj, 'object'); // an object key 9 | 10 | 11 | console.log(typeMap.get(10) ); // number 12 | console.log(typeMap.get('10') ); // string 13 | console.log(typeMap.get(keyObj)) // object 14 | console.log(typeMap.get({'one': 1})) // undefined 15 | 16 | console.log(typeMap.size ); // 4 17 | 18 | for(let item of typeMap) { 19 | console.log(item); 20 | } 21 | 22 | 23 | for(let item in typeMap) { 24 | console.log(item); 25 | } -------------------------------------------------------------------------------- /es2019/1.array-flat-flatmap.js: -------------------------------------------------------------------------------- 1 | // flat 2 | const numberArray = [[1, 2], [[3], 4], [5, 6]]; 3 | const charArray = ['a', , 'b', , , ['c', 'd'], 'e']; 4 | const flattenedArrOneLevel = numberArray.flat(1); 5 | const flattenedArrTwoLevel = numberArray.flat(2); 6 | const flattenedCharArrOneLevel = charArray.flat(1); 7 | 8 | console.log(flattenedArrOneLevel); // [1, 2, [3], 4, 5, 6] 9 | console.log(flattenedArrTwoLevel); // [1, 2, 3, 4, 5, 6] 10 | console.log(flattenedCharArrOneLevel); // ['a', 'b', 'c', 'd', 'e'] 11 | 12 | // flatMap 13 | const numberArray1 = [[1], [2], [3], [4], [5]]; 14 | 15 | console.log(numberArray1.flatMap(value => [value * 10])); // [10, 20, 30, 40, 50] -------------------------------------------------------------------------------- /es2018/2.object-rest-spread-operators.js: -------------------------------------------------------------------------------- 1 | // Rest parameters 2 | 3 | function myfunc(p1, p2, ...p3) { 4 | console.log(p1, p2, p3); // 1, 2, [3, 4, 5, 6] 5 | } 6 | myfunc(1, 2, 3, 4, 5, 6); 7 | 8 | // spread operator 9 | const myArray = [10, 5, 25, -100, 200, -200]; 10 | console.log( Math.max(...myArray) ); // 200 11 | 12 | // Rest parameters for objects 13 | function myfunc1({ a, ...x }) { 14 | console.log(a, x); // 1, { b: 2, c: 3, d:4 } 15 | } 16 | myfunc1({ 17 | a: 1, 18 | b: 2, 19 | c: 3, 20 | d: 4 21 | }); 22 | 23 | // spread operator for objects 24 | const myObject = { a: 1, b: 2, c: 3, d:4 }; 25 | const myNewObject = { ...myObject, e: 5 }; // { a: 1, b: 2, c: 3, d: 4, e: 5 } -------------------------------------------------------------------------------- /es2023/4.change-array-by-copy.js: -------------------------------------------------------------------------------- 1 | const numbers = [1, 3, 2, 4, 5]; 2 | 3 | // toReversed 4 | const reversedArray = numbers.toReversed(); 5 | console.log(reversedArray); // [5, 4, 2, 3, 1] 6 | console.log(numbers); // [1, 3, 2, 4, 5] 7 | 8 | // toSorted 9 | const sortedArray = numbers.toSorted(); 10 | console.log(sortedArray); // [1, 2, 3, 4, 5] 11 | console.log(numbers); // [1, 3, 2, 4, 5] 12 | 13 | // toSpliced 14 | const splicedArray = numbers.toSpliced(1, 3); 15 | console.log(splicedArray); // [1, 5] 16 | console.log(numbers); // [1, 3, 2, 4, 5] 17 | 18 | // with 19 | const replaceWithArray = numbers.with(2, 10); 20 | console.log(replaceWithArray); // [1, 3, 10, 4, 5] 21 | console.log(numbers); // [1, 3, 2, 4, 5] -------------------------------------------------------------------------------- /es2015/10.iterators-forof.js: -------------------------------------------------------------------------------- 1 | const collection = { 2 | one: 1, 3 | two: 2, 4 | three: 3, 5 | [Symbol.iterator]() { 6 | const values = Object.keys(this); 7 | let i = 0; 8 | return { 9 | next: () => { 10 | return { 11 | value: this[values[i++]], 12 | done: i > values.length 13 | } 14 | } 15 | }; 16 | } 17 | }; 18 | 19 | const iterator = collection[Symbol.iterator](); 20 | 21 | console.log(iterator.next()); // → {value: 1, done: false} 22 | console.log(iterator.next()); // → {value: 2, done: false} 23 | console.log(iterator.next()); // → {value: 3, done: false} 24 | console.log(iterator.next()); // → {value: undefined, done: true} 25 | 26 | for (const value of collection) { 27 | console.log(value); 28 | } -------------------------------------------------------------------------------- /es2015/18.symbols.js: -------------------------------------------------------------------------------- 1 | //1. Object properties 2 | let id = Symbol("id"); 3 | let user = { 4 | name: "John", 5 | age: 40, 6 | [id]: 111 7 | }; 8 | 9 | for (let key in user) { 10 | console.log(key); // name, age without symbols 11 | } 12 | 13 | console.log(JSON.stringify(user)); // {"name":"John", "age": 40} 14 | console.log(Object.keys(user)); // ["name", "age"] 15 | 16 | console.log( "User Id: " + user[id] ); // Direct access by the symbol works 17 | 18 | //2. Unique constants 19 | const logLevels = { 20 | DEBUG: Symbol('debug'), 21 | INFO: Symbol('info'), 22 | WARN: Symbol('warn'), 23 | ERROR: Symbol('error'), 24 | }; 25 | console.log(logLevels.DEBUG, 'debug message'); 26 | console.log(logLevels.INFO, 'info message'); 27 | 28 | //3. Equality Checks 29 | 30 | console.log(Symbol('foo') === Symbol('foo')); // false 31 | console.log(Symbol.for('foo') === Symbol.for('foo')); // true -------------------------------------------------------------------------------- /.github/workflows/main.yaml: -------------------------------------------------------------------------------- 1 | name: Documentation 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | 7 | env: 8 | FILE_NAME: ECMAScript-features 9 | 10 | jobs: 11 | convert_via_pandoc: 12 | runs-on: ubuntu-18.04 13 | steps: 14 | - uses: actions/checkout@v2 15 | 16 | - name: Create output directory 17 | run: | 18 | mkdir output # create output dir 19 | 20 | - name: Create PDF 21 | uses: docker://pandoc/latex:2.9 22 | with: 23 | args: --pdf-engine=xelatex --output=output/${{env.FILE_NAME}}.pdf README.md 24 | 25 | - name: Create epub 26 | uses: docker://pandoc/latex:2.9 27 | with: 28 | args: --output=output/${{env.FILE_NAME}}.epub README.md 29 | 30 | - name: Upload 31 | uses: actions/upload-artifact@master 32 | with: 33 | name: output 34 | path: output -------------------------------------------------------------------------------- /es2015/3.classes.js: -------------------------------------------------------------------------------- 1 | // Class declarations 2 | class Square { 3 | constructor(length) { 4 | this.length = length; 5 | } 6 | 7 | get area() { 8 | return this.length * this.length; 9 | } 10 | 11 | set area(value) { 12 | this.area = value; 13 | } 14 | } 15 | 16 | // Class expression 17 | const square = class Square { 18 | constructor(length) { 19 | this.length = length; 20 | } 21 | 22 | get area() { 23 | return this.length * this.length; 24 | } 25 | 26 | set area(value) { 27 | this.area = value; 28 | } 29 | } 30 | 31 | // Inheritance 32 | class Vehicle { 33 | constructor(name) { 34 | this.name = name; 35 | } 36 | 37 | start() { 38 | console.log(`${this.name} vehicle started`); 39 | } 40 | } 41 | 42 | class Car extends Vehicle { 43 | start() { 44 | console.log(`${this.name} car started`); 45 | } 46 | } 47 | 48 | const car = new Car('BMW'); 49 | console.log(car.start()); // BMW car started -------------------------------------------------------------------------------- /es2015/12.modules.js: -------------------------------------------------------------------------------- 1 | //Export Statement 2 | // Named exports 3 | 4 | // module "my-module.js" 5 | 6 | const PI = Math.PI; 7 | 8 | function add(...args) { 9 | return args.reduce((num, tot) => tot + num); 10 | } 11 | 12 | function multiply(...args) { 13 | return args.reduce((num, tot) => tot * num); 14 | } 15 | 16 | // private function 17 | function print(msg) { 18 | console.log(msg); 19 | } 20 | 21 | export { PI, add, multiply }; 22 | 23 | // Default exports 24 | // module "my-module.js" 25 | 26 | export default function add(...args) { 27 | return args.reduce((num, tot) => tot + num); 28 | } 29 | 30 | //Import Statement 31 | 32 | // 1. Import an entire module's contents 33 | import * as name from "my-module"; 34 | 35 | //2.Import a single export from a module 36 | import { export1 } from "my-module"; 37 | 38 | //3.Import multiple exports from a module 39 | import { export1 , export2 } from "my-module"; 40 | 41 | //4.Import default export from a module 42 | import defaultExport from "my-module"; 43 | 44 | //5.Import an export with an alias 45 | import { export1 as alias1 } from "my-module"; 46 | 47 | -------------------------------------------------------------------------------- /es2015/19.proxies.js: -------------------------------------------------------------------------------- 1 | const target = { 2 | name: "John", 3 | age: 3 4 | }; 5 | 6 | const handler = { 7 | get: function(target, prop) { 8 | return prop in target ? 9 | target[prop] : 10 | `${prop} does not exist'; 11 | } 12 | }; 13 | 14 | const user = new Proxy(target, handler); 15 | console.log(user.name); // John 16 | console.log(user.age); // 3 17 | console.log(user.gender); // gender does not exist 18 | 19 | // validations 20 | 21 | let ageValidator = { 22 | set: function(obj, prop, value) { 23 | if (prop === 'age') { 24 | if (!Number.isInteger(value)) { 25 | throw new TypeError('The age is not an integer'); 26 | } 27 | if (value > 200) { 28 | throw new RangeError('Invalid age'); 29 | } 30 | } 31 | 32 | obj[prop] = value; // The default behavior to store the value 33 | 34 | return true; // Indicate success 35 | } 36 | }; 37 | 38 | const person = new Proxy({}, ageValidator); 39 | 40 | person.age = 30; 41 | console.log(person.age); // 30 42 | person.age = 'old'; // Throws an exception 43 | person.age = 200; // Throws an exception -------------------------------------------------------------------------------- /es2017/6.shared-memory-atomics.js: -------------------------------------------------------------------------------- 1 | // Atomic operations 2 | const sharedMemory = new SharedArrayBuffer(1024); 3 | const sharedArray = new Uint8Array(sharedMemory); 4 | sharedArray[0] = 10; 5 | 6 | Atomics.add(sharedArray, 0, 20); 7 | console.log(Atomics.load(sharedArray, 0)); // 30 8 | 9 | Atomics.sub(sharedArray, 0, 10); 10 | console.log(Atomics.load(sharedArray, 0)); // 20 11 | 12 | Atomics.and(sharedArray, 0, 5); 13 | console.log(Atomics.load(sharedArray, 0)); // 4 14 | 15 | Atomics.or(sharedArray, 0, 1); 16 | console.log(Atomics.load(sharedArray, 0)); // 5 17 | 18 | Atomics.xor(sharedArray, 0, 1); 19 | console.log(Atomics.load(sharedArray, 0)); // 4 20 | 21 | Atomics.store(sharedArray, 0, 10); // 10 22 | 23 | Atomics.compareExchange(sharedArray, 0, 5, 10); 24 | console.log(Atomics.load(sharedArray, 0)); // 10 25 | 26 | Atomics.exchange(sharedArray, 0, 10); 27 | console.log(Atomics.load(sharedArray, 0)); //10 28 | 29 | Atomics.isLockFree(1); // true 30 | 31 | // waiting to be notified 32 | const sharedMemory = new SharedArrayBuffer(1024); 33 | const sharedArray = new Int32Array(sharedMemory); 34 | 35 | Atomics.wait(sharedArray, 0, 10); 36 | console.log(sharedArray[0]); // 100 37 | 38 | Atomics.store(sharedArray, 0, 100); 39 | Atomics.notify(sharedArray, 0, 1); -------------------------------------------------------------------------------- /es2024/1.groupby-objects-maps.js: -------------------------------------------------------------------------------- 1 | const persons = [ 2 | {name:"John", age:70}, 3 | {name:"Kane", age:5}, 4 | {name:"Jack", age:50}, 5 | {name:"Rambo", age:15} 6 | ]; 7 | 8 | // Callback function to categorize people based on age 9 | function callbackFunc({ age }) { 10 | if(age >= 60) { 11 | return "senior"; 12 | } else if(age > 17 && age < 60) { 13 | return "adult"; 14 | } 15 | else { 16 | return "kid"; 17 | } 18 | } 19 | 20 | //Object groupBy 21 | const result = Object.groupBy(persons, callbackFunc); 22 | 23 | console.log("Kids: "); 24 | for (let [x,y] of result.kid.entries()) { 25 | console.log(y.name + " " + y.age); 26 | } 27 | 28 | console.log("Adults: "); 29 | for (let [x,y] of result.adult.entries()) { 30 | console.log(y.name + " " + y.age); 31 | } 32 | 33 | console.log("Seniors: "); 34 | for (let [x,y] of result.senior.entries()) { 35 | console.log(y.name + " " + y.age); 36 | } 37 | 38 | //Map groupBy 39 | const result1 = Map.groupBy(persons, callbackFunc); 40 | 41 | console.log("Kids: "); 42 | for (let x of result1.get("kid")) { 43 | console.log(x.name + " " + x.age); 44 | } 45 | 46 | console.log("Adults: "); 47 | for (let x of result1.get("adult")) { 48 | console.log(x.name + " " + x.age); 49 | } 50 | 51 | console.log("Seniors: "); 52 | for (let x of result1.get("senior")) { 53 | console.log(x.name + " " + x.age); 54 | } -------------------------------------------------------------------------------- /es2015/21.reflect.js: -------------------------------------------------------------------------------- 1 | // Creating objects using Reflect.construct() 2 | class User { 3 | constructor(firstName, lastName) { 4 | this.firstName = firstName; 5 | this.lastName = lastName; 6 | } 7 | get fullName() { 8 | return `${this.firstName} ${this.lastName}`; 9 | } 10 | }; 11 | 12 | let args = ['John', 'Emma']; 13 | 14 | let john = Reflect.construct( 15 | User, 16 | args 17 | ); 18 | 19 | console.log(john instanceof User); 20 | console.log(john.fullName); // John Doe 21 | 22 | //Calling a function using Reflect.apply() 23 | const max = Reflect.apply(Math.max, Math, [100, 200, 300]); 24 | console.log(max); 25 | 26 | //Defining a property using Reflect.defineProperty() 27 | class User { 28 | constructor(firstName, lastName) { 29 | this.firstName = firstName; 30 | this.lastName = lastName; 31 | } 32 | get fullName() { 33 | return `${this.firstName} ${this.lastName}`; 34 | } 35 | }; 36 | 37 | let john = new User('John', 'Resig'); 38 | 39 | if (Reflect.defineProperty(john, 'age', { 40 | writable: true, 41 | configurable: true, 42 | enumerable: false, 43 | value: 33, 44 | })) { 45 | console.log(john.age); 46 | } else { 47 | console.log('Cannot define the age property on the user object.'); 48 | 49 | } 50 | 51 | //Delete property using Reflect.deleteProperty() 52 | const user = { 53 | name: 'John', 54 | age: 33 55 | }; 56 | 57 | console.log(Reflect.deleteProperty(user, 'age')); // true 58 | console.log(user.age); // undefined 59 | 60 | //Get property of an object using Reflect.get() 61 | const user = { 62 | name: 'John', 63 | age: 33 64 | }; 65 | 66 | console.log(Reflect.get(user, 'age')); // 33 -------------------------------------------------------------------------------- /es2015/2.arrow-functions.js: -------------------------------------------------------------------------------- 1 | // Arrow functions were introduced in ES6 (ES2015) as a new syntax for writing JavaScript functions. 2 | // They provide a more concise syntax and lexical 'this' binding. 3 | 4 | // 1. Single parameter and single statement 5 | // No parentheses needed for single parameter 6 | const message = name => console.log(`Hello, ${name}!`); 7 | message("Sudheer"); // Hello, Sudheer! 8 | 9 | // 2. Multiple parameters and single statement 10 | // Parentheses required for multiple parameters 11 | const multiply = (x, y) => x * y; 12 | console.log(multiply(2, 3)); // 6 13 | 14 | // 3. Single parameter and multiple statements 15 | // Curly braces required for multiple statements 16 | const even = number => { 17 | if(number % 2 === 0) { 18 | console.log("Even"); 19 | } else { 20 | console.log("Odd"); 21 | } 22 | }; 23 | even(5); // Odd 24 | 25 | // 4. Multiple parameters and multiple statements 26 | const divide = (x, y) => { 27 | if(y !== 0) { 28 | return x / y; 29 | } 30 | return "Cannot divide by zero"; 31 | }; 32 | console.log(divide(100, 5)); // 20 33 | console.log(divide(100, 0)); // Cannot divide by zero 34 | 35 | // 5. No parameter and single statement 36 | // Empty parentheses required when no parameters 37 | const greet = () => console.log('Hello World!'); 38 | greet(); // Hello World! 39 | 40 | // 6. Lexical 'this' binding 41 | // Arrow functions don't have their own 'this' context 42 | const person = { 43 | name: 'John', 44 | regularFunction: function() { 45 | console.log(this.name); // 'John' 46 | }, 47 | arrowFunction: () => { 48 | console.log(this.name); // undefined (or global 'name' if defined) 49 | } 50 | }; 51 | // person.regularFunction(); 52 | // person.arrowFunction(); 53 | -------------------------------------------------------------------------------- /es2020/bigint.js: -------------------------------------------------------------------------------- 1 | // BigInt is a built-in object introduced in ES2020 (ES11) that provides a way to represent 2 | // integers larger than 2^53 - 1, which is the largest number JavaScript can reliably represent 3 | // with the Number primitive. 4 | 5 | // 1. Limitations of the current number system 6 | console.log("Maximum safe integer in JavaScript:", Number.MAX_SAFE_INTEGER); // 9007199254740991 7 | const max = Number.MAX_SAFE_INTEGER; 8 | console.log("max + 1:", max + 1); // 9007199254740992 9 | console.log("max + 2:", max + 2); // 9007199254740992 (Notice this is the same as max + 1, showing precision loss) 10 | 11 | // 2. BigInt representation - three ways to create BigInts 12 | // a) Using the 'n' suffix 13 | const bigInt = 9007199254740991n; 14 | console.log("Using 'n' suffix:", bigInt); 15 | 16 | // b) Using BigInt constructor with a number 17 | const bigIntFromNumber = BigInt(9007199254740991); 18 | console.log("Using BigInt constructor with number:", bigIntFromNumber); // 9007199254740991n 19 | 20 | // c) Using BigInt constructor with a string (useful for very large numbers) 21 | const bigIntFromString = BigInt("9007199254740991"); 22 | console.log("Using BigInt constructor with string:", bigIntFromString); // 9007199254740991n 23 | 24 | // 3. Type checking with typeof 25 | console.log("\nType checking:"); 26 | console.log("typeof 1:", typeof 1); // number 27 | console.log("typeof 1n:", typeof 1n); // bigint 28 | console.log("typeof BigInt('1'):", typeof BigInt('1')); // bigint 29 | 30 | // 4. Arithmetic operators with BigInt 31 | console.log("\nArithmetic operations with BigInt:"); 32 | const previousMaxNum = BigInt(Number.MAX_SAFE_INTEGER); 33 | console.log("Addition:", previousMaxNum + 2n); // 9007199254740993n (this was not possible with regular numbers) 34 | console.log("Subtraction:", previousMaxNum - 2n); // 9007199254740989n 35 | console.log("Multiplication:", previousMaxNum * 2n); // 18014398509481982n 36 | console.log("Division:", previousMaxNum / 2n); // 4503599627370495n 37 | console.log("Modulus:", previousMaxNum % 2n); // 1n 38 | 39 | // 5. Comparison operations 40 | console.log("\nComparison operations:"); 41 | console.log("1n === 1:", 1n === 1); // false (strict equality checks type) 42 | console.log("1n === BigInt(1):", 1n === BigInt(1)); // true (same type and value) 43 | console.log("1n == 1:", 1n == 1); // true (loose equality converts types) 44 | 45 | // 6. Limitations of BigInt 46 | console.log("\nLimitations of BigInt:"); 47 | // Cannot mix BigInt and other types in operations 48 | try { 49 | console.log(1n + 1); 50 | } catch (error) { 51 | console.log("Error when mixing types:", error.message); // Cannot mix BigInt and other types 52 | } 53 | 54 | // No support for decimal points 55 | console.log("BigInt(1.5):", BigInt(1.5)); // 1n (decimal part is truncated) 56 | 57 | // 7. Use cases for BigInt 58 | console.log("\nUse cases for BigInt:"); 59 | console.log("- Working with large integers beyond Number.MAX_SAFE_INTEGER"); 60 | console.log("- Cryptography and security applications"); 61 | console.log("- High-precision timestamps"); 62 | console.log("- Scientific calculations requiring high precision"); 63 | -------------------------------------------------------------------------------- /es2015/6.destructuring.js: -------------------------------------------------------------------------------- 1 | // Destructuring is a JavaScript expression that allows us to extract data from arrays, 2 | // objects, and maps and set them into new, distinct variables. 3 | // Introduced in ES6 (ES2015), it provides a more concise and readable way to extract values. 4 | 5 | // 1. OBJECT DESTRUCTURING 6 | console.log("--- OBJECT DESTRUCTURING ---"); 7 | 8 | // Basic object destructuring 9 | const user = { firstName: 'John', lastName: 'Kary', age: 30 }; 10 | const { firstName, lastName } = user; 11 | console.log("Basic:", firstName, lastName); // John Kary 12 | 13 | // Assigning to new variable names 14 | const { firstName: fName, lastName: lName } = user; 15 | console.log("New variable names:", fName, lName); // John Kary 16 | 17 | // Default values (used when property doesn't exist) 18 | const { firstName: first, middleName = 'M', age } = user; 19 | console.log("With defaults:", first, middleName, age); // John M 30 20 | 21 | // Nested object destructuring 22 | const employee = { 23 | id: 1001, 24 | name: { 25 | first: 'Jane', 26 | last: 'Smith' 27 | }, 28 | department: 'Engineering' 29 | }; 30 | const { name: { first: employeeFirst, last: employeeLast }, department } = employee; 31 | console.log("Nested:", employeeFirst, employeeLast, department); // Jane Smith Engineering 32 | 33 | // Rest operator in object destructuring 34 | const { firstName: userFirst, ...rest } = user; 35 | console.log("Rest operator:", userFirst, rest); // John { lastName: 'Kary', age: 30 } 36 | 37 | // 2. ARRAY DESTRUCTURING 38 | console.log("\n--- ARRAY DESTRUCTURING ---"); 39 | 40 | // Basic array destructuring 41 | const colors = ['red', 'green', 'blue']; 42 | const [first1, second, third] = colors; 43 | console.log("Basic:", first1, second, third); // red green blue 44 | 45 | // Skipping elements 46 | const [red, , blue] = colors; 47 | console.log("Skipping elements:", red, blue); // red blue 48 | 49 | // Default values 50 | const [primary, secondary, tertiary, quaternary = 'yellow'] = colors; 51 | console.log("With defaults:", primary, secondary, tertiary, quaternary); // red green blue yellow 52 | 53 | // Swapping variables without a temporary variable 54 | let a = 1; 55 | let b = 2; 56 | [a, b] = [b, a]; 57 | console.log("Swapped variables:", a, b); // 2 1 58 | 59 | // Rest operator in array destructuring 60 | const [first2, ...remaining] = colors; 61 | console.log("Rest operator:", first2, remaining); // red ['green', 'blue'] 62 | 63 | // 3. FUNCTION PARAMETER DESTRUCTURING 64 | console.log("\n--- FUNCTION PARAMETER DESTRUCTURING ---"); 65 | 66 | // Object parameter destructuring 67 | function printUserInfo({ firstName, lastName, age = 25 }) { 68 | console.log(`Name: ${firstName} ${lastName}, Age: ${age}`); 69 | } 70 | printUserInfo(user); // Name: John Kary, Age: 30 71 | 72 | // Array parameter destructuring 73 | function printRGB([r, g, b]) { 74 | console.log(`RGB: ${r}, ${g}, ${b}`); 75 | } 76 | printRGB(colors); // RGB: red, green, blue 77 | 78 | // 4. PRACTICAL USE CASES 79 | console.log("\n--- PRACTICAL USE CASES ---"); 80 | 81 | // Returning multiple values from a function 82 | function getUserInfo() { 83 | return { 84 | id: 123, 85 | name: 'Alice', 86 | email: 'alice@example.com' 87 | }; 88 | } 89 | const { id, name, email } = getUserInfo(); 90 | console.log("Multiple return values:", id, name, email); // 123 Alice alice@example.com 91 | 92 | // Destructuring in loops 93 | const users = [ 94 | { id: 1, name: 'John' }, 95 | { id: 2, name: 'Jane' } 96 | ]; 97 | for (const { id: userId, name: userName } of users) { 98 | console.log(`User: ${userId} - ${userName}`); 99 | } 100 | // User: 1 - John 101 | // User: 2 - Jane 102 | -------------------------------------------------------------------------------- /es2025/1.set-methods.js: -------------------------------------------------------------------------------- 1 | // Set Methods - New in ES2025 2 | // ES2025 introduces several new methods to the Set prototype that make working with sets more convenient 3 | 4 | // Create some sample sets 5 | const set1 = new Set([1, 2, 3, 4, 5]); 6 | const set2 = new Set([3, 4, 5, 6, 7]); 7 | const set3 = new Set([5, 6, 7, 8, 9]); 8 | 9 | console.log("Original sets:"); 10 | console.log("set1:", [...set1]); // [1, 2, 3, 4, 5] 11 | console.log("set2:", [...set2]); // [3, 4, 5, 6, 7] 12 | console.log("set3:", [...set3]); // [5, 6, 7, 8, 9] 13 | 14 | // 1. Set.prototype.union() 15 | // Returns a new Set containing all elements from both sets 16 | console.log("\n--- Set.prototype.union() ---"); 17 | const union = set1.union(set2); 18 | console.log("set1.union(set2):", [...union]); // [1, 2, 3, 4, 5, 6, 7] 19 | 20 | // Union with multiple sets 21 | const multiUnion = set1.union(set2, set3); 22 | console.log("set1.union(set2, set3):", [...multiUnion]); // [1, 2, 3, 4, 5, 6, 7, 8, 9] 23 | 24 | // 2. Set.prototype.intersection() 25 | // Returns a new Set containing elements present in all sets 26 | console.log("\n--- Set.prototype.intersection() ---"); 27 | const intersection = set1.intersection(set2); 28 | console.log("set1.intersection(set2):", [...intersection]); // [3, 4, 5] 29 | 30 | // Intersection with multiple sets 31 | const multiIntersection = set1.intersection(set2, set3); 32 | console.log("set1.intersection(set2, set3):", [...multiIntersection]); // [5] 33 | 34 | // 3. Set.prototype.difference() 35 | // Returns a new Set containing elements present in the first set but not in the second 36 | console.log("\n--- Set.prototype.difference() ---"); 37 | const difference = set1.difference(set2); 38 | console.log("set1.difference(set2):", [...difference]); // [1, 2] 39 | console.log("set2.difference(set1):", [...set2.difference(set1)]); // [6, 7] 40 | 41 | // 4. Set.prototype.symmetricDifference() 42 | // Returns a new Set containing elements present in either set but not in both 43 | console.log("\n--- Set.prototype.symmetricDifference() ---"); 44 | const symDifference = set1.symmetricDifference(set2); 45 | console.log("set1.symmetricDifference(set2):", [...symDifference]); // [1, 2, 6, 7] 46 | 47 | // 5. Set.prototype.isSubsetOf() 48 | // Returns a boolean indicating if the set is a subset of the given set 49 | console.log("\n--- Set.prototype.isSubsetOf() ---"); 50 | const subset = new Set([3, 4]); 51 | console.log("subset:", [...subset]); // [3, 4] 52 | console.log("subset.isSubsetOf(set1):", subset.isSubsetOf(set1)); // true 53 | console.log("set1.isSubsetOf(subset):", set1.isSubsetOf(subset)); // false 54 | 55 | // 6. Set.prototype.isSupersetOf() 56 | // Returns a boolean indicating if the set is a superset of the given set 57 | console.log("\n--- Set.prototype.isSupersetOf() ---"); 58 | console.log("set1.isSupersetOf(subset):", set1.isSupersetOf(subset)); // true 59 | console.log("subset.isSupersetOf(set1):", subset.isSupersetOf(set1)); // false 60 | 61 | // 7. Set.prototype.isDisjointFrom() 62 | // Returns a boolean indicating if the set has no elements in common with the given set 63 | console.log("\n--- Set.prototype.isDisjointFrom() ---"); 64 | const disjointSet = new Set([10, 11, 12]); 65 | console.log("disjointSet:", [...disjointSet]); // [10, 11, 12] 66 | console.log("set1.isDisjointFrom(disjointSet):", set1.isDisjointFrom(disjointSet)); // true 67 | console.log("set1.isDisjointFrom(set2):", set1.isDisjointFrom(set2)); // false 68 | 69 | // Practical example: Finding common interests between users 70 | console.log("\n--- Practical Example: User Interests ---"); 71 | const user1Interests = new Set(["coding", "reading", "music", "hiking"]); 72 | const user2Interests = new Set(["gaming", "music", "movies", "hiking"]); 73 | const user3Interests = new Set(["travel", "photography", "hiking", "cooking"]); 74 | 75 | // Find common interests between all users 76 | const commonInterests = user1Interests.intersection(user2Interests, user3Interests); 77 | console.log("Common interests among all users:", [...commonInterests]); // ["hiking"] 78 | 79 | // Find unique interests of user1 80 | const uniqueInterests = user1Interests.difference(user2Interests, user3Interests); 81 | console.log("Unique interests of user1:", [...uniqueInterests]); // ["coding", "reading"] 82 | 83 | // Find all interests across users 84 | const allInterests = user1Interests.union(user2Interests, user3Interests); 85 | console.log("All interests:", [...allInterests]); 86 | // ["coding", "reading", "music", "hiking", "gaming", "movies", "travel", "photography", "cooking"] -------------------------------------------------------------------------------- /es2025/2.iterator-helpers.js: -------------------------------------------------------------------------------- 1 | // Iterator Helpers - New in ES2025 2 | // ES2025 introduces several helper methods for working with iterators and iterables 3 | 4 | // Iterator helpers provide a set of utility methods that can be chained together 5 | // to perform operations on iterators in a more readable and functional way 6 | 7 | // 1. Basic Iterator Creation 8 | console.log("--- Basic Iterator Creation ---"); 9 | 10 | // Create an iterator from an array 11 | const numbers = [1, 2, 3, 4, 5]; 12 | const numbersIterator = numbers[Symbol.iterator](); 13 | 14 | console.log(numbersIterator.next()); // { value: 1, done: false } 15 | console.log(numbersIterator.next()); // { value: 2, done: false } 16 | console.log(numbersIterator.next()); // { value: 3, done: false } 17 | 18 | // 2. Iterator.prototype.map() 19 | // Maps each value in the iterator to a new value 20 | console.log("\n--- Iterator.prototype.map() ---"); 21 | 22 | function* generateNumbers() { 23 | yield 1; 24 | yield 2; 25 | yield 3; 26 | yield 4; 27 | yield 5; 28 | } 29 | 30 | const doubledIterator = generateNumbers()[Symbol.iterator]() 31 | .map(x => x * 2); 32 | 33 | console.log([...doubledIterator]); // [2, 4, 6, 8, 10] 34 | 35 | // 3. Iterator.prototype.filter() 36 | // Filters values in the iterator based on a predicate 37 | console.log("\n--- Iterator.prototype.filter() ---"); 38 | 39 | const evenIterator = generateNumbers()[Symbol.iterator]() 40 | .filter(x => x % 2 === 0); 41 | 42 | console.log([...evenIterator]); // [2, 4] 43 | 44 | // 4. Iterator.prototype.take() 45 | // Takes a specified number of values from the iterator 46 | console.log("\n--- Iterator.prototype.take() ---"); 47 | 48 | const firstThreeIterator = generateNumbers()[Symbol.iterator]() 49 | .take(3); 50 | 51 | console.log([...firstThreeIterator]); // [1, 2, 3] 52 | 53 | // 5. Iterator.prototype.drop() 54 | // Skips a specified number of values from the iterator 55 | console.log("\n--- Iterator.prototype.drop() ---"); 56 | 57 | const afterTwoIterator = generateNumbers()[Symbol.iterator]() 58 | .drop(2); 59 | 60 | console.log([...afterTwoIterator]); // [3, 4, 5] 61 | 62 | // 6. Iterator.prototype.flatMap() 63 | // Maps each value and flattens the result 64 | console.log("\n--- Iterator.prototype.flatMap() ---"); 65 | 66 | const flatMappedIterator = generateNumbers()[Symbol.iterator]() 67 | .flatMap(x => [x, x * 10]); 68 | 69 | console.log([...flatMappedIterator]); // [1, 10, 2, 20, 3, 30, 4, 40, 5, 50] 70 | 71 | // 7. Iterator.prototype.reduce() 72 | // Reduces the iterator to a single value 73 | console.log("\n--- Iterator.prototype.reduce() ---"); 74 | 75 | const sum = generateNumbers()[Symbol.iterator]() 76 | .reduce((acc, val) => acc + val, 0); 77 | 78 | console.log("Sum:", sum); // 15 79 | 80 | // 8. Iterator.prototype.toArray() 81 | // Converts the iterator to an array 82 | console.log("\n--- Iterator.prototype.toArray() ---"); 83 | 84 | const numbersArray = generateNumbers()[Symbol.iterator]() 85 | .toArray(); 86 | 87 | console.log("Array:", numbersArray); // [1, 2, 3, 4, 5] 88 | 89 | // 9. Iterator.prototype.forEach() 90 | // Executes a function for each value in the iterator 91 | console.log("\n--- Iterator.prototype.forEach() ---"); 92 | 93 | let forEachSum = 0; 94 | generateNumbers()[Symbol.iterator]() 95 | .forEach(x => { 96 | forEachSum += x; 97 | console.log("Processing:", x); 98 | }); 99 | 100 | console.log("forEach Sum:", forEachSum); // 15 101 | 102 | // 10. Iterator.prototype.some() and Iterator.prototype.every() 103 | // Check if some or all values satisfy a condition 104 | console.log("\n--- Iterator.prototype.some() and Iterator.prototype.every() ---"); 105 | 106 | const hasSomeEven = generateNumbers()[Symbol.iterator]() 107 | .some(x => x % 2 === 0); 108 | 109 | const allEven = generateNumbers()[Symbol.iterator]() 110 | .every(x => x % 2 === 0); 111 | 112 | console.log("Has some even numbers:", hasSomeEven); // true 113 | console.log("All numbers are even:", allEven); // false 114 | 115 | // 11. Chaining Iterator Helpers 116 | // Iterator helpers can be chained together for complex operations 117 | console.log("\n--- Chaining Iterator Helpers ---"); 118 | 119 | const result = generateNumbers()[Symbol.iterator]() 120 | .filter(x => x > 1) 121 | .map(x => x * 10) 122 | .take(3) 123 | .toArray(); 124 | 125 | console.log("Chained result:", result); // [20, 30, 40] 126 | 127 | // 12. Practical Example: Processing a Stream of Data 128 | console.log("\n--- Practical Example: Processing a Stream of Data ---"); 129 | 130 | function* generateUserData() { 131 | yield { id: 1, name: "Alice", age: 25 }; 132 | yield { id: 2, name: "Bob", age: 30 }; 133 | yield { id: 3, name: "Charlie", age: 35 }; 134 | yield { id: 4, name: "Dave", age: 40 }; 135 | yield { id: 5, name: "Eve", age: 45 }; 136 | } 137 | 138 | // Find users over 30, extract their names, and take the first 2 139 | const olderUserNames = generateUserData()[Symbol.iterator]() 140 | .filter(user => user.age > 30) 141 | .map(user => user.name) 142 | .take(2) 143 | .toArray(); 144 | 145 | console.log("Names of first 2 users over 30:", olderUserNames); // ["Charlie", "Dave"] 146 | 147 | // Calculate the average age of all users 148 | const totalUsers = generateUserData()[Symbol.iterator]() 149 | .toArray(); 150 | 151 | const totalAge = totalUsers.reduce((sum, user) => sum + user.age, 0); 152 | const averageAge = totalAge / totalUsers.length; 153 | 154 | console.log("Average age:", averageAge); // 35 -------------------------------------------------------------------------------- /es2025/3.temporal-api.js: -------------------------------------------------------------------------------- 1 | // Temporal API - New in ES2025 2 | // ES2025 introduces a new date and time API called Temporal 3 | // It addresses many of the limitations of the existing Date object 4 | 5 | // The Temporal API provides a modern, immutable, and more intuitive way to work with dates and times 6 | // It includes several objects for different date/time representations 7 | 8 | // 1. Temporal.Now - Get current date and time in various formats 9 | console.log("--- Temporal.Now ---"); 10 | console.log("Instant:", Temporal.Now.instant()); 11 | console.log("TimeZone:", Temporal.Now.timeZone()); 12 | console.log("PlainDate:", Temporal.Now.plainDate()); 13 | console.log("PlainTime:", Temporal.Now.plainTime()); 14 | console.log("PlainDateTime:", Temporal.Now.plainDateTime()); 15 | console.log("ZonedDateTime:", Temporal.Now.zonedDateTime()); 16 | 17 | // 2. Temporal.Instant - Represents a fixed point in time (like a timestamp) 18 | console.log("\n--- Temporal.Instant ---"); 19 | const instant = Temporal.Instant.from("2025-06-01T12:30:45Z"); 20 | console.log("Instant:", instant.toString()); 21 | console.log("Epoch milliseconds:", instant.epochMilliseconds); 22 | console.log("Epoch nanoseconds:", instant.epochNanoseconds); 23 | 24 | // 3. Temporal.PlainDate - Represents a calendar date without time or timezone 25 | console.log("\n--- Temporal.PlainDate ---"); 26 | const date = Temporal.PlainDate.from("2025-06-01"); 27 | console.log("Date:", date.toString()); 28 | console.log("Year:", date.year); 29 | console.log("Month:", date.month); 30 | console.log("Day:", date.day); 31 | console.log("Day of week:", date.dayOfWeek); 32 | console.log("Days in month:", date.daysInMonth); 33 | console.log("Days in year:", date.daysInYear); 34 | console.log("Is leap year:", date.inLeapYear); 35 | 36 | // 4. Temporal.PlainTime - Represents wall-clock time without date or timezone 37 | console.log("\n--- Temporal.PlainTime ---"); 38 | const time = Temporal.PlainTime.from("14:30:45.123"); 39 | console.log("Time:", time.toString()); 40 | console.log("Hour:", time.hour); 41 | console.log("Minute:", time.minute); 42 | console.log("Second:", time.second); 43 | console.log("Millisecond:", time.millisecond); 44 | 45 | // 5. Temporal.PlainDateTime - Combines date and time without timezone 46 | console.log("\n--- Temporal.PlainDateTime ---"); 47 | const dateTime = Temporal.PlainDateTime.from("2025-06-01T14:30:45"); 48 | console.log("DateTime:", dateTime.toString()); 49 | console.log("Date component:", dateTime.toPlainDate().toString()); 50 | console.log("Time component:", dateTime.toPlainTime().toString()); 51 | 52 | // 6. Temporal.ZonedDateTime - Full date, time, and timezone information 53 | console.log("\n--- Temporal.ZonedDateTime ---"); 54 | const zonedDateTime = Temporal.ZonedDateTime.from("2025-06-01T14:30:45+02:00[Europe/Paris]"); 55 | console.log("ZonedDateTime:", zonedDateTime.toString()); 56 | console.log("TimeZone:", zonedDateTime.timeZone.toString()); 57 | console.log("Offset:", zonedDateTime.offset); 58 | console.log("Epoch nanoseconds:", zonedDateTime.epochNanoseconds); 59 | 60 | // 7. Temporal.Duration - Represents a length of time 61 | console.log("\n--- Temporal.Duration ---"); 62 | const duration = Temporal.Duration.from({ 63 | years: 1, 64 | months: 2, 65 | days: 15, 66 | hours: 12, 67 | minutes: 30, 68 | seconds: 45 69 | }); 70 | console.log("Duration:", duration.toString()); 71 | console.log("Total days:", duration.total("days")); 72 | console.log("Total hours:", duration.total("hours")); 73 | 74 | // 8. Temporal.TimeZone - Represents a timezone 75 | console.log("\n--- Temporal.TimeZone ---"); 76 | const timeZone = Temporal.TimeZone.from("America/New_York"); 77 | console.log("TimeZone:", timeZone.toString()); 78 | console.log("Current offset:", timeZone.getOffsetStringFor(Temporal.Now.instant())); 79 | 80 | // 9. Temporal.Calendar - Represents a calendar system 81 | console.log("\n--- Temporal.Calendar ---"); 82 | const calendar = Temporal.Calendar.from("iso8601"); 83 | console.log("Calendar:", calendar.toString()); 84 | console.log("Days in month:", calendar.daysInMonth(2025, 2)); 85 | console.log("Days in year:", calendar.daysInYear(2025)); 86 | console.log("Is leap year:", calendar.inLeapYear(2025)); 87 | 88 | // 10. Date Arithmetic - Adding and subtracting durations 89 | console.log("\n--- Date Arithmetic ---"); 90 | const startDate = Temporal.PlainDate.from("2025-01-15"); 91 | const endDate = startDate.add({ months: 1, days: 20 }); 92 | console.log("Start date:", startDate.toString()); 93 | console.log("End date:", endDate.toString()); 94 | console.log("Difference:", startDate.until(endDate).toString()); 95 | 96 | // 11. Comparing Dates and Times 97 | console.log("\n--- Comparing Dates and Times ---"); 98 | const date1 = Temporal.PlainDate.from("2025-06-01"); 99 | const date2 = Temporal.PlainDate.from("2025-07-15"); 100 | console.log("date1 < date2:", date1.compare(date2) < 0); 101 | console.log("date1 > date2:", date1.compare(date2) > 0); 102 | console.log("date1 == date2:", date1.compare(date2) === 0); 103 | 104 | // 12. Formatting Dates and Times 105 | console.log("\n--- Formatting Dates and Times ---"); 106 | const formatDate = Temporal.PlainDate.from("2025-06-01"); 107 | console.log("ISO format:", formatDate.toString()); 108 | console.log("Custom format (using Intl):", 109 | new Intl.DateTimeFormat("en-US", { 110 | year: "numeric", 111 | month: "long", 112 | day: "numeric" 113 | }).format(new Date(2025, 5, 1)) 114 | ); 115 | 116 | // 13. Practical Examples 117 | console.log("\n--- Practical Examples ---"); 118 | 119 | // Example 1: Calculate age 120 | function calculateAge(birthDate) { 121 | const today = Temporal.Now.plainDate(); 122 | const age = birthDate.until(today).years; 123 | return age; 124 | } 125 | 126 | const birthDate = Temporal.PlainDate.from("1990-05-15"); 127 | console.log("Age:", calculateAge(birthDate)); 128 | 129 | // Example 2: Add business days (skip weekends) 130 | function addBusinessDays(date, days) { 131 | let result = date; 132 | let daysAdded = 0; 133 | 134 | while (daysAdded < days) { 135 | result = result.add({ days: 1 }); 136 | // Skip weekends (6 = Saturday, 7 = Sunday) 137 | if (result.dayOfWeek !== 6 && result.dayOfWeek !== 7) { 138 | daysAdded++; 139 | } 140 | } 141 | 142 | return result; 143 | } 144 | 145 | const startBusinessDate = Temporal.PlainDate.from("2025-06-01"); 146 | console.log("Start date:", startBusinessDate.toString()); 147 | console.log("After 10 business days:", addBusinessDays(startBusinessDate, 10).toString()); 148 | 149 | // Example 3: Flight duration between timezones 150 | function calculateFlightDuration(departure, arrival) { 151 | const departureTZ = Temporal.ZonedDateTime.from(departure); 152 | const arrivalTZ = Temporal.ZonedDateTime.from(arrival); 153 | const duration = departureTZ.until(arrivalTZ); 154 | return duration; 155 | } 156 | 157 | const flightDeparture = "2025-06-01T08:30:00-04:00[America/New_York]"; 158 | const flightArrival = "2025-06-01T22:15:00+02:00[Europe/Paris]"; 159 | console.log("Flight duration:", calculateFlightDuration(flightDeparture, flightArrival).toString()); -------------------------------------------------------------------------------- /es2025/4.decorator-metadata.js: -------------------------------------------------------------------------------- 1 | // Decorator Metadata - New in ES2025 2 | // ES2025 enhances JavaScript decorators by allowing them to associate metadata with decorated elements 3 | 4 | // Decorators were introduced in ES2022 as a way to modify classes and class members 5 | // Decorator Metadata extends this by providing a standard way to attach and retrieve metadata 6 | 7 | // Note: This is experimental syntax and requires appropriate transpiler/compiler support 8 | 9 | // 1. Basic Decorator Syntax (Review) 10 | console.log("--- Basic Decorator Syntax ---"); 11 | 12 | // Class decorator 13 | function logClass(target) { 14 | console.log(`Class decorated: ${target.name}`); 15 | return target; 16 | } 17 | 18 | // Method decorator 19 | function logMethod(target, context) { 20 | const methodName = context.name; 21 | const originalMethod = target.value; 22 | 23 | // Replace the method with a new one that logs before and after execution 24 | target.value = function(...args) { 25 | console.log(`Entering method: ${methodName}`); 26 | const result = originalMethod.apply(this, args); 27 | console.log(`Exiting method: ${methodName}`); 28 | return result; 29 | }; 30 | 31 | return target; 32 | } 33 | 34 | // 2. Decorator Metadata - Attaching Metadata 35 | console.log("\n--- Decorator Metadata - Attaching Metadata ---"); 36 | 37 | // Define a decorator that attaches metadata 38 | function addMetadata(metadataObj) { 39 | return function(target, context) { 40 | // Store metadata on the decorated element 41 | context.metadata = { ...context.metadata, ...metadataObj }; 42 | return target; 43 | }; 44 | } 45 | 46 | // 3. Accessing Decorator Metadata 47 | console.log("\n--- Accessing Decorator Metadata ---"); 48 | 49 | // Define a decorator that reads metadata 50 | function validateParams(target, context) { 51 | const originalMethod = target.value; 52 | const methodMetadata = context.metadata; 53 | 54 | target.value = function(...args) { 55 | // Check if we have validation metadata 56 | if (methodMetadata && methodMetadata.params) { 57 | const validations = methodMetadata.params; 58 | 59 | // Validate each parameter based on metadata 60 | for (let i = 0; i < validations.length; i++) { 61 | const validation = validations[i]; 62 | const arg = args[i]; 63 | 64 | if (validation.required && (arg === undefined || arg === null)) { 65 | throw new Error(`Parameter ${i} is required for ${context.name}`); 66 | } 67 | 68 | if (validation.type && typeof arg !== validation.type) { 69 | throw new Error(`Parameter ${i} must be of type ${validation.type} for ${context.name}`); 70 | } 71 | 72 | if (validation.min !== undefined && arg < validation.min) { 73 | throw new Error(`Parameter ${i} must be >= ${validation.min} for ${context.name}`); 74 | } 75 | 76 | if (validation.max !== undefined && arg > validation.max) { 77 | throw new Error(`Parameter ${i} must be <= ${validation.max} for ${context.name}`); 78 | } 79 | } 80 | } 81 | 82 | return originalMethod.apply(this, args); 83 | }; 84 | 85 | return target; 86 | } 87 | 88 | // 4. Practical Example: API Documentation and Validation 89 | console.log("\n--- Practical Example: API Documentation and Validation ---"); 90 | 91 | // Define decorators for API documentation and validation 92 | function apiEndpoint(path, method = "GET") { 93 | return function(target, context) { 94 | context.metadata = { 95 | ...context.metadata, 96 | api: { path, method } 97 | }; 98 | return target; 99 | }; 100 | } 101 | 102 | function params(validations) { 103 | return function(target, context) { 104 | context.metadata = { 105 | ...context.metadata, 106 | params: validations 107 | }; 108 | return target; 109 | }; 110 | } 111 | 112 | function returns(description) { 113 | return function(target, context) { 114 | context.metadata = { 115 | ...context.metadata, 116 | returns: description 117 | }; 118 | return target; 119 | }; 120 | } 121 | 122 | // 5. Complete Example with Metadata 123 | console.log("\n--- Complete Example with Metadata ---"); 124 | 125 | @logClass 126 | class UserService { 127 | constructor() { 128 | this.users = [ 129 | { id: 1, name: "Alice", age: 30 }, 130 | { id: 2, name: "Bob", age: 25 }, 131 | { id: 3, name: "Charlie", age: 35 } 132 | ]; 133 | } 134 | 135 | @logMethod 136 | @apiEndpoint("/users", "GET") 137 | @returns("Array of all users") 138 | getAllUsers() { 139 | return this.users; 140 | } 141 | 142 | @logMethod 143 | @apiEndpoint("/users/:id", "GET") 144 | @params([ 145 | { name: "id", type: "number", required: true, description: "User ID" } 146 | ]) 147 | @returns("User object or null if not found") 148 | getUserById(id) { 149 | return this.users.find(user => user.id === id) || null; 150 | } 151 | 152 | @logMethod 153 | @apiEndpoint("/users", "POST") 154 | @params([ 155 | { name: "name", type: "string", required: true, description: "User name" }, 156 | { name: "age", type: "number", required: true, min: 0, max: 120, description: "User age" } 157 | ]) 158 | @returns("Created user object with assigned ID") 159 | @validateParams 160 | createUser(name, age) { 161 | const newId = Math.max(...this.users.map(user => user.id)) + 1; 162 | const newUser = { id: newId, name, age }; 163 | this.users.push(newUser); 164 | return newUser; 165 | } 166 | 167 | @logMethod 168 | @apiEndpoint("/users/:id", "PUT") 169 | @params([ 170 | { name: "id", type: "number", required: true, description: "User ID" }, 171 | { name: "name", type: "string", required: false, description: "User name" }, 172 | { name: "age", type: "number", required: false, min: 0, max: 120, description: "User age" } 173 | ]) 174 | @returns("Updated user object or null if not found") 175 | @validateParams 176 | updateUser(id, name, age) { 177 | const user = this.users.find(user => user.id === id); 178 | if (!user) return null; 179 | 180 | if (name !== undefined) user.name = name; 181 | if (age !== undefined) user.age = age; 182 | 183 | return user; 184 | } 185 | 186 | @logMethod 187 | @apiEndpoint("/users/:id", "DELETE") 188 | @params([ 189 | { name: "id", type: "number", required: true, description: "User ID" } 190 | ]) 191 | @returns("Boolean indicating success") 192 | @validateParams 193 | deleteUser(id) { 194 | const index = this.users.findIndex(user => user.id === id); 195 | if (index === -1) return false; 196 | 197 | this.users.splice(index, 1); 198 | return true; 199 | } 200 | } 201 | 202 | // 6. Generate API Documentation from Metadata 203 | console.log("\n--- Generate API Documentation from Metadata ---"); 204 | 205 | function generateApiDocs(serviceClass) { 206 | const docs = { 207 | service: serviceClass.name, 208 | endpoints: [] 209 | }; 210 | 211 | // Get all method names from the prototype 212 | const methodNames = Object.getOwnPropertyNames(serviceClass.prototype) 213 | .filter(name => name !== 'constructor'); 214 | 215 | for (const methodName of methodNames) { 216 | const method = serviceClass.prototype[methodName]; 217 | const metadata = method.context?.metadata; 218 | 219 | if (metadata && metadata.api) { 220 | docs.endpoints.push({ 221 | path: metadata.api.path, 222 | method: metadata.api.method, 223 | function: methodName, 224 | parameters: metadata.params || [], 225 | returns: metadata.returns || "No return description" 226 | }); 227 | } 228 | } 229 | 230 | return docs; 231 | } 232 | 233 | // Create an instance and test the service 234 | const userService = new UserService(); 235 | 236 | // Test the methods 237 | console.log("All users:", userService.getAllUsers()); 238 | console.log("User with ID 2:", userService.getUserById(2)); 239 | console.log("Create user:", userService.createUser("Dave", 40)); 240 | console.log("Update user:", userService.updateUser(1, "Alice Smith", 31)); 241 | console.log("Delete user:", userService.deleteUser(3)); 242 | console.log("All users after operations:", userService.getAllUsers()); 243 | 244 | // Generate API documentation 245 | const apiDocs = generateApiDocs(UserService); 246 | console.log("API Documentation:", JSON.stringify(apiDocs, null, 2)); 247 | 248 | // 7. Error Handling with Validation 249 | console.log("\n--- Error Handling with Validation ---"); 250 | 251 | try { 252 | // This should throw an error because age is out of range 253 | userService.createUser("Invalid", 150); 254 | } catch (error) { 255 | console.error("Validation error:", error.message); 256 | } 257 | 258 | try { 259 | // This should throw an error because name is required 260 | userService.createUser(null, 25); 261 | } catch (error) { 262 | console.error("Validation error:", error.message); 263 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ECMAScript Features or Cheatsheet 2 | 3 | > Click :star:if you like the project. Pull Request are highly appreciated. Follow me [@SudheerJonna](https://twitter.com/SudheerJonna) for technical updates. 4 | 5 | ## How to run examples 6 | ```cmd 7 | npm install 8 | npx babel-node es2020\bigint.js # Try other examples too 9 | ``` 10 | 11 | # What is ECMAScript? 12 | 13 | **ECMAScript** is the scripting language which acts as the basis of JavaScript. ECMAScript is standardized by the ECMA International standards organization in the ECMA-262 and ECMA-402 specifications. 14 | Each proposal for an ECMAScript feature goes through the following maturity stages: 15 | 16 | 1. Stage 0: Strawman; 17 | 2. Stage 1: Proposal; 18 | 3. Stage 2: Draft; 19 | 4. Stage 3: Candidate; 20 | 5. Stage 4: Finished. 21 | 22 | 23 | ## Version History 24 | 25 | | Edition | Date | 26 | | --- | --------- | 27 | | ES2015 Or ES6 | June 2015 | 28 | | ES2016 Or ES7 | June 2016 | 29 | | ES2017 Or ES8 | June 2017 | 30 | | ES2018 Or ES9 | June 2018 | 31 | | ES2019 Or ES10 | June 2019 | 32 | | ES2020 Or ES11 | June 2020 | 33 | | ES2021 Or ES12 | June 2021 | 34 | | ES2022 Or ES13 | June 2022 | 35 | | ES2023 Or ES14 | June 2023 | 36 | | ES2024 Or ES15 | June 2024 | 37 | | ES2025 Or ES16 | June 2025 | 38 | 39 | ### Table of Contents 40 | 41 | | No. | Feature | 42 | | --- | --------- | 43 | | | **ES2015 Or ES6** | 44 | |1 | [Variable Scoping](#variable-scoping) | 45 | |2 | [Arrow functions](#arrow-functions) | 46 | |3 | [Classes](#classes) | 47 | |4 | [Enhanced object literals](#Enhanced-object-literals) | 48 | |5 | [Template literals](#template-literals) | 49 | |6 | [Destructuring](#destructuring) | 50 | |7 | [Default parameters](#default-parameters) | 51 | |8 | [Rest parameter](#rest-parameter) | 52 | |9 | [Spread Operator](#spread-operator) | 53 | |10 | [Iterators & For..of](#iterators--forof) | 54 | |11 | [Generators](#generators) | 55 | |12 | [Modules](#modules) | 56 | |13 | [Set](#set) | 57 | |14 | [Weakset](#weakset) | 58 | |15 | [Map](#map) | 59 | |16 | [Weakmap](#weakmap) | 60 | |17 | [Unicode](#unicode) | 61 | |18 | [Proxies](#proxies) | 62 | |19 | [Symbols](#symbols) | 63 | |20 | [Promises](#promises) | 64 | |21 | [Reflect](#reflect) | 65 | |22 | [Binary and Octal](#binary-and-octal) | 66 | |23 | [Proper Tail calls](#proper-tail-calls)| 67 | |24 | [Array find methods](#array-find-methods) | 68 | | | **ES2016 Or ES7** | 69 | |1 | [Array includes](#array-includes) | 70 | |2 | [Exponentiation Operator](#exponentiation-operator) | 71 | | | **ES2017 Or ES8** | 72 | |1 | [Async functions](#async-functions) | 73 | |2 | [Object values](#object-values) | 74 | |3 | [Object entries](#object-entries) | 75 | |4 | [Object property descriptors](#object-property-descriptors) | 76 | |5 | [String padding](#string-padding)| 77 | |6 | [Shared memory and atomics](#shared-memory-and-atomics)| 78 | |7 | [Trailing commas](#trailing-commas) | 79 | | | **ES2018 Or ES9**| 80 | |1 | [Async iterators](#async-iterators) | 81 | |2 | [Object rest and spread operators](#object-rest-and-spread-operators) | 82 | |3 | [Promise finally](#promise-finally) | 83 | | | **ES2019 Or ES10**| 84 | |1 | [Array flat and flatMap](#array-flat-and-flatmap) | 85 | |2 | [Object fromEntries](#object-fromentries) | 86 | |3 | [String trimStart and trimEnd](#string-trimstart-and-trimend) | 87 | |4 | [Symbol description](#symbol-description)| 88 | |5 | [Optional Catch Binding](#optional-catch-binding)| 89 | |6 | [JSON Improvements](#json-improvements)| 90 | |7 | [Array Stable Sort](#array-stable-sort)| 91 | |8 | [Function.toString()](#functiontostring)| 92 | |9 | [Private Class Variables](#private-class-variables)| 93 | | | **ES2020 Or ES11**| 94 | |1 | [BigInt](#bigint) | 95 | |2 | [Dynamic Import](#dynamic-import) | 96 | |3 | [Nullish Coalescing Operator](#nullish-coalescing-operator)| 97 | |4 | [Optional chaining](#optional-chaining)| 98 | |5 | [Promise allSettled](#promiseallsettled)| 99 | |6 | [String matchAll](#string-matchall)| 100 | |7 | [globalThis](#globalthis)| 101 | |8 | [import.meta](#importmeta)| 102 | |9 | [for..in order](#forin-order)| 103 | | | **ES2021 Or ES12**| 104 | |1 | [replaceAll](#replaceall) | 105 | |2 | [promise.any](#promiseany) | 106 | |3 | [WeakRef](#weakref)| 107 | |4 | [Numeric Separators](#numeric-separators)| 108 | |5 | [Logical Operators](#logical-operators)| 109 | | | **ES2022 Or ES13**| 110 | |1 | [Top level await](#top-level-await) | 111 | |2 | [Class fields](#class-fields) | 112 | |3 | [Array .at() method](#array-at-method)| 113 | |4 | [Error cause](#error-cause)| 114 | |5 | [hasOwn](#hasown)| 115 | |6 | [Regex match indices](#regex-match-indices)| 116 | | | **ES2023 Or ES14**| 117 | |1 | [Find array from last](#find-array-from-last) | 118 | |2 | [Hashbang syntax](#hashbang-syntax) | 119 | |3 | [Symbols as WeakMap keys](#symbols-as-weakmap-keys)| 120 | |4 | [Change Array by Copy](#change-array-by-copy)| 121 | | | **ES2024 Or ES15**| 122 | |1 | [GroupBy into objects and maps](#groupby-into-objects-and-maps) | 123 | |2 | [Temporal API](#temporal-api) | 124 | |3 | [Well formed unicode strings](#well-formed-unicode-strings) | 125 | |4 | [Atomic waitSync](#atomic-waitsync) | 126 | |5 | [RegEx v Flag and string properties](#regEx-v-flag-and-string-properties) | 127 | |6 | [Promise withResolvers](#promise-withResolvers) | 128 | |7 | [Pipeline Operator](#pipeline-operator) | 129 | |8 | [Records and Tuples](#records-and-tuples) | 130 | |9 | [Decorators](#decorators) | 131 | | | **ES2025 Or ES16**| 132 | |1 | [Set Methods](#set-methods) | 133 | |2 | [Iterator Helpers](#iterator-helpers) | 134 | |3 | [Temporal API](#temporal-api-es2025) | 135 | |4 | [Decorator Metadata](#decorator-metadata) | 136 | 137 | ## ES2015 Or ES6 138 | 139 | 1. ### Variable Scoping 140 | 141 | The variable scoping determines the visibility or accessibility of a variable within the certain part of the program or region. 142 | 143 | In ES6, both `const` and `let` keywords allow developers to declare variables in the block scope. 144 | 145 | The `let` statement declares a block-scoped local variable which can be reassigned. i.e, `let` declaration creates a mutable variable. 146 | 147 | ```js 148 | let a = 1; 149 | 150 | if (a === 1) { 151 | let a = 2; 152 | 153 | console.log(a); //2 154 | } 155 | 156 | console.log(a); //1 157 | ``` 158 | 159 | **const** variables are similar to **let** variables but they can't be changed through reassignment. i.e, The const declaration creates a read-only reference to a value. 160 | 161 | ```js 162 | const x = 1; 163 | 164 | if (x === 1) { 165 | const y = 2; // You cannot re-assign the value similar to let variable 166 | 167 | console.log(y); //2 168 | } 169 | 170 | console.log(x); //1 171 | ``` 172 | 173 | **[⬆ Back to Top](#table-of-contents)** 174 | 175 | 2. ### Arrow functions 176 | 177 | The arrow functions provides a more concise syntax for writing function expressions by opting out the function and return keywords using fat arrow(=>) notation. Let's see how this arrow function looks like, 178 | 179 | ```js 180 | // Function Expression 181 | var multiplyFunc = function(a, b) { 182 | return a * b; 183 | } 184 | console.log(multiplyFunc(2, 5)); // 10 185 | 186 | // Arrow function 187 | var multiplyArrowFunc = (a, b) => a * b; 188 | console.log(multiplyArrowFunc(2, 5)); // 10 189 | ``` 190 | You can also skip parenthesis(()) if the function has exactly one parameter(either zero or more than one parameter). Apart from this, you can wrap braces({}) if the function has more than one expression in the body. 191 | 192 | Let's list down all the variations of arrow functions, 193 | ```js 194 | //1. Single parameter and single statement 195 | const message = name => console.log("Hello, " + name + "!"); 196 | message("Sudheer"); // Hello, Sudheer! 197 | 198 | //2. Multiple parameters and single statement 199 | const multiply = (x, y) => x * y; 200 | console.log(multiply(2, 5)); // 10 201 | 202 | //3. Single parameter and multiple statements 203 | const even = number => { 204 | if(number%2) { 205 | console.log("Even"); 206 | } else { 207 | console.log("Odd"); 208 | } 209 | } 210 | even(5); // odd 211 | 212 | //4. Multiple parameters and multiple statements 213 | const divide = (x, y) => { 214 | if(y != 0) { 215 | return x / y; 216 | } 217 | } 218 | console.log(divide(100, 5)); // 20 219 | 220 | //5. No parameter and single statement 221 | const greet = () => console.log('Hello World!'); 222 | greet(); // Hello World! 223 | ``` 224 | 225 | **[⬆ Back to Top](#table-of-contents)** 226 | 227 | 3. ### Classes 228 | The classes are introduced as syntactic sugar over existing prototype based inheritance and constructor functions. So this feature doesn't bring new object-oriented inheritance model to JavaScript. 229 | 230 | There are two ways to define classes, 231 | 232 | 1. **Class declarations:** 233 | 234 | ```js 235 | class Square { 236 | constructor(length) { 237 | this.length = length; 238 | } 239 | 240 | get area() { 241 | return this.length * this.length; 242 | } 243 | 244 | set length(value) { 245 | this.length = value; 246 | } 247 | } 248 | ``` 249 | 2. **Class expressions:** 250 | 251 | ```js 252 | const square = class Square { 253 | constructor(length) { 254 | this.length = length; 255 | } 256 | 257 | get area() { 258 | return this.length * this.length; 259 | } 260 | 261 | set length(value) { 262 | this.length = value; 263 | } 264 | } 265 | ``` 266 | 267 | You can use **extend** keyword to use inheritance. This enables the subclass to get all features of a parent class. 268 | 269 | ```js 270 | class Vehicle { 271 | constructor(name) { 272 | this.name = name; 273 | } 274 | 275 | start() { 276 | console.log(`${this.name} vehicle started`); 277 | } 278 | } 279 | 280 | class Car extends Vehicle { 281 | start() { 282 | console.log(`${this.name} car started`); 283 | } 284 | } 285 | 286 | const car = new Car('BMW'); 287 | console.log(car.start()); // BMW car started 288 | ``` 289 | 290 | **Note:** Even though ES6 classes looks similar to classes in other object oriented languages, such as Java, PHP, etc but they do not work exactly the same way. 291 | 292 | **[⬆ Back to Top](#table-of-contents)** 293 | 294 | 4. ### Enhanced object literals 295 | 296 | Object literals are extended to support setting the prototype at construction, shorthand for foo: foo assignments, defining methods, making super calls, and computing property names with expressions. 297 | 298 | The important enhancements of object literals are, 299 | 300 | 1. **Property Shorthand:** 301 | 302 | Object's properties are often created from variables with the same name. 303 | 304 | Let's see the ES5 representation 305 | 306 | ```js 307 | var a = 1, b = 2, c = 3, 308 | obj = { 309 | a: a, 310 | b: b, 311 | c: c 312 | }; 313 | console.log(obj); 314 | ``` 315 | and it can be represented in a shorter syntax as below, 316 | 317 | ```js 318 | const a = 1, b = 2, c = 3; 319 | const obj = { 320 | a, 321 | b, 322 | c 323 | }; 324 | console.log(obj); 325 | ``` 326 | 327 | 2. **Method Shorthand:** 328 | In ES5, Object methods require the function statement as below, 329 | 330 | ```js 331 | var calculation = { 332 | sum: function(a, b) { return a + b; }, 333 | multiply: function(a, b) { return a * b; } 334 | }; 335 | 336 | console.log(calculation.sum(5, 3)); // 8 337 | console.log(calculation.multiply(5, 3)); // 15 338 | ``` 339 | 340 | This can be avoided in ES6, 341 | 342 | ```js 343 | const calculation = { 344 | sum(a, b) { return a + b; }, 345 | multiply(a, b) { return a * b; } 346 | }; 347 | 348 | console.log(calculation.sum(5, 3)); // 8 349 | console.log(calculation.multiply(5, 3)); // 15 350 | ``` 351 | 352 | 3. **Computed Property Names:** 353 | In ES5, it wasn’t possible to use a variable for a key name during object creation stage. 354 | 355 | ```js 356 | var 357 | key = 'three', 358 | obj = { 359 | one: 1, 360 | two: 2 361 | }; 362 | 363 | obj[key] = 3; 364 | ``` 365 | 366 | Object keys can be dynamically assigned in ES6 by placing an expression in square brackets([]) 367 | 368 | ```js 369 | const 370 | key = 'three', 371 | computedObj = { 372 | one: 1, 373 | two: 2, 374 | [key]: 3 375 | }; 376 | ``` 377 | 378 | **[⬆ Back to Top](#table-of-contents)** 379 | 380 | 5. ### Template literals 381 | Prior to ES6, JavaScript developers would need to do ugly string concatenation to creat dynamic strings. 382 | 383 | 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}`). Also, these literals are enclosed by the backtick (`` ` ``) character instead of double or single quotes. 384 | 385 | ES6 has two new kinds of literals: 386 | 387 | 1. **Template literals:** string literals which exists across multiple lines and include interpolated expressions(i.e, ${expression}) 388 | 389 | ```js 390 | const firstName = 'John'; 391 | console.log(`Hello ${firstName}! 392 | Good morning!`); 393 | ``` 394 | 395 | 2. **Tagged template literals:** Function calls which are created by mentioning a function before a template literal. 396 | 397 | The real world use case is creating components in CSS-In-JS styled components to use across the application 398 | 399 | ```js 400 | const Button = styled.a` 401 | display: inline-block; 402 | border-radius: 3px; 403 | ` 404 | ``` 405 | 406 | **[⬆ Back to Top](#table-of-contents)** 407 | 408 | 6. ### Destructuring 409 | 410 | Destructuring is a javascript expression for extracting multiple values from data stored in objects(properties of an object) and Arrays. 411 | 412 | **Object destructuring:** 413 | 414 | This feature is used to extract values from an object. 415 | 416 | ```js 417 | const user = { firstName: 'John', lastName: 'Kary' }; 418 | const {firstName, lastName} = user; 419 | console.log(firstName, lastName); // John, Kary 420 | ``` 421 | 422 | **Array destructuring:** 423 | 424 | This feature is used to extract values from an array. 425 | 426 | ```js 427 | const [one, two, three] = ['one', 'two', 'three']; 428 | console.log(one, two, three); // one, two, three 429 | ``` 430 | 431 | You can use destructing in below places, 432 | 433 | 1. Variable declarations 434 | 2. Assignments 435 | 3. Parameter definitions 436 | 4. for-of loop 437 | 438 | **[⬆ Back to Top](#table-of-contents)** 439 | 440 | 7. ### Default parameters 441 | 442 | Default parameters allow named parameters of a function to be initialized with default values if no value or undefined is passed. 443 | 444 | Prior to ES6, you need check for undefined values and provide the default value for undefined values using if/else or ternary operator 445 | ```js 446 | function add(a, b) { 447 | a = (typeof a !== 'undefined') ? a : 10; 448 | b = (typeof b !== 'undefined') ? b : 20; 449 | return a + b; 450 | } 451 | add(20); // 40 452 | add(); // 30 453 | ``` 454 | In ES6, these checks can be avoided using default parameters 455 | 456 | ```js 457 | function add(a = 10, b = 20) { 458 | return a + b; 459 | } 460 | add(20); // 40 461 | add(); // 30 462 | ``` 463 | 464 | **[⬆ Back to Top](#table-of-contents)** 465 | 466 | 8. ### Rest parameter 467 | 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". This feature has been introduced to reduce the boilerplate code that was induced by the arguments. 468 | 469 | ```js 470 | function sum(...args) { 471 | return args.reduce((previous, current) => { 472 | return previous + current; 473 | }); 474 | } 475 | 476 | console.log(sum(1, 2, 3)); // 6 477 | console.log(sum(1, 2, 3, 4)); // 10 478 | console.log(sum(1, 2, 3, 4, 5)); // 15 479 | ``` 480 | 481 | **[⬆ Back to Top](#table-of-contents)** 482 | 483 | 9. ### Spread Operator 484 | Spread Operator allows iterables( arrays / objects / strings ) to be expanded into single arguments/elements. 485 | 486 | 1. In function and constructor calls, the spread operator turns iterable values into arguments 487 | 488 | ```js 489 | console.log(Math.max(...[-10, 30, 10, 20])); //30 490 | console.log(Math.max(-10, ...[-50, 10], 30)); //30 491 | ``` 492 | 2. In Array literals and strings, the spread operator turns iterable values into Array elements 493 | 494 | ```js 495 | console.log([1, ...[2,3], 4, ...[5, 6, 7]]); // 1, 2, 3, 4, 5, 6, 7 496 | ``` 497 | 498 | **Note:** The spread syntax is opposite of rest parameter. 499 | 500 | **[⬆ Back to Top](#table-of-contents)** 501 | 502 | 10. ### Iterators & For..of 503 | 504 | String, Array, TypedArray, Map, and Set are all built-in iterables but objects are not iterables by default. 505 | Iterators are a new way to loop over any collection in JavaScript. These are objects which defines a sequence and potentially a return value upon its termination. 506 | An iterator implements the Iterator protocol by having a next() method that returns an object with two properties: 507 | 508 | 1. **value:** The next value in the iteration sequence. 509 | 2. **done:** returns true if the last value in the sequence has already been consumed. 510 | 511 | You can make the object iterable by defining a `Symbol.iterator` property on it. 512 | 513 | ```js 514 | const collection = { 515 | one: 1, 516 | two: 2, 517 | three: 3, 518 | [Symbol.iterator]() { 519 | const values = Object.keys(this); 520 | let i = 0; 521 | return { 522 | next: () => { 523 | return { 524 | value: this[values[i++]], 525 | done: i > values.length 526 | } 527 | } 528 | }; 529 | } 530 | }; 531 | 532 | const iterator = collection[Symbol.iterator](); 533 | 534 | console.log(iterator.next()); // → {value: 1, done: false} 535 | console.log(iterator.next()); // → {value: 2, done: false} 536 | console.log(iterator.next()); // → {value: 3, done: false} 537 | console.log(iterator.next()); // → {value: undefined, done: true} 538 | 539 | for (const value of collection) { 540 | console.log(value); 541 | } 542 | ``` 543 | 544 | The for...of statement creates a loop iterating over user defined collection object. But this loop can be used for built-in objects too. 545 | 546 | **Note:** The abrupt iteration termination can be caused by break, throw or return. 547 | 548 | **[⬆ Back to Top](#table-of-contents)** 549 | 550 | 11. ### Generators 551 | 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(i.e, function* ()). 552 | 553 | 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. 554 | 555 | ```js 556 | function* myGenerator(i) { 557 | yield i + 10; 558 | yield i + 20; 559 | return i + 30; 560 | } 561 | 562 | const myGenObj = myGenerator(10); 563 | 564 | console.log(myGenObj.next().value); // 20 565 | console.log(myGenObj.next().value); // 30 566 | console.log(myGenObj.next().value); // 40 567 | ``` 568 | 569 | **Note:** We can use `yield*` to delegate to another generator function 570 | 571 | **[⬆ Back to Top](#table-of-contents)** 572 | 573 | 12. ### Modules 574 | Modules are small units of independent, reusable code to be used as the building blocks in a Javascript application. 575 | 576 | Prior to ES6, there was no native modules support in JavaScript. There were 3 major module standards used, 577 | 578 | 1. Asynchronous Module Definition (AMD) 579 | 2. RequireJS Modules 580 | 3. CommonJS Modules (module.exports and require syntax used in Node.js) 581 | 582 | ES6 has provided the built-in support for modules. Everything inside a module is private by default, and runs in strict mode. Public variables, functions and classes are exposed using `export` statement and import the same using `import` statement. 583 | 584 | **Export Statement:** 585 | 586 | There are two types of exports: 587 | 588 | 1. Named Exports (Zero or more exports per module) 589 | 590 | You can export each element or a single export statement to export all the elements at once 591 | 592 | ```js 593 | // module "my-module.js" 594 | 595 | const PI = Math.PI; 596 | 597 | function add(...args) { 598 | return args.reduce((num, tot) => tot + num); 599 | } 600 | 601 | function multiply(...args) { 602 | return args.reduce((num, tot) => tot * num); 603 | } 604 | 605 | // private function 606 | function print(msg) { 607 | console.log(msg); 608 | } 609 | 610 | export { PI, add, multiply }; 611 | ``` 612 | 613 | 2. Default Exports (One per module) 614 | 615 | If we want to export a single value, you could use a default export 616 | 617 | ```js 618 | // module "my-module.js" 619 | 620 | export default function add(...args) { 621 | return args.reduce((num, tot) => tot + num); 622 | } 623 | ``` 624 | 625 | **Import Statement:** 626 | 627 | The static import statement is used to import read only live bindings which are exported by another module. 628 | 629 | There are many variations of import scenarios as below, 630 | 631 | ```js 632 | // 1. Import an entire module's contents 633 | import * as name from "my-module"; 634 | 635 | //2.Import a single export from a module 636 | import { export1 } from "my-module"; 637 | 638 | //3.Import multiple exports from a module 639 | import { export1 , export2 } from "my-module"; 640 | 641 | //4.Import default export from a module 642 | import defaultExport from "my-module"; 643 | 644 | //5.Import an export with an alias 645 | import { export1 as alias1 } from "my-module"; 646 | 647 | ``` 648 | 649 | **[⬆ Back to Top](#table-of-contents)** 650 | 651 | 13. ### Set 652 | 653 | Set is a built-in object to store collections of unique values of any type. 654 | 655 | ```js 656 | let mySet = new Set() 657 | 658 | mySet.add(1); 659 | mySet.add(2); 660 | mySet.add(2); 661 | mySet.add('some text here'); 662 | mySet.add({one: 1, two: 2 , three: 3}); 663 | console.log(mySet); // Set [ 1, 2, 'some text here', {one: 1, two: 2 , three: 3} ] 664 | console.log(mySet.size) // 4 665 | console.log(mySet.has(2)); // true 666 | ``` 667 | 668 | **[⬆ Back to Top](#table-of-contents)** 669 | 670 | 14. ### Weakset 671 | 672 | The Set is used to store any type of data such as primitives and object types. Whereas WeakSet is an object to store weakly held objects in a collection. (i.e, WeakSet is the collections of objects only). Here weak means, If no other references to an object stored in the WeakSet exist, those objects can be garbage collected. 673 | 674 | ```js 675 | let myUserSet = new WeakSet(); 676 | 677 | let john = { name: "John" }; 678 | let rocky = { name: "Rocky" }; 679 | let alex = { name: "Alex" }; 680 | let nick = { name: "Nick" }; 681 | 682 | myUserSet.add(john); 683 | myUserSet.add(rocky); 684 | myUserSet.add(john); 685 | myUserSet.add(nick); 686 | 687 | console.log(myUserSet.has(john)); // true 688 | console.log(myUserSet.has(alex)); // false 689 | console.log(myUserSet.delete(nick)); 690 | console.log(myUserSet.has(nick)); // false 691 | 692 | john = null; 693 | ``` 694 | 695 | **[⬆ Back to Top](#table-of-contents)** 696 | 697 | 15. ### Map 698 | 699 | Map is a collection of elements where each element is stored as a Key, value pair. It can hold both objects and primitive values as either key or value and iterates its elements in insertion order. 700 | 701 | Let's take a map with different types of primitives and objects as key-value pairs and various methods on it, 702 | 703 | ```js 704 | let typeMap = new Map(); 705 | 706 | var keyObj = {'one': 1} 707 | 708 | typeMap.set('10', 'string'); // a string key 709 | typeMap.set(10, 'number'); // a numeric key 710 | typeMap.set(true, 'boolean'); // a boolean key 711 | typeMap.set(keyObj, 'object'); // an object key 712 | 713 | 714 | console.log(typeMap.get(10)); // number 715 | console.log(typeMap.get('10')); // string 716 | console.log(typeMap.get(keyObj)); // object 717 | console.log(typeMap.get({'one': 1})); // undefined 718 | 719 | console.log(typeMap.size ); // 4 720 | 721 | for(let item of typeMap) { 722 | console.log(item); 723 | } 724 | 725 | 726 | for(let item in typeMap) { 727 | console.log(item); 728 | } 729 | ``` 730 | 731 | **[⬆ Back to Top](#table-of-contents)** 732 | 733 | 16. ### Weakmap 734 | 735 | WeakMap object is a collection of key/value pairs in which the keys are weakly referenced. For this object, the keys must be objects and the values can be arbitrary values. 736 | 737 | Let's see various methods of weakmap with below example, 738 | 739 | ```js 740 | var weakMap = new WeakMap(); 741 | 742 | var obj1 = {} 743 | var obj2 = {} 744 | 745 | 746 | weakMap.set(obj1, 1); 747 | weakMap.set(obj2, 2); 748 | weakMap.set({}, {"four": 4}); 749 | 750 | console.log(weakMap.get(obj2)); // 2 751 | console.log(weakMap.has({})); // return false even though empty object exists as key. Because the keys have different references 752 | 753 | delete obj2; 754 | console.log(weakMap.get(obj2)); // 2 755 | weakMap.delete(obj1) 756 | console.log(weakMap.get(obj1)); //undefined 757 | ``` 758 | 759 | **[⬆ Back to Top](#table-of-contents)** 760 | 761 | 17. ### Unicode 762 | 763 | 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. Since the character set is been expanded by Unicode, you will get unexpected results from UTF-16 encoded strings containing surrogate pairs(i.e, Since it is not sufficient to represent certain characters in just 16-bits, you need two 16-bit code units). 764 | 765 | ```js 766 | let str = '𠮷'; 767 | 768 | console.log(str.length); // 2 769 | console.log(text.charAt(0)); // "" 770 | console.log(text.charAt(1)); // "" 771 | console.log(text.charCodeAt(0)); // 55362(1st code unit) 772 | console.log(text.charCodeAt(1)); // 57271(2nd code unit) 773 | 774 | console.log(/^.$/.test(str)); // false, because length is 2 775 | console.log('\u20BB7'); // 7!(wrong value) 776 | console.log(str === '\uD842\uDFB7'); // true 777 | ``` 778 | 779 | ECMAScript 6 added full support for UTF-16 within strings and regular expressions. It introduces new Unicode literal form in strings and new RegExp u mode to handle code points, as well as new APIs(codePointAt, fromCodePoint) to process strings. 780 | 781 | ```js 782 | let str = '𠮷'; 783 | 784 | // new string form 785 | console.log('\u{20BB7}'); // "𠮷" 786 | 787 | // new RegExp u mode 788 | console.log(new RegExp('\u{20BB7}', 'u')); 789 | console.log(/^.$/u.test(str)); // true 790 | 791 | //API methods 792 | console.log(str.codePointAt(0)); // 134071 793 | console.log(str.codePointAt(1)); // 57271 794 | 795 | console.log(String.fromCodePoint(134071)); // "𠮷" 796 | ``` 797 | 798 | **[⬆ Back to Top](#table-of-contents)** 799 | 800 | 18. ### Symbols 801 | 802 | Symbol is a new peculiar primitive data type of JavaScript, along with other primitive types such as string, number, boolean, null and undefined. The new symbol is created just by calling the Symbol function. i.e, Every time you call the Symbol function, you’ll get a new and completely unique value. You can also pass a parameter to Symbol(), which is useful for debugging purpose only. 803 | 804 | Even though equality checks on two symbols is always false, it will be true while comparing symbols with `.for` method due to global registry (i.e, Symbols.for('key') === Symbols.for('key')) 805 | 806 | These symbols are useful to uniquely identify properties or unique constants, 807 | 808 | ```js 809 | //1. Object properties 810 | let id = Symbol("id"); 811 | let user = { 812 | name: "John", 813 | age: 40, 814 | [id]: 111 815 | }; 816 | 817 | for (let key in user) { 818 | console.log(key); // name, age without symbols 819 | } 820 | 821 | console.log(JSON.stringify(user)); // {"name":"John", "age": 40} 822 | console.log(Object.keys(user)); // ["name", "age"] 823 | 824 | console.log( "User Id: " + user[id] ); // Direct access by the symbol works 825 | 826 | //2. Unique constants 827 | const logLevels = { 828 | DEBUG: Symbol('debug'), 829 | INFO: Symbol('info'), 830 | WARN: Symbol('warn'), 831 | ERROR: Symbol('error'), 832 | }; 833 | console.log(logLevels.DEBUG, 'debug message'); 834 | console.log(logLevels.INFO, 'info message'); 835 | 836 | //3. Equality Checks 837 | 838 | console.log(Symbol('foo') === Symbol('foo')); // false 839 | console.log(Symbol.for('foo') === Symbol.for('foo')); // true 840 | ``` 841 | 842 | **[⬆ Back to Top](#table-of-contents)** 843 | 844 | 19. ### Proxies 845 | The Proxy object is used to create a proxy for another object, which can intercept and redefine fundamental operations for that object such as property lookup, assignment, enumeration, function invocation etc. These are used in many libraries and some browser frameworks. 846 | 847 | The proxy object is created with two parameters with below syntax, 848 | 849 | ```js 850 | let proxy = new Proxy(target, handler) 851 | ``` 852 | 853 | 1. **target:** Object on which you want to proxy 854 | 2. **handler:** An object that defines which operations will be intercepted and how to redefine them. 855 | 856 | The property Lookup Behavior of a user proxied object will be as below, 857 | 858 | ```js 859 | const target = { 860 | name: "John", 861 | age: 3 862 | }; 863 | 864 | const handler = { 865 | get: function(target, prop) { 866 | return prop in target ? 867 | target[prop] : 868 | `${prop} does not exist`; 869 | } 870 | }; 871 | 872 | const user = new Proxy(target, handler); 873 | console.log(user.name); // John 874 | console.log(user.age); // 3 875 | console.log(user.gender); // gender does not exist 876 | ``` 877 | 878 | These proxies also enforce value validations. Let's take an example with set handler, 879 | 880 | ```js 881 | let ageValidator = { 882 | set: function(obj, prop, value) { 883 | if (prop === 'age') { 884 | if (!Number.isInteger(value)) { 885 | throw new TypeError('The age is not an integer'); 886 | } 887 | if (value > 200) { 888 | throw new RangeError('Invalid age'); 889 | } 890 | } 891 | 892 | obj[prop] = value; // The default behavior to store the value 893 | 894 | return true; // Indicate success 895 | } 896 | }; 897 | 898 | const person = new Proxy({}, ageValidator); 899 | 900 | person.age = 30; 901 | console.log(person.age); // 30 902 | person.age = 'old'; // Throws an exception 903 | person.age = 200; // Throws an exception 904 | ``` 905 | 906 | **[⬆ Back to Top](#table-of-contents)** 907 | 908 | 20. ### Promises 909 | 910 | A promise is an object which represent the eventual completion or failure of an asynchronous operation. 911 | 912 | It is in one of these states: 913 | 914 | **pending:** Represents initial state, neither fulfilled nor rejected. 915 | **fulfilled:** Indicates that the operation is completed successfully. 916 | **rejected:** Indicates that the operation is failed. 917 | 918 | A promise is said to be settled if it is either fulfilled or rejected, but not pending. The instance methods `promise.then()`, `promise.catch()`, and `promise.finally()` are used to associate further action with a promise that becomes settled. And these methods also return a newly generated promise object, which can optionally be used for chaining. 919 | 920 | ![Screenshot](images/promises.png) 921 | 922 | The promise chaining structure would be as below, 923 | 924 | ```js 925 | const promise = new Promise(function(resolve, reject) { 926 | setTimeout(() => resolve(1), 1000); 927 | }); 928 | 929 | promise.then(function(result) { 930 | console.log(result); // 1 931 | 932 | return result * 2; 933 | }).then(function(result) { 934 | console.log(result); // 2 935 | 936 | return result * 3; 937 | }).then(function(result) { 938 | console.log(result); // 6 939 | 940 | return result * 4; 941 | }).catch(function(error){ 942 | console.log(error); 943 | }); 944 | ``` 945 | 946 | **[⬆ Back to Top](#table-of-contents)** 947 | 948 | 21. ### Reflect 949 | 950 | Reflection is the ability of a code to inspect and manipulate variables, properties, and methods of objects at runtime. JavaScript already provides `Object.keys(), Object.getOwnPropertyDescriptor(), and Array.isArray()` methods as classic refection features. In ES6, it has been officially provided through Reflect object. Reflect is a new global object which is used to call methods, construct objects, get and set properties, manipulate and extend properties. 951 | 952 | Unlike most global objects, Reflect is not a constructor. i.e, You cannot use Reflect with the new operator or invoke the Reflect as a function. It is similar to Math and JSON objects in which all the methods of this object are static. 953 | 954 | Let's see the usage of Reflect API with below examples, 955 | 956 | 1. **Creating objects using Reflect.construct();** 957 | 958 | The `construct()` method behaves like the regular new operator, but as a function. It is equivalent to calling new target(...args) with an option to specify a different prototype. The syntax looks like as below, 959 | 960 | ```js 961 | Reflect.construct(target, args [, newTarget]); 962 | ``` 963 | 964 | The method has below parameters, 965 | 966 | 1. target: The target function to call. 967 | 2. argumentsList: An array-like object specifying the arguments with which target should be called. 968 | 3. newTarget: The constructor whose prototype should be used. This is an optional parameter. i.e, If newTarget is not present, its value defaults to target. 969 | 970 | 971 | **Example:** 972 | ```js 973 | class User { 974 | constructor(firstName, lastName) { 975 | this.firstName = firstName; 976 | this.lastName = lastName; 977 | } 978 | get fullName() { 979 | return `${this.firstName} ${this.lastName}`; 980 | } 981 | }; 982 | 983 | let args = ['John', 'Emma']; 984 | 985 | let john = Reflect.construct( 986 | User, 987 | args 988 | ); 989 | 990 | console.log(john instanceof User); 991 | console.log(john.fullName); // John Doe 992 | ``` 993 | 994 | 2. **Calling a function using Reflect.apply():** 995 | Prior to ES6, you can invoke a function with a specified `this` value and arguments by using the `Function.prototype.apply()` method. 996 | 997 | For example, you can call `max()` static method of Math object, 998 | ```js 999 | const max = Function.prototype.apply.call(Math.max, Math, [100, 200, 300]); 1000 | console.log(max); 1001 | ``` 1002 | 1003 | In ES6, Reflect.apply() provides the same features as Function.prototype.apply() but in a less verbose syntax. 1004 | 1005 | ```js 1006 | const max = Reflect.apply(Math.max, Math, [100, 200, 300]); 1007 | console.log(max); 1008 | ``` 1009 | 3. **Defining a property using Reflect.defineProperty():** 1010 | The `Reflect.defineProperty()` method is similar to `Object.defineProperty()` but it returns a Boolean value indicating whether or not the property was defined successfully instead of throwing an exception. 1011 | 1012 | The syntax of this method looks like below, 1013 | ```js 1014 | Reflect.defineProperty(target, propertyName, propertyDescriptor) 1015 | ``` 1016 | 1017 | Let's define the age property on user object, 1018 | ```js 1019 | class User { 1020 | constructor(firstName, lastName) { 1021 | this.firstName = firstName; 1022 | this.lastName = lastName; 1023 | } 1024 | get fullName() { 1025 | return `${this.firstName} ${this.lastName}`; 1026 | } 1027 | }; 1028 | 1029 | let john = new User('John', 'Resig'); 1030 | 1031 | if (Reflect.defineProperty(john, 'age', { 1032 | writable: true, 1033 | configurable: true, 1034 | enumerable: false, 1035 | value: 33, 1036 | })) { 1037 | console.log(john.age); 1038 | } else { 1039 | console.log('Cannot define the age property on the user object.'); 1040 | } 1041 | ``` 1042 | 1043 | 4. **Delete property using Reflect.deleteProperty():** 1044 | 1045 | The `Reflect.deleteProperty()` method is used to delete properties like the delete operator but as a function. It returns Boolean value indicating whether or not the property was successfully deleted. 1046 | 1047 | ```js 1048 | const user = { 1049 | name: 'John', 1050 | age: 33 1051 | }; 1052 | 1053 | console.log(Reflect.deleteProperty(user, 'age')); // true 1054 | console.log(user.age); // undefined 1055 | ``` 1056 | 1057 | 5. **Get property of an object using Reflect.get():** 1058 | The `Reflect.get` method is used to get a property on an object like the property accessor syntax but as a function. 1059 | 1060 | ```js 1061 | const user = { 1062 | name: 'John', 1063 | age: 33 1064 | }; 1065 | 1066 | console.log(Reflect.get(user, 'age')); // 33 1067 | ``` 1068 | 1069 | 6. **:** 1070 | 1071 | **[⬆ Back to Top](#table-of-contents)** 1072 | 1073 | 22. ### Binary and Octal 1074 | 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. 1075 | 1076 | **1. Binary literals:** 1077 | 1078 | 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()` 1079 | 1080 | ```js 1081 | const num = parseInt('110',2); 1082 | console.log(num); // 6 1083 | ``` 1084 | 1085 | Whereas ES6 added support for binary literals using the **0b** prefix followed by a sequence of binary numbers (i.e, 0 and 1). 1086 | 1087 | ```js 1088 | const num = 0b110; 1089 | console.log(num); // 6 1090 | ``` 1091 | 1092 | **2. Octal literals:** 1093 | 1094 | In ES5, to represent an octal literal, you use the zero prefix (0) followed by a sequence of octal digits (from 0 to 7). 1095 | 1096 | ```js 1097 | const num = 055; 1098 | console.log(num); // 45 1099 | 1100 | let invalidNum = 058; 1101 | console.log(invalidNum); // treated as decimal 58 1102 | ``` 1103 | 1104 | Whereas ES6 represents the octal literal by using the prefix **0o** followed by a sequence of octal digits from 0 through 7. 1105 | 1106 | ```js 1107 | const num = 055; 1108 | console.log(num); // 45 1109 | 1110 | const invalidNum = 028; 1111 | console.log(invalidNum); // treated as decimal 28 1112 | ``` 1113 | 1114 | Remember If you use an invalid number in the octal literal, JavaScript will throw a SyntaxError as below, 1115 | 1116 | ```js 1117 | const invalidNum = 028; 1118 | console.log(invalidNum); // SyntaxError 1119 | ``` 1120 | 1121 | **[⬆ Back to Top](#table-of-contents)** 1122 | 1123 | 23. ### Proper Tail Calls 1124 | 1125 | **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. 1126 | 1127 | 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)` 1128 | 1129 | ```js 1130 | function factorial(n) { 1131 | if (n === 0) { 1132 | return 1 1133 | } 1134 | return n * factorial(n - 1) 1135 | } 1136 | console.log(factorial(5)); //120 1137 | ``` 1138 | 1139 | But if you use Tail recursion functions, they keep passing all the necessary data it needs down the recursion without relying on the stack. 1140 | 1141 | ```js 1142 | function factorial(n, acc = 1) { 1143 | if (n === 0) { 1144 | return acc 1145 | } 1146 | return factorial(n - 1, n * acc) 1147 | } 1148 | console.log(factorial(5)); //120 1149 | ``` 1150 | 1151 | The above pattern returns the same output as first one. But the accumulator keeps track of total as an argument without using stack memory on recursive calls. 1152 | 1153 | The browsers which supports PTC do not generate stack overflow instead shows Infinity with below inputs, 1154 | ```js 1155 | console.log(factorial(10)); 1156 | console.log(factorial(100)); 1157 | console.log(factorial(1000)); 1158 | console.log(factorial(10000)); 1159 | ``` 1160 | 1161 | **[⬆ Back to Top](#table-of-contents)** 1162 | 1163 | 24. ### Array find methods 1164 | ES6 introduced few array methods and two of them are `Array.find()` and `Array.findIndex()`. 1165 | 1166 | **Array.find()** 1167 | This method returns the value of the first element in an array that satisfies the given test. Let's take an example of array with all even elements except one element and use `find` method to find the odd element. 1168 | 1169 | ```js 1170 | let arr = [2, 4, 5, 6, 8, 10]; 1171 | 1172 | function isOdd(i) { 1173 | return i % 2 !== 0; 1174 | } 1175 | 1176 | console.log(arr.find(isOdd)); // 5 1177 | ``` 1178 | **Array.findIndex()** 1179 | 1180 | This method returns the index of the first element in the array that satisfies the given test. Let's take an example of array with all even elements except one element and use `findIndex` method to find the index of odd element. 1181 | 1182 | ```js 1183 | let arr = [2, 4, 5, 6, 8, 10]; 1184 | 1185 | function isOdd(i) { 1186 | return i % 2 !== 0; 1187 | } 1188 | 1189 | console.log(arr.findIndex(isOdd)); //2 1190 | ``` 1191 | 1192 | **[⬆ Back to Top](#table-of-contents)** 1193 | 1194 | ## ES2016 Or ES7 1195 | 1196 | ES2015/ES6 introduced a huge set of new features. But ECMAScript 2016 Or ES7 introduced only two new features: 1197 | 1198 | 1. Array.prototype.includes() 1199 | 2. Exponentiation operator 1200 | 1201 | 1. ### Array Includes 1202 | Prior to ES7, you have to use `indexOf` method and compare the result with '-1' to check whether an array element contains particular element or not. 1203 | ```js 1204 | const array = [1,2,3,4,5,6]; 1205 | if(array.indexOf(5) > -1 ){ 1206 | console.log("Found an element"); 1207 | } 1208 | ``` 1209 | Whereas in ES7, `array.prototype.includes()` method is introduced as a direct approach to determine whether an array includes a certain value among its entries or not. 1210 | ```js 1211 | const array = [1,2,3,4,5,6]; 1212 | if(array.includes(5)){ 1213 | console.log("Found an element"); 1214 | } 1215 | ``` 1216 | In addition to this, `Array.prototype.includes()` handles NaN and Undefined values better than `Array.prototype.indexOf()` methods. i.e, If the array contains NaN and Undefined values then `indexOf()` does not return correct index while searching for NaN and Undefined. 1217 | ```js 1218 | let numbers = [1, 2, 3, 4, NaN, ,]; 1219 | console.log(numbers.indexOf(NaN)); // -1 1220 | console.log(numbers.indexOf(undefined)); // -1 1221 | ``` 1222 | On the otherhand, `includes` method is able to find these elements 1223 | ```js 1224 | let numbers = [1, 2, 3, 4, NaN, ,]; 1225 | console.log(numbers.includes(NaN)); // true 1226 | console.log(numbers.includes(undefined)); // true 1227 | ``` 1228 | 1229 | **[⬆ Back to Top](#table-of-contents)** 1230 | 1231 | 2. ### Exponentiation Operator 1232 | 1233 | The older versions of javascript uses `Math.pow` function to find the exponentiation of given numbers. ECMAScript 2016 introduced the exponentiation operator, **(similar to other languages such as Python or F#) to calculate the power computation in a clear representation using infix notation. 1234 | ```js 1235 | //Prior ES7 1236 | const cube = x => Math.pow(x, 3); 1237 | console.log(cube(3)); // 27 1238 | 1239 | //Using ES7 1240 | const cube1 = x => x ** 3; 1241 | console.log(cube1(3)); // 27 1242 | ``` 1243 | 1244 | **[⬆ Back to Top](#table-of-contents)** 1245 | 1246 | ## ES2017 Or ES8 1247 | 1248 | 1. ### Async functions 1249 | 1250 | In ES6, Promises were introduced to solve the famous callback hell problem. When a series of nested asynchronous functions need to be executed in order, it leads to a callback hell 1251 | ```js 1252 | function task() { 1253 | task1((response1) => { 1254 | task2(response1, (response2) => { 1255 | task3(response2, (response3) => { 1256 | // etc... 1257 | }; 1258 | }); 1259 | }); 1260 | } 1261 | ``` 1262 | But the Chained Promises creates complex flow for asynchronous code. 1263 | 1264 | Async functions were introduced as a combination of promises and generators to give us the possibility of writing asynchronous in a synchronous manner. i.e, This function is going to be declared with the `async` keyword which enable asynchronous, promise-based behavior to be written in a cleaner style by avoiding promise chains. These functions can contain zero or more `await` expressions. 1265 | 1266 | Let's take a below async function example, 1267 | 1268 | ```js 1269 | async function logger() { 1270 | let data = await fetch('http://someapi.com/users'); // pause until fetch returns 1271 | console.log(data) 1272 | } 1273 | logger(); 1274 | ``` 1275 | 1276 | **[⬆ Back to Top](#table-of-contents)** 1277 | 1278 | 2. ### Object values 1279 | Similar to Object.keys which iterate over JavaScript object’s keys, Object.values will do the same thing on values. i.e, The Object.values() method is introduced to returns an array of a given object's own enumerable property values in the same order as `for...in` loop. 1280 | 1281 | ```js 1282 | const countries = { 1283 | IN: 'India', 1284 | SG: 'Singapore', 1285 | } 1286 | Object.values(countries) // ['India', 'Singapore'] 1287 | ``` 1288 | 1289 | By the way, non-object argument will be coerced to an object 1290 | 1291 | ```js 1292 | console.log(Object.values(['India', 'Singapore'])); // ['India', 'Singapore'] 1293 | console.log(Object.values('India')); // ['I', 'n', 'd', 'i', 'a'] 1294 | ``` 1295 | 1296 | **[⬆ Back to Top](#table-of-contents)** 1297 | 1298 | 3. ### Object entries 1299 | The `Object.entries()` method is introduced to returns an array of a given object's own enumerable string-keyed property [key, value] pairs in the same order as `for...in` loop. 1300 | ```js 1301 | const countries = { 1302 | IN: 'India', 1303 | SG: 'Singapore', 1304 | } 1305 | Object.entries(countries) // [["IN", "India"], ["SG", "Singapore"]] 1306 | ``` 1307 | By the way, non-object argument will be coerced to an object 1308 | ```js 1309 | const countriesArr = ['India', 'Singapore']; 1310 | console.log(Object.entries(countriesArr)); // [ ['0', 'India'], ['1', 'Singapore']] 1311 | 1312 | const country = 'India'; 1313 | console.log(Object.entries(country)); // [["0", "I"], ["1", "n"], ["2", "d"], ["3", "i"], ["4", "a"]] 1314 | 1315 | console.log(Object.entries(100)); // [], an empty array for any primitive type because it won't have any own properties 1316 | ``` 1317 | 1318 | **[⬆ Back to Top](#table-of-contents)** 1319 | 1320 | 4. ### Object property descriptors 1321 | 1322 | Property descriptors describe the attributes of a property. The `Object.getOwnPropertyDescriptors()` method returns all own property descriptors of a given object. 1323 | 1324 | It provides the below attributes, 1325 | 1326 | 1. **value:** The value associated with the property (data descriptors only). 1327 | 2. **writable:** true if and only if the value associated with the property may be changed 1328 | 3. **get:** A function which serves as a getter for the property. 1329 | 4. **set:** A function which serves as a setter for the property. 1330 | 5. **configurable:** true if and only if the type of this property descriptor may be changed or deleted. 1331 | 6. **enumerable:** true if and only if this property shows up during enumeration of the property. 1332 | 1333 | The usage of finding property descriptors for any property seems to be as below, 1334 | 1335 | ```js 1336 | const profile = { 1337 | age: 42 1338 | }; 1339 | 1340 | const descriptors = Object.getOwnPropertyDescriptors(profile); 1341 | console.log(descriptors); // {age: {configurable: true, enumerable: true, writable: true }} 1342 | ``` 1343 | 1344 | **[⬆ Back to Top](#table-of-contents)** 1345 | 1346 | 5. ### String padding 1347 | Some strings and numbers(money, date, timers etc) need to be represented in a particular format. Both `padStart() & padEnd()` methods introduced to pad a string with another string until the resulting string reaches the supplied length. 1348 | 1349 | 1. **padStart():** Using this method, padding is applied to the left or beginning side of the string. 1350 | 1351 | For example, you may want to show only the last four digits of credit card number for security reasons, 1352 | 1353 | ```js 1354 | const cardNumber = '01234567891234'; 1355 | const lastFourDigits = cardNumber.slice(-4); 1356 | const maskedCardNumber = lastFourDigits.padStart(cardNumber.length, '*'); 1357 | console.log(maskedCardNumber); // expected output: "**********1234" 1358 | ``` 1359 | 1360 | 2. **padEnd():** Using this method, padding is applied to the right or ending side of the string. 1361 | 1362 | For example, the profile information padded for label and values as below 1363 | ```js 1364 | const label1 = "Name"; 1365 | const label2 = "Phone Number"; 1366 | const value1 = "John" 1367 | const value2 = "(222)-333-3456"; 1368 | 1369 | console.log((label1 + ': ').padEnd(20, ' ') + value1); // Name: John 1370 | console.log(label2 + ": " + value2); // Phone Number: (222)-333-3456 1371 | ``` 1372 | 1373 | **[⬆ Back to Top](#table-of-contents)** 1374 | 1375 | 6. ### Shared memory and atomics 1376 | The Atomics is a global object which provides atomic operations to be performed as static methods. They are used with SharedArrayBuffer(fixed-length binary data buffer) objects. The main use cases of these methods are, 1377 | 1378 | 1. **atomic operations:** When memory is shared, multiple threads can read and write the same data in memory. So there would be a chance of loss of data. But atomic operations make sure that predictable values are written and read, that operations are finished before the next operation starts and that operations are not interrupted. 1379 | 1380 | It provides static methods such as add, or, and, xor, load, store, isLockFree etc as demonstrated below. 1381 | 1382 | ```js 1383 | const sharedMemory = new SharedArrayBuffer(1024); 1384 | const sharedArray = new Uint8Array(sharedMemory); 1385 | sharedArray[0] = 10; 1386 | 1387 | Atomics.add(sharedArray, 0, 20); 1388 | console.log(Atomics.load(sharedArray, 0)); // 30 1389 | 1390 | Atomics.sub(sharedArray, 0, 10); 1391 | console.log(Atomics.load(sharedArray, 0)); // 20 1392 | 1393 | Atomics.and(sharedArray, 0, 5); 1394 | console.log(Atomics.load(sharedArray, 0)); // 4 1395 | 1396 | Atomics.or(sharedArray, 0, 1); 1397 | console.log(Atomics.load(sharedArray, 0)); // 5 1398 | 1399 | Atomics.xor(sharedArray, 0, 1); 1400 | console.log(Atomics.load(sharedArray, 0)); // 4 1401 | 1402 | Atomics.store(sharedArray, 0, 10); // 10 1403 | 1404 | Atomics.compareExchange(sharedArray, 0, 5, 10); 1405 | console.log(Atomics.load(sharedArray, 0)); // 10 1406 | 1407 | Atomics.exchange(sharedArray, 0, 10); 1408 | console.log(Atomics.load(sharedArray, 0)); //10 1409 | 1410 | Atomics.isLockFree(1); // true 1411 | ``` 1412 | 1413 | 2. **waiting to be notified:** 1414 | Both `wait()` and `notify()` methods provides ways for waiting until a certain condition becomes true and are typically used as blocking constructs. 1415 | 1416 | Let's demonstrate this functionality with reading and writing threads. 1417 | 1418 | First define a shared memory and array 1419 | ```js 1420 | const sharedMemory = new SharedArrayBuffer(1024); 1421 | const sharedArray = new Int32Array(sharedMemory); 1422 | ``` 1423 | 1424 | A reading thread is sleeping and waiting on location 0 which is expected to be 10. You can observe a different value after the value overwritten by a writing thread. 1425 | ```js 1426 | Atomics.wait(sharedArray, 0, 10); 1427 | console.log(sharedArray[0]); // 100 1428 | ``` 1429 | 1430 | Now a writing thread stores a new value(e.g, 100) and notifies the waiting thread, 1431 | ```js 1432 | Atomics.store(sharedArray, 0, 100); 1433 | Atomics.notify(sharedArray, 0, 1); 1434 | ``` 1435 | 1436 | 7. ### Trailing commas 1437 | Trailing commas are allowed in parameter definitions and function calls 1438 | ```js 1439 | function func(a,b,) { // declaration 1440 | console.log(a, b); 1441 | } 1442 | func(1,2,); // invocation 1443 | ``` 1444 | But if the function parameter definition or function call only contains a comma, a syntax error will be thrown 1445 | ```js 1446 | function func1(,) { // SyntaxError: missing formal parameter 1447 | console.log('no args'); 1448 | }; 1449 | func1(,); // SyntaxError: expected expression, got ',' 1450 | ``` 1451 | 1452 | **Note:** Trailing commas are not allowed in Rest Parameters and JSON. 1453 | 1454 | **[⬆ Back to Top](#table-of-contents)** 1455 | 1456 | ## ES2018 Or ES9 1457 | 1458 | 1. ### Async iterators 1459 | 1460 | ECMAScript 6 provides built-in support for synchronously iterating over data using iterators. Both strings and collections objects such as Set, Map, and Array come with a Symbol.iterator property which makes them iterable. 1461 | 1462 | ```js 1463 | const arr = ['a', 'b', 'c', 'd']; 1464 | const syncIterator = arr[Symbol.iterator](); 1465 | 1466 | console.log(syncIterator.next()); // {value: a, done: false} 1467 | console.log(syncIterator.next()); // {value: b, done: false} 1468 | console.log(syncIterator.next()); // {value: c, done: false} 1469 | console.log(syncIterator.next()); // {value: d, done: false} 1470 | console.log(syncIterator.next()); // {value: undefined, done: true} 1471 | ``` 1472 | 1473 | But these iterators are only suitable for representing synchronous data sources. 1474 | 1475 | In order to access asynchronous data sources, ES2018 introduced the AsyncIterator interface, an asynchronous iteration statement (for-await-of), and async generator functions. 1476 | 1477 | **[⬆ Back to Top](#table-of-contents)** 1478 | 1479 | 2. ### Object rest and spread operators 1480 | 1481 | ES2015 or ES6 introduced both rest parameters and spread operators to convert arguments to array and vice versa using three-dot(...) notation. 1482 | 1. Rest parameters can be used to convert function arguments to an array 1483 | 1484 | ```js 1485 | function myfunc(p1, p2, ...p3) { 1486 | console.log(p1, p2, p3); // 1, 2, [3, 4, 5, 6] 1487 | } 1488 | myfunc(1, 2, 3, 4, 5, 6); 1489 | ``` 1490 | 1491 | 2. The spread operator works in the opposite way by converting an array into separate arguments in order to pass to a function 1492 | 1493 | ```js 1494 | const myArray = [10, 5, 25, -100, 200, -200]; 1495 | console.log( Math.max(...myArray) ); // 200 1496 | ``` 1497 | 1498 | ES2018 enables this rest/spread behavior for objects as well. 1499 | 1500 | 1. You can pass object to a function 1501 | 1502 | ```js 1503 | function myfunc1({ a, ...x }) { 1504 | console.log(a, x); // 1, { b: 2, c: 3, d:4 } 1505 | } 1506 | myfunc1({ 1507 | a: 1, 1508 | b: 2, 1509 | c: 3, 1510 | d: 4 1511 | }); 1512 | ``` 1513 | 1514 | 2. The spread operator can be used within other objects 1515 | 1516 | ```js 1517 | const myObject = { a: 1, b: 2, c: 3, d:4 }; 1518 | const myNewObject = { ...myObject, e: 5 }; // { a: 1, b: 2, c: 3, d: 4, e: 5 } 1519 | ``` 1520 | 1521 | **[⬆ Back to Top](#table-of-contents)** 1522 | 1523 | 3. ### Promise finally 1524 | 1525 | Sometimes you may need to avoid duplicate code in the then() and catch() methods. 1526 | 1527 | ```js 1528 | myPromise 1529 | .then(result => { 1530 | // process the result and then clean up the resources 1531 | }) 1532 | .catch(error => { 1533 | // handle the error and then clean up the resources 1534 | }); 1535 | ``` 1536 | 1537 | The `finally()` method is useful if you want to do some processing or resource cleanup once the promise is settled(i.e either fulfilled or rejected). 1538 | 1539 | Let's take a below example to hide the loading spinner after the data is fetched and processed. 1540 | 1541 | ```js 1542 | let isLoading = true; 1543 | fetch('http://somesite.com/users') 1544 | .then(data => data.json()) 1545 | .catch(err => console.error(err)) 1546 | .finally(() => { 1547 | isLoading = false; 1548 | console.log('Finished loading!!'); 1549 | }) 1550 | ``` 1551 | 1552 | **[⬆ Back to Top](#table-of-contents)** 1553 | 1554 | ## ES2019 Or ES10 1555 | 1556 | 1. ### Array flat and flatMap 1557 | 1558 | Prior to ES2019, you need to use `reduce() or concat()` methods to get a flat array. 1559 | 1560 | ```js 1561 | function flatten(arr) { 1562 | const flat = [].concat(...arr); 1563 | return flat.some(Array.isArray) ? flatten(flat) : flat; 1564 | } 1565 | flatten([ [1, 2, 3], ['one', 'two', 'three', [22, 33] ], ['a', 'b', 'c'] ]); 1566 | ``` 1567 | 1568 | In ES2019, the `flat()` method is introduced to 'flattens' the nested arrays into the top-level array. The functionality of this method is similar to Lodash's `_.flattenDepth()` function. This method accepts an optional argument that specifies the number of levels a nested array should be flattened and the default nested level is 1. 1569 | **Note:** If there are any empty slots in the array, they will be discarded. 1570 | 1571 | ```js 1572 | const numberArray = [[1, 2], [[3], 4], [5, 6]]; 1573 | const charArray = ['a', , 'b', , , ['c', 'd'], 'e']; 1574 | const flattenedArrOneLevel = numberArray.flat(1); 1575 | const flattenedArrTwoLevel = numberArray.flat(2); 1576 | const flattenedCharArrOneLevel = charArray.flat(1); 1577 | 1578 | console.log(flattenedArrOneLevel); // [1, 2, [3], 4, 5, 6] 1579 | console.log(flattenedArrTwoLevel); // [1, 2, 3, 4, 5, 6] 1580 | console.log(flattenedCharArrOneLevel); // ['a', 'b', 'c', 'd', 'e'] 1581 | ``` 1582 | 1583 | Whereas, **flatMap()** method combines `map()` and `flat()` into one method. It first creates a new array with the return value of a given function and then concatenates all sub-array elements of the array. 1584 | ```js 1585 | const numberArray1 = [[1], [2], [3], [4], [5]]; 1586 | 1587 | console.log(numberArray1.flatMap(value => [value * 10])); // [10, 20, 30, 40, 50] 1588 | ``` 1589 | 1590 | **[⬆ Back to Top](#table-of-contents)** 1591 | 1592 | 2. ### Object fromEntries 1593 | 1594 | In JavaScript, it is very common to transforming data from one format. ES2017 introduced `Object.entries()` method to objects into arrays. 1595 | 1596 | **Object to Array:** 1597 | 1598 | ```js 1599 | const obj = {'a': '1', 'b': '2', 'c': '3' }; 1600 | const arr = Object.entries(obj); 1601 | console.log(arr); // [ ['a', '1'], ['b', '2'], ['c', '3'] ] 1602 | ``` 1603 | 1604 | But if you want to get the object back from an array then you need iterate and convert it as below, 1605 | 1606 | ```js 1607 | const arr = [ ['a', '1'], ['b', '2'], ['c', '3'] ]; 1608 | let obj = {} 1609 | for (let [key, val] of arr) { 1610 | obj[key] = val; 1611 | } 1612 | console.log(obj); 1613 | ``` 1614 | We need a straightforward way to avoid this iteration. In ES2019, `Object.fromEntries()` method is introduced which performs the reverse of `Object.entries()` behavior. The above loop can be avoided easily as below, 1615 | 1616 | **Iterable( e.g Array or Map) to Object** 1617 | ```js 1618 | const arr = [ ['a', '1'], ['b', '2'], ['c', '3'] ]; 1619 | const obj = Object.fromEntries(arr); 1620 | console.log(obj); // { a: "1", b: "2", c: "3" } 1621 | ``` 1622 | One of the common case of this method usage is working with query params of an URL, 1623 | ```js 1624 | const paramsString = 'param1=foo¶m2=baz'; 1625 | const searchParams = new URLSearchParams(paramsString); 1626 | 1627 | Object.fromEntries(searchParams); // => {param1: "foo", param2: "baz"} 1628 | ``` 1629 | 1630 | **[⬆ Back to Top](#table-of-contents)** 1631 | 1632 | 3. ### String trimStart and trimEnd 1633 | In order to make consistency with padStart/padEnd, ES2019 provided the standard functions named as `trimStart` and `trimEnd` to trim white spaces on the beginning and ending of a string. However for web compatibility(avoid any breakage) `trimLeft` and `trimRight` will be an alias for `trimStart` and `trimEnd` respectively. 1634 | 1635 | Let's see the usage with an example, 1636 | ```js 1637 | //Prior ES2019 1638 | let messageOne = " Hello World!! "; 1639 | console.log(messageOne.trimLeft()); //Hello World!! 1640 | console.log(messageOne.trimRight()); // Hello World!! 1641 | 1642 | //With ES2019 1643 | let messageTwo = " Hello World!! "; 1644 | console.log(messageTwo.trimStart()); //Hello World!! 1645 | console.log(messageTwo.trimEnd()); // Hello World!! 1646 | ``` 1647 | 1648 | **[⬆ Back to Top](#table-of-contents)** 1649 | 1650 | 4. ### Symbol description 1651 | 1652 | While creating symbols, you also can add a description to it for debugging purposes. But there was no method to access the description directly before ES2019. Considering this, ES2019 introduced a read-only description property to retrieve a string containing the description of the Symbol. 1653 | 1654 | This gives the possibility to access symbol description for different variations of Symbol objects 1655 | 1656 | ```js 1657 | console.log(Symbol('one').description); // one 1658 | 1659 | console.log(Symbol.for('one').description); // "one" 1660 | 1661 | console.log(Symbol('').description); // '' 1662 | 1663 | console.log(Symbol().description); // undefined 1664 | 1665 | console.log(Symbol.iterator.description); // "Symbol.iterator" 1666 | ``` 1667 | 1668 | **[⬆ Back to Top](#table-of-contents)** 1669 | 1670 | 5. ### Optional catch binding 1671 | Prior to ES9, if you don't need `error` variable and omit the same variable then catch() clause won't be invoked. Also, the linters complain about unused variables. Inorder to avoid this problem, the optional catch binding feature is introduced to make the binding parameter optional in the catch clause. If you want to completely ignore the error or you already know the error but you just want to react to that the this feature is going to be useful. 1672 | 1673 | Let's see the below syntax difference between the versions, 1674 | ```js 1675 | // With binding parameter( a.age - b.age); 1754 | ``` 1755 | **[⬆ Back to Top](#table-of-contents)** 1756 | 1757 | 8. ### Function.toString() 1758 | Functions have an instance method called `toString()` which return a string to represent the function code. Previous versions of ECMAScript removes white spaces,new lines and comments from the function code but it has been retained with original source code in ES2020. 1759 | 1760 | ```js 1761 | function sayHello(message) { 1762 | let msg = message; 1763 | //Print message 1764 | console.log(`Hello, ${msg}`); 1765 | } 1766 | 1767 | console.log(sayHello.toString()); 1768 | // function sayHello(message) { 1769 | // let msg = message; 1770 | // //Print message 1771 | // console.log(`Hello, ${msg}`); 1772 | // } 1773 | ``` 1774 | 1775 | **[⬆ Back to Top](#table-of-contents)** 1776 | 1777 | 9. ### Private Class Variables 1778 | In ES6, the classes are introduced to create reusable modules and variables are declared in clousure to make them private. Where as in ES2020, private class variables are introduced to allow the variables used in the class only. By just adding a simple hash symbol in front of our variable or function, you can reserve them entirely for internal to the class. 1779 | ```js 1780 | class User { 1781 | #message = "Welcome to ES2020" 1782 | 1783 | login() { console.log(this.#message) } 1784 | } 1785 | 1786 | const user = new User() 1787 | 1788 | user.login() // Welcome to ES2020 1789 | console.log(user.#message) // Uncaught SyntaxError: Private field '# 1790 | ``` 1791 | 1792 | **Note:** As shown in the above code, If you still try to access the variable directly from the object then you will receive syntax error. 1793 | 1794 | **[⬆ Back to Top](#table-of-contents)** 1795 | 1796 | ## ES2020 Or ES11 1797 | 1798 | ES2020 is the current newer version of ECMAScript corresponding to the year 2020. This is the eleventh edition of the ECMAScript Language Specification. Even though this release doesn't bring as many features as ES6, it included some really useful features. 1799 | 1800 | Most of these features already supported by some browsers and try out with babel parser support for unsupported features. This edition is set for final approval by the ECMA general assembly in June, 2020. The [ECMAScript 2020 (ES2020) language specification](https://tc39.es/ecma262/2020/) is ready now. 1801 | 1802 | 1. ### BigInt 1803 | In earlier JavaScript version, there is a limitation of using the Number type. i.e, You cannot safely represent integer values(`Number` primitive) larger than pow(2, 53). In ES2020, 1804 | 1805 | `BigInt` is introduced as the 7th primitive type to represent whole numbers(integers with arbitrary precision) larger than pow(2, 53) - 1(or 9007199254740991 or Number.MAX_SAFE_INTEGER). This is been created by appending `n` to the end of an integer literal or by calling the function BigInt(). 1806 | 1807 | ```js 1808 | // 1. Current number system 1809 | const max = Number.MAX_SAFE_INTEGER; 1810 | console.log(max + 1) // 9007199254740992 1811 | console.log(max + 2) // 9007199254740992 1812 | 1813 | // 2. BigInt representation 1814 | const bigInt = 9007199254740991n; 1815 | const bigIntConstructorRep = BigInt(9007199254740991); // 9007199254740991n 1816 | const bigIntStringRep = BigInt("9007199254740991"); // 9007199254740991n 1817 | 1818 | // 3. Typeof usage 1819 | 1820 | console.log(typeof 1)// number 1821 | console.log(typeof 1n)// bigint 1822 | console.log(typeof BigInt('1'))// bigint 1823 | 1824 | // 4. Operators 1825 | 1826 | const previousMaxNum = BigInt(Number.MAX_SAFE_INTEGER); 1827 | console.log(previousMaxNum + 2n); //9007199254740993n (this was not possible before) 1828 | console.log(previousMaxNum -2n); //9007199254740990n 1829 | console.log(previousMaxNum * 2n); //18014398509481982n 1830 | console.log(previousMaxNum % 2n); //1n 1831 | console.log(previousMaxNum / 2n); // 4503599627370495n 1832 | 1833 | // 5. comparison 1834 | console.log(1n === 1); // false 1835 | console.log(1n === BigInt(1)); // true 1836 | console.log(1n == 1); // true 1837 | ``` 1838 | 1839 | **[⬆ Back to Top](#table-of-contents)** 1840 | 1841 | 2. ### Dynamic Import 1842 | Static imports supports some of the important use cases such as static analysis, bundling tools, and tree shaking, it is also it's desirable to be able to dynamically load parts of a JavaScript application at runtime. 1843 | 1844 | The new feature `dynamic import` is introduced to load a module conditionally or on demand. Since it returns a promise for the module namespace object of the requested module, the module can be resolved or import can now be assigned to a variable using async/await as below 1845 | ```js 1846 | 1855 | ``` 1856 | ```js 1857 | 1865 | ``` 1866 | and the imported module appears with both default and named exports 1867 | ```js 1868 | export default () => { 1869 | return "Hello, default export"; 1870 | } 1871 | export const sayGoodBye = () => { 1872 | return "Bye, named export" 1873 | } 1874 | ``` 1875 | 1876 | **Note:** Dynamic import does not require scripts of `type="module"` 1877 | 1878 | **[⬆ Back to Top](#table-of-contents)** 1879 | 1880 | 3. ### Nullish Coalescing Operator 1881 | The nullish coalescing operator (??) is a logical operator that returns its right-hand side operand when its left-hand side operand is `null` or `undefined`, and otherwise returns its left-hand side operand. This operator replaces `||` operator to provide default values if you treat empty value or '', 0 and NaN as valid values. This is because the logical OR(||) operator treats(empty value or '', 0 and NaN) as falsy values and returns the right operand value which is wrong in this case. Hence, this operator truly checks for `nullish` values instead `falsy` values. 1882 | ```js 1883 | let vehicle = { 1884 | car: { 1885 | name: "", 1886 | speed: 0 1887 | } 1888 | }; 1889 | 1890 | console.log(vehicle.car.name || "Unknown"); // Unknown 1891 | console.log(vehicle.car.speed || 90); // 90 1892 | 1893 | console.log(vehicle.car.name ?? "Unknown"); // ""(empty is valid case for name) 1894 | console.log(vehicle.car.speed ?? 90); // 0(zero is valid case for speed) 1895 | ``` 1896 | In a short note, nullish operator returns a non-nullish value and || operator returns truthy values. 1897 | 1898 | **[⬆ Back to Top](#table-of-contents)** 1899 | 1900 | 4. ### String matchAll 1901 | 1902 | There is `String#match` method to get all the matches of a string against a regular expression by iterating for each match. However this method gives you the substrings that match. 1903 | 1904 | The `String#matchAll()` is a new method added to String prototype, which returns an iterator of all results matching a string against a regular expression. 1905 | ```js 1906 | const regex = /t(e)(st(\d?))/g; 1907 | const string = 'test1test2'; 1908 | const matchesIterator = string.matchAll(regex); 1909 | Array.from(matchesIterator, result => console.log(result)); 1910 | ``` 1911 | When you this code in browser console, the matches iterator produces an array for each match including the capturing groups with a few extras. 1912 | ```cmd 1913 | ["test1", "e", "st1", "1", index: 0, input: "test1test2", groups: undefined] 1914 | ["test2", "e", "st2", "2", index: 5, input: "test1test2", groups: undefined] 1915 | ``` 1916 | 1917 | **[⬆ Back to Top](#table-of-contents)** 1918 | 1919 | 5. ### Optional chaining 1920 | 1921 | In JavaScript, Long chains of property accesses is quite error-prone if any of them evaluates to `null` or `undefined` value. Also, it is not a good idea to check property existence on each item which in turn leads to a deeply-nested structured `if` statements. 1922 | 1923 | Optional chaining is a new feature that can make your JavaScript code look cleaner and robust by appending(?.) operator to stop the evaluation and return undefined if the item is undefined or null. 1924 | By the way, this operator can be used together with nullish coalescing operator to provide default values 1925 | ```js 1926 | let vehicle = { 1927 | }; 1928 | 1929 | let vehicle1 = { 1930 | car: { 1931 | name: 'ABC', 1932 | speed: 90 1933 | } 1934 | }; 1935 | 1936 | console.log(vehicle.car.name); // TypeError: Cannot read property 'name' of undefined 1937 | 1938 | console.log(vehicle.car?.name); // Undefined 1939 | console.log(vehicle.car?.speed); // Undefined 1940 | 1941 | console.log(vehicle1.car?.name); // ABC 1942 | console.log(vehicle1.car?.speed); // 90 1943 | 1944 | console.log(vehicle.car?.name ?? "Unknown"); // Unknown 1945 | console.log(vehicle.car?.speed ?? 90); // 90 1946 | ``` 1947 | 1948 | **[⬆ Back to Top](#table-of-contents)** 1949 | 1950 | 6. ### Promise.allSettled 1951 | 1952 | It is really helpful to log(especially to debug errors) about each promise when you are handling multiple promises. The `Promise.allSettled()` method returns a new promise that resolves after all of the given promises have either fulfilled or rejected, with an array of objects describing the outcome of each promise. 1953 | ```js 1954 | const promise1 = new Promise((resolve, reject) => setTimeout(() => resolve(100), 1000)); 1955 | 1956 | const promise2 = new Promise((resolve, reject) => setTimeout(reject, 1000)); 1957 | 1958 | Promise.allSettled([promise1, promise2]).then(data => console.log(data)); // [ 1959 | // Object { status: "fulfilled", value: 100}, 1960 | // Object { status: "rejected", reason: undefined} 1961 | // ] 1962 | ``` 1963 | As per the output, each outcome object returns `status` field which denotes either "fulfilled"(value present) or "rejected"(reason present) 1964 | 1965 | **[⬆ Back to Top](#table-of-contents)** 1966 | 1967 | 7. ### globalThis 1968 | Prior to ES2020, you need to write different syntax in different JavaScript environments(cross-platforms) just to access the global object. It is really a hard time for developers because you need to use `window, self, or frames` on the browser side, `global` on the nodejs, `self` on the web workers side. 1969 | 1970 | On the other hand, `this` keyword can be used inside functions for non-strict mode but it gives undefined in strict mode. If you think about `Function('return this')()` as a solution for above environments, it will fail for CSP enabled environments(where eval() is disabled). 1971 | 1972 | In the older versions, you can use es6-shim as below, 1973 | ```js 1974 | var getGlobal = function () { 1975 | if (typeof self !== 'undefined') { return self; } 1976 | if (typeof window !== 'undefined') { return window; } 1977 | if (typeof global !== 'undefined') { return global; } 1978 | throw new Error('unable to locate global object'); 1979 | }; 1980 | 1981 | var globals = getGlobal(); 1982 | 1983 | if (typeof globals.setTimeout !== 'function') { 1984 | console.log('no setTimeout in this environment or runtime'); 1985 | } 1986 | ``` 1987 | In ES2020, `globalThis` property is introduced to provide a standard way of accessing the global this value across environments. 1988 | ```js 1989 | if (typeof globalThis.setTimeout !== 'function') { 1990 | console.log('no setTimeout in this environment or runtime'); 1991 | } 1992 | ``` 1993 | 1994 | 8. ### import.meta 1995 | 1996 | The `import.meta` object was created by the ECMAScript implementation with a null prototype to get context-specific metadata about a JavaScript module. 1997 | Let's say you are trying to load `my-module` from a script, 1998 | ```js 1999 | 2000 | ``` 2001 | Now you can access meta information(base URL of the module) about the module using the import.meta object 2002 | ```js 2003 | console.log(import.meta); // { url: "file:///home/user/my-module.js" } 2004 | ``` 2005 | The above URL can be either URL from which the script was obtained (for external scripts), or the document base URL of the containing document (for inline scripts). 2006 | 2007 | **Note:** Remember `import` is not really an object but `import.meta` is provided as an object which is extensible, and its properties are writeable, configurable, and enumerable. 2008 | 2009 | 9. ### for..in order 2010 | 2011 | Prior to ES2020, the specifications did not specify in which order for (a in b) should run. Even though most of the javascript engines/browsers loop over the properties of an object in the order in which they were defined, it is not the case with all scenarios. This has been officially standardized in ES2020. 2012 | ```js 2013 | var object = { 2014 | 'a': 2, 2015 | 'b': 3, 2016 | 'c': 4 2017 | } 2018 | 2019 | 2020 | for(let key in object) { 2021 | console.log(key); // a b c 2022 | } 2023 | ``` 2024 | 2025 | **[⬆ Back to Top](#table-of-contents)** 2026 | 2027 | ## ES2021 Or ES12 2028 | ECMAScript 2021 or ES12 has been released in mid of 2021 with few important features which can be used JavaScript. 2029 | 2030 | 1. ### replaceAll 2031 | The new `replaceAll()` method from `String` prototype is used to replace all the occurrences of a string from another string value. Earlier it was not possible to replace all the instances of a substring without the use of regex. 2032 | 2033 | **Before ES2021** 2034 | ```javascript 2035 | console.log('10101010'.replace(new RegExp('0', 'g'), '1')); // 11111111 2036 | console.log('01010101'.replace(/0/g, '1')); // 11111111 2037 | ``` 2038 | 2039 | **After ES2021** 2040 | ```javascript 2041 | console.log('10101010'.replaceAll('0', '1')); // 11111111 2042 | console.log('01010101'.replaceAll('0', '1')); // 11111111 2043 | ``` 2044 | 2045 | 2. ### promise.any 2046 | The new `promise.any` method takes multiple promises and resolves to the value of the first promise which is successfully fulfilled. 2047 | 2048 | ```javascript 2049 | let promise1 = new Promise((resolve) => setTimeout(resolve, 100, 'Resolves after 100ms')); 2050 | let promise2 = new Promise((resolve) => setTimeout(resolve, 200, 'Resolves after 200ms')); 2051 | let promise3 = new Promise((resolve, reject) => setTimeout(reject, 0) ); 2052 | 2053 | let promises = [promise1, promise2, promise3]; 2054 | 2055 | Promise.any(promises) 2056 | .then( value => console.log(value)); // Resolves after 100ms 2057 | ``` 2058 | 2059 | In case none of the promises resolved then it throws `AggregateError` exception. 2060 | 2061 | ```javascript 2062 | (async () => { 2063 | try { 2064 | const output = await Promise.any([ 2065 | Promise.reject('Error 1'), 2066 | Promise.reject('Error 2'), 2067 | Promise.reject('Error 3'), 2068 | ]); 2069 | console.log(`Output: ${output}`); 2070 | } catch (err) { 2071 | console.log(`Error: ${err.errors}`); 2072 | } 2073 | })(); 2074 | // Error: Error1,Error2,Error3 2075 | ``` 2076 | 3. ### WeakRef 2077 | WeakRef provides two new pieces of functionality 2078 | 1. creating weak references to objects with the WeakRef class 2079 | 2. running user-defined finalizers after objects are garbage-collected, with the FinalizationRegistry class 2080 | 2081 | **WeakRef:** 2082 | weak reference is a reference to an object that doesn’t prevent garbage collection if it is the only reference to the object in the memory.It’s useful when we don’t want to keep the object in memory forever(e.g, WebSocket). The main use of weak references is to implement caches or mappings to large objects for which you don't need to keep it in memory for rarely used objects. 2083 | 2084 | Prior to ES12, WeakMaps and WeakSets are the only way to kind-of-weakly reference an object in JavaScript. Whereas WeakRef in ES12 provides actual weak references, enabling a window into the lifetime of an object. 2085 | 2086 | Let's see an example of a weak reference object using `WeakRef` constructor and read the reference using `deref()` method 2087 | 2088 | ```javascript 2089 | const myObject = new WeakRef({ 2090 | name: ‘Sudheer’, 2091 | age: 34 2092 | }); 2093 | console.log(myObject.deref()); //output: {name: “Sudheer”, age: 35} 2094 | console.log(myObject.deref().name); //output: Sudheer 2095 | ``` 2096 | **Finalizers:** 2097 | A `FinalizationRegistry` object lets you request a callback when an object is garbage-collected. It works as a cleanup callback. 2098 | 2099 | ```javascript 2100 | // Create new FinalizationRegistry: 2101 | const reg = new FinalizationRegistry((val) => { 2102 | console.log(val); 2103 | }); 2104 | 2105 | (() => { 2106 | // Create new object: 2107 | const obj = {} 2108 | 2109 | // Register finalizer for the "obj" as first argument and value for callback function as second argument: 2110 | reg.register(obj, 'obj has been garbage-collected.') 2111 | })(); 2112 | ``` 2113 | 2114 | **Note:** The finalization callback does not run immediately after garbage-collecting the event listener, so don't use it for important logic or metrics. 2115 | 4. ### Numeric Separators 2116 | Numeric separators are helpful to read large numbers(or numeric literals) in JavaScript by providing separation between digits using underscores(_). In other words, numeric literals are more readable by creating a visual separation between groups of digits. 2117 | 2118 | For example, one billion and one trillion becomes more readable with _ numeric separator, 2119 | 2120 | ```javascript 2121 | const billion = 1000_000_000; 2122 | console.log(billion); // 1000000000 2123 | 2124 | const trillion = 1000_000_000_000n; // BigInt number 2125 | console.log(trillion); // 1000000000000 2126 | ``` 2127 | 2128 | It can be used for binary and hex literals as well. 2129 | 2130 | ```javascript 2131 | const binaryLiteral = 0b1010_1010; 2132 | console.log(binaryLiteral); 2133 | const hexLiteral = 0xFF_FF_FF_FF; 2134 | console.log(hexLiteral); 2135 | ``` 2136 | 2137 | **Note:** The separator can be placed anywhere within the number for readability purpose. 2138 | 2139 | 5. ### Logical Operators 2140 | Logical assignment operators combines the logical operations(&&, || or ??) with assignment. They are quite useful for assigning default values to variables. 2141 | 2142 | **&&=:** 2143 | 2144 | The `&&=` operator performs the assignment only when the left operand is truthy. 2145 | ```javascript 2146 | let x = 10; 2147 | let y = 20; 2148 | x &&= y; 2149 | console.log(x); // 20 2150 | ``` 2151 | 2152 | The above logical assignment operation can be expanded to: 2153 | 2154 | ```javascript 2155 | x = x && (x = y); 2156 | (OR) 2157 | if (x) { 2158 | x = y; 2159 | } 2160 | ``` 2161 | 2162 | **||=:** 2163 | 2164 | The ||= operator performs the assignment only when the left operand is falsy. 2165 | ```javascript 2166 | let x = 0; 2167 | let y = 20; 2168 | x ||= y; 2169 | console.log(x); // 20 2170 | ``` 2171 | 2172 | The above logical assignment operation can be expanded to: 2173 | 2174 | ```javascript 2175 | x = x || (x = y); 2176 | (OR) 2177 | if (!x) { 2178 | x = y; 2179 | } 2180 | ``` 2181 | 2182 | **??=:** 2183 | 2184 | The ??= operator performs the assignment only when the left operand is null or undefined. 2185 | ```javascript 2186 | let x; 2187 | let y = 1; 2188 | x ??= y; 2189 | console.log(x); // 1 2190 | ``` 2191 | 2192 | The above logical assignment operation can be expanded to: 2193 | 2194 | ```javascript 2195 | x ?? (x = y); 2196 | (OR) 2197 | if (!x) { 2198 | x = y; 2199 | } 2200 | ``` 2201 | 2202 | **[⬆ Back to Top](#table-of-contents)** 2203 | 2204 | ## ES2022 Or ES13 2205 | ECMAScript 2022 or ES13 has been released in the month of June 2022 with some of important features in JavaScript. 2206 | 2207 | 1. ### Top-level await 2208 | 2209 | In ES2022, it is possible to use `await` outside of the asynchronous (async) function scope, which makes it easier to use at the module level. This feature delays the execution of current and parent modules until the imported module is loaded. 2210 | 2211 | Let's take an example of `await` usage prior to ES2022, 2212 | 2213 | ```javascript 2214 | import posts from './posts'; 2215 | 2216 | const getPosts = async() => { 2217 | let posts = await posts(); 2218 | return posts; 2219 | } 2220 | ``` 2221 | 2222 | The usage of `await` is straightforward with ES2022 as below, 2223 | 2224 | ```javascript 2225 | let posts = await posts(); 2226 | ``` 2227 | 2228 | There are few use cases to know the benefits of this top-level await. 2229 | 2230 | #### Usecases 2231 | 2232 | 1. **Dynamic dependency pathing:** 2233 | When you have a dynamic path for a dependency that depends on a runtime value, then await is helpful to load or import the messages at runtime. 2234 | ```javascript 2235 | const messages = await import(`./messages-${language}.js`); 2236 | ``` 2237 | 2. **Dependency fallbacks:** 2238 | If the imported module is failed to load, then fallback module loaded used to load the dependency. 2239 | ```javascript 2240 | let lodash; 2241 | try { 2242 | lodash = await import('https://first.domain.com/lodash'); 2243 | } catch { 2244 | lodash = await import('https://second.domain.com/lodash'); 2245 | } 2246 | ``` 2247 | 2248 | 3. **Resource initialization:** 2249 | This feature can be used to initialize an app with Database. 2250 | 2251 | ```javascript 2252 | import { dbConnector} from './dbUtils.js' 2253 | //connect to database 2254 | const connection = await dbConnector.connect(); 2255 | export default function(){ 2256 | connection.list() 2257 | } 2258 | ``` 2259 | 2260 | **[⬆ Back to Top](#table-of-contents)** 2261 | 2262 | 2. ### Class fields 2263 | 2264 | ES2015 introduced classes to javascript and class properties are defined in the constructor. In the below example, the Employee class has two fields defined in the constructor. 2265 | 2266 | ```javascript 2267 | class Employee { 2268 | 2269 | constructor() { 2270 | this.name = "John"; //public 2271 | this._age=35; //private 2272 | } 2273 | 2274 | const employee = new Employee(); 2275 | employee.name = "Jack"; 2276 | employee._age =35; //No error 2277 | ``` 2278 | 2279 | In the above example, the private `_age` property can be accessed and modified outside of the class. The prefix `_` is just a naming convention for private field and won't enforce any strict rule. 2280 | 2281 | ES2022 introduced private and static fields to the classes. 2282 | 2283 | 1. **Public and private fields or methods:** 2284 | ES2022 introduced public and private properties or methods like other programming languages. These properties and methods can be defined outside the constructor and private fields prefixed with # symbol followed by variable name. 2285 | 2286 | In the below example, the Employee class has been defined with private variable outside the constructor. 2287 | 2288 | ```javascript 2289 | class Employee { 2290 | name = "John"; 2291 | #age=35; 2292 | constructor() { 2293 | } 2294 | 2295 | #getAge() { 2296 | return #age 2297 | } 2298 | 2299 | } 2300 | 2301 | const employee = new Employee(); 2302 | employee.name = "Jack"; 2303 | employee.#age = 35; // Throws an error 2304 | ``` 2305 | 2306 | 2. **Static fields and methods:** 2307 | 2308 | Static fields and methods are applied to the class level and they can be accessed without an instance of a class. These fields and methods declared with `static` keyword 2309 | 2310 | Let's define a employee class with static field and methods declared with `static` keyword. 2311 | 2312 | ```javascript 2313 | class Employee{ 2314 | name = "John"; 2315 | static #employerName="Github" 2316 | 2317 | static #getEmployerName() { 2318 | return #employerName 2319 | } 2320 | } 2321 | const employee = new Employee(); 2322 | employee.emp = "Jack"; 2323 | employee.#employerName = 35; // Throws an error 2324 | 2325 | ``` 2326 | **[⬆ Back to Top](#table-of-contents)** 2327 | 2328 | 3. ### Array .at() method 2329 | The `.at()` method is used to access an array or string elements by passing the negative index value. i.e, It allows accessing values from the end of an array or from a string. 2330 | 2331 | Before ES2022, You should have to access the elements from the end as below, 2332 | 2333 | ```javascript 2334 | const array = [1, 2, 3, 4, 5]; 2335 | console.log(array[array.length - 2]); //4 2336 | console.log(array.slice(-2)[0]); //4 2337 | 2338 | const string = '12345'; 2339 | console.log(string[string.length - 2]); // '4' 2340 | console.log(string.slice(-2)[0]); // '4' 2341 | ``` 2342 | 2343 | Now you should be able to write: 2344 | 2345 | ```javascript 2346 | const array = [1, 2, 3, 4, 5]; 2347 | console.log(array.at(-2)); // 4 2348 | 2349 | const string = '12345'; 2350 | console.log(string.at(-2)); 2351 | ``` 2352 | 2353 | **[⬆ Back to Top](#table-of-contents)** 2354 | 2355 | 4. ### Error Cause 2356 | 2357 | The `cause` property is added to the Error() constructor as an extra parameter which allow errors to be chained similar to Java-like stack traces in error chains. 2358 | 2359 | In the below example, let's catch an error from JSON processing and re-throw it with a new meaningful message along with the original cause of the error. 2360 | 2361 | ```javascript 2362 | function processUserData(arrayData) { 2363 | return arrayData.map(data => { 2364 | try { 2365 | const json = JSON.parse(data); 2366 | return json; 2367 | } catch (err) { 2368 | throw new Error( 2369 | `Data processing failed`, 2370 | {cause: err} 2371 | ); 2372 | } 2373 | }); 2374 | } 2375 | ``` 2376 | 2377 | **[⬆ Back to Top](#table-of-contents)** 2378 | 2379 | 5. ### hasOwn 2380 | 2381 | The new `Object.hasOwn()` method is a replacement or improved version of `Object.prototype.hasOwnProperty`. It is a static method that returns true if the specified object has the indicated property as its own property. If the property is inherited, or does not exist, the method returns false. 2382 | 2383 | It's not only more concise and readable but also overcomes the below limitations of `hasOwnProperty`. 2384 | 2385 | 1. **When `hasOwnProperty`overwritten:** 2386 | 2387 | There are cases where you need to define customized `hasOwnProperty` on the object. When you try to apply `hasOwnProperty` to determine the own property or not, it throws an error as shown in below example. 2388 | 2389 | ```javascript 2390 | const user = { 2391 | age: 35, 2392 | hasOwnProperty: ()=> { 2393 | return false; 2394 | } 2395 | }; 2396 | 2397 | user.hasOwnProperty('age') // throws a TypeError 2398 | ``` 2399 | 2400 | This issue can be solved by hasOwn method . 2401 | 2402 | ```javascript 2403 | const user = { 2404 | age: 35, 2405 | hasOwnProperty: ()=> { 2406 | return false; 2407 | } 2408 | }; 2409 | 2410 | user.hasOwn('age') // true 2411 | ``` 2412 | 2413 | 2. **Create an object with create(null) function:** 2414 | 2415 | If you create new object with help of create(null) function, then newly created object doesn’t inherit from Object.prototype. So it doesn't have hasOwnProperty method. 2416 | 2417 | Let's take an example to verify hasOwnProperty on an object created with create(null) function. 2418 | ```javascript 2419 | const user = Object.create(null); 2420 | user.age = 35; 2421 | user.hasOwnProperty('age'); // throws a TypeError 2422 | ``` 2423 | 2424 | In this case, hasOwn() method can be used to determine the own property. 2425 | 2426 | ```javascript 2427 | const user = Object.create(null); 2428 | user.age = 35; 2429 | user.hasOwn('age'); // true 2430 | ``` 2431 | 2432 | **[⬆ Back to Top](#table-of-contents)** 2433 | 2434 | 6. ### Regex match indices 2435 | 2436 | Regex match has been upgraded to include more information about the matching groups. The additional information includes starting and ending indices of matches in a RegExp with the usage of `\d` flag in the input string. 2437 | 2438 | Let's take an example of regex pattern on the input string without `\d` flag and the information of the matches. 2439 | 2440 | ```javascript 2441 | const regexPatter = /Jack/g; 2442 | const input = 'Authos: Jack, Alexander and Jacky'; 2443 | const result = [...input.matchAll(regexPatter)]; 2444 | console.log(result[0]); // ['Jack', index: 8, input: 'Authos: Jack, Alex and Jacky', groups: undefined] 2445 | ``` 2446 | 2447 | Whereas `\d` flag provides an additional array with the indices of the different elements that match the regex, 2448 | 2449 | ```javascript 2450 | const regexPatter = /(Jack)/gd; 2451 | const input = 'Authos: Jack, Alexander and Jacky'; 2452 | const result = [...input.matchAll(regexPatter)]; 2453 | console.log(result[0]); // ['Jack', 'Jack', index: 8, input: 'Authos: Jack, Alexander and Jacky', groups: undefined, indices: Array(2)] 2454 | ``` 2455 | 2456 | **[⬆ Back to Top](#table-of-contents)** 2457 | 2458 | ## ES2023 Or ES14 2459 | ECMAScript 2023 or ES14 has been released in the month of June 2023 with some of important features like adding new methods for searching and altering the arrays, supporting symbols as keys for WeakMap API and standardizing the hashbang syntax in JavaScript. 2460 | 2461 | 1. ### Find array from last 2462 | 2463 | This release introduced two new array methods named **findLast()** and **findLastIndex()** to search an array element from the last. Their functionality is similar to **find()** and **findIndex()** but searching starts from the end of an array. These methods are available on **Array** and **TypedArray** prototypes. This feature provides an efficient way for reverse order search by eliminating the process of manual array reversal. 2464 | 2465 | For example, let's see the usage of finding odd elements from end of an array prior to ES2023. There are no direct methods available to search element from end of an array. Hence, the array needs to be reversed first before applying **first()** and **firstIndex()** methods. 2466 | 2467 | ```javascript 2468 | const isOdd = (number) => number % 2 === 1; 2469 | const numbers = [1, 2, 3, 4, 5]; 2470 | const reverseNumbers = [5, 4, 3, 2, 1]; 2471 | 2472 | console.log(reverseNumbers.find(isOdd)); // 5 2473 | console.log(reverseNumbers.findIndex(isOdd)); // 4 2474 | ``` 2475 | This process is going to be simplified in ES2023 release using **findLast()** and **findLastIndex()** methods. 2476 | 2477 | ```javascript 2478 | const isOdd = (number) => number % 2 === 1; 2479 | const numbers = [1, 2, 3, 4, 5]; 2480 | 2481 | console.log(numbers.findLast(isOdd)); // 5 2482 | console.log(numbers.findLastIndex(isOdd)); // 4 2483 | ``` 2484 | 2485 | 2. ### Hashbang syntax 2486 | Hashbang(as known as shebang) grammar has been supported with a sequence of characters(#!) at the beginning of an executable script to define the interpreter for the program to run. In other words, this syntax is helpful to tell the operating system which interpreter to use while executing the script. 2487 | 2488 | For example, the below javascript file will be executed in NodeJS interpreter from Unix commandline. 2489 | ```javascript 2490 | #!/usr/bin/env node 2491 | 'use strict'; 2492 | console.log("Hello world from hashbang syntax"); 2493 | ``` 2494 | 2495 | 3. ### Symbols as weakmap keys 2496 | Prior to ES2023, WeakMaps are only limited to allow objects as keys because objects are unique and cannot be re-created. Since symbols are the only primitives in ECMAScript that allows unique values, WeakMap API has been extended with symbols as keys instead of just using objects. 2497 | 2498 | As an example, the usage of WeakMap with objects as keys prior to ES2023 looks like below 2499 | ```javascript 2500 | const weak = new WeakMap(); 2501 | const objKey = { x:10 }; 2502 | 2503 | weak.set(objKey, "ES2023"); 2504 | console.log(weak.get(objKey)); //ES2023 2505 | ``` 2506 | In ES2023, it is possible to use symbols as keys 2507 | ```javascript 2508 | const weak = new WeakMap(); 2509 | const key = Symbol("ref"); 2510 | weak.set(key, "ES2023"); 2511 | 2512 | console.log(weak.get(key)); //ES2023 2513 | ``` 2514 | 2515 | 4. ### Change array by copy 2516 | Both **Array** and **TypedArray** has built-in methods such as **reverse()**, **sort()** and **splice()** to perform common actions like sorting, reverse the array elements and replacing(or removing) elements. But these methods are mutating the original array. Where as ES2023 has provided additional methods such as **toReversed()**, **toSorted()**, **toSpliced** and **with()** methods which returns new array copies instead of mutating the original array. 2517 | 2518 | For example, these additional methods returns new array copies for number array without mutating the original array as shown below, 2519 | ```javascript 2520 | const numbers = [1, 3, 2, 4, 5]; 2521 | 2522 | // toReversed 2523 | const reversedArray = numbers.toReversed(); 2524 | console.log(reversedArray); // [5, 4, 2, 3, 1] 2525 | console.log(numbers); // [1, 3, 2, 4, 5] 2526 | 2527 | // toSorted 2528 | const sortedArray = numbers.toSorted(); 2529 | console.log(sortedArray); // [1, 2, 3, 4, 5] 2530 | console.log(numbers); // [1, 3, 2, 4, 5] 2531 | 2532 | // toSpliced 2533 | const splicedArray = numbers.toSpliced(1, 3); 2534 | console.log(splicedArray); // [1, 5] 2535 | console.log(numbers); // [1, 3, 2, 4, 5] 2536 | 2537 | // with 2538 | const replaceWithArray = numbers.with(2, 10); 2539 | console.log(replaceWithArray); // [1, 3, 10, 4, 5] 2540 | console.log(numbers); // [1, 3, 2, 4, 5] 2541 | ``` 2542 | 2543 | **[⬆ Back to Top](#table-of-contents)** 2544 | 2545 | ## ES2024 or ES15 2546 | 2547 | ES2024 is planned to be release in June 2024 with a couple of features and enhancements for the developers to make coding in JavaScript more efficient, readable, and robust. 2548 | 2549 | 1. ### GroupBy into objects and maps: 2550 | 2551 | The `Object.groupBy()` method is used to group object elements of an iterable(like array) based on string values returned from a callback function. It returns an object with each group name as key and respective array of elements as value. The elements in the returned object and the original object are the same. i.e, If you change the internal structure of the elements, it will be reflected in both original and returned object. 2552 | 2553 | In the following example, persons are into categories into several groups based on their age. 2554 | 2555 | ```javascript 2556 | const persons = [ 2557 | {name:"John", age:70}, 2558 | {name:"Kane", age:5}, 2559 | {name:"Jack", age:50}, 2560 | {name:"Rambo", age:15} 2561 | ]; 2562 | 2563 | // Callback function to categorize people based on age 2564 | function callbackFunc({ age }) { 2565 | if(age >= 60) { 2566 | return "senior"; 2567 | } else if(age > 17 && age < 60) { 2568 | return "adult"; 2569 | } 2570 | else { 2571 | return "kid"; 2572 | } 2573 | } 2574 | 2575 | const result = Object.groupBy(persons, callbackFunc); 2576 | 2577 | console.log("Kids: "); 2578 | for (let [x,y] of result.kid.entries()) { 2579 | console.log(y.name + " " + y.age); 2580 | } 2581 | 2582 | console.log("Adults: "); 2583 | for (let [x,y] of result.adult.entries()) { 2584 | console.log(y.name + " " + y.age); 2585 | } 2586 | 2587 | console.log("Seniors: "); 2588 | for (let [x,y] of result.senior.entries()) { 2589 | console.log(y.name + " " + y.age); 2590 | } 2591 | ``` 2592 | The `Map.groupBy()` method is also used to group elements of an object but the result is in the form of a map. 2593 | 2594 | ```javascript 2595 | const persons = [ 2596 | {name:"John", age:70}, 2597 | {name:"Kane", age:5}, 2598 | {name:"Jack", age:50}, 2599 | {name:"Rambo", age:15} 2600 | ]; 2601 | 2602 | // Callback function to categorize people based on age 2603 | function callbackFunc({ age }) { 2604 | if(age >= 60) { 2605 | return "senior"; 2606 | } else if(age > 17 && age < 60) { 2607 | return "adult"; 2608 | } 2609 | else { 2610 | return "kid"; 2611 | } 2612 | } 2613 | 2614 | const result = Map.groupBy(persons, callbackFunc); 2615 | 2616 | console.log("Kids: "); 2617 | for (let x of result.get("kid")) { 2618 | console.log(x.name + " " + x.age); 2619 | } 2620 | 2621 | console.log("Adults: "); 2622 | for (let x of result.get("adult")) { 2623 | console.log(x.name + " " + x.age); 2624 | } 2625 | 2626 | console.log("Seniors: "); 2627 | for (let x of result.get("senior")) { 2628 | console.log(x.name + " " + x.age); 2629 | } 2630 | ``` 2631 | 2632 | 2. ### Temporal API 2633 | 2634 | The Temporal API is a modern API for working with dates and times, used to supersede the original Date API. It provides a more comprehensive and user-friendly way to handle date and time manipulation. 2635 | 2636 | It contains the following core objects, 2637 | 1. Temporal.PlainDate 2638 | 2. Temporal.PlainTime 2639 | 3. Temporal.PlainDateTime 2640 | 4. Temporal.PlainYearMonth 2641 | 5. Temporal.PlainMonthDay 2642 | 6. Temporal.ZonedDateTime 2643 | 2644 | 3. ### Well formed unicode strings 2645 | Unicode strings are mainly used for representing a wide range of characters from different languages and symbols. In UTF-16, strings which contain lone surrogates(16-bit Code Unit) are considered as "malformed" or "not well formatted". These lone surrogates can be of two types, 2646 | 2647 | 1. **Leading surrogates:** Range between `0XD800` to `0XDBFF` 2648 | 2. **Trailing Surrogate:** Range between `0XDC00` to `0XDFFF` 2649 | 2650 | Well-Formed Unicode Strings feature introduced below two string methods to check and convert into wellformed strings. 2651 | 2652 | 1. **String.prototype.isWellFormed:** 2653 | This method is used to check if the string contains lone surrogates or not. Returns `true`, if unicode string is not present. The following stings can be verified either as well-formed or not well-formed strings, 2654 | 2655 | ```javascript 2656 | const str1 = "Hello World \uD815"; 2657 | const str2 = "Welcome to ES2024"; 2658 | const str3 = "Welcome to ES2024 😀"; 2659 | 2660 | console.log(str1.isWellFormed()); // false 2661 | console.log(str2.isWellFormed()); // true 2662 | console.log(str2.isWellFormed()); // true 2663 | ``` 2664 | 2665 | **Note:** Emojis are considered as well-formed unicode strings. 2666 | 2667 | 2. **String.prototype.toWellFormed:** 2668 | This method is used to return a string by converting unpaired surrogate(i.e, leading and trailing surrogates) code points with `U+FFFD` Replacement characters. 2669 | 2670 | ```javascript 2671 | const str1 = "Hello World \uD815"; 2672 | const str2 = "Welcome to ES2024"; 2673 | 2674 | console.log(str1.toWellFormed()); // Hello World � 2675 | console.log(str2.toWellFormed()); // Welcome to ES2024 2676 | ``` 2677 | 2678 | These two methods are mainly helpful for developers to work with string encoding without any errors. For example, the below encoding process throws an error due to lone surrogates, 2679 | 2680 | ```javascript 2681 | const url = "https://somedomain.com/query=\uD423"; 2682 | 2683 | try { 2684 | console.log(encodeURI(url)); 2685 | } catch (e) { 2686 | console.log('Error:', e.message); // Expected: URIError: URI malformed 2687 | } 2688 | ``` 2689 | 2690 | After applying `toWellFormed()` method, the lone surrogate is replaced with the Unicode replacement character (U+FFFD). It make sure `encodeURI()` is processed without errors. 2691 | 2692 | ```javascript 2693 | console.log(encodeURI(url.toWellFormed())); // https://somedomain.com/query=%ED%90%A3 2694 | ``` 2695 | 4. ### Atomic waitSync 2696 | The `Atomics.waitAsync()` is a static method that waits asynchronously on a shared memory location and returns a Promise. It is non-blocking as compared to `Atomics.wait()` and can be used on the main thread. The syntax looks like below, 2697 | 2698 | ```javascript 2699 | Atomics.waitAsync(typedArray, ind, val, timeOut); 2700 | ``` 2701 | If the promise is not fulfilled then it will lead to a 'time-out' status otherwise the status will always be 'ok' once the promise has been fulfilled. 2702 | 2703 | Let's take a shared Int32Array. Here it waits asynchronously for position 0 and expects a result 0 waiting for 500ms. 2704 | 2705 | ```javascript 2706 | const arrayBuffer = new SharedArrayBuffer(1024); 2707 | const arr = new Int32Array(arrayBuffer); 2708 | 2709 | Atomics.waitAsync(arr, 0 , 0 , 500); // { async: true, value: Promise {}} 2710 | 2711 | Atomics.notify(arr, 0); // { async: true, value: Promise {: 'ok'} } 2712 | ``` 2713 | After that, the notify method awakes the waiting agent(i.e, array) that are sleeping in waiting queue and the promise is fulfilled. 2714 | 2715 | Remember that, SharedArrayBuffer have been disabled on most browsers unless you specify `Cross-Origin-Opener-Policy` and `Cross-Origin-Embedder-Policy` headers. For example, 2716 | 2717 | ` 2718 | Cross-Origin-Opener-Policy: same-origin 2719 | Cross-Origin-Embedder-Policy: require-corp 2720 | ` 2721 | 2722 | **Note:** There will be a TypeError if typedArray is not an **Int32Array** or **BigInt64Array** that views a SharedArrayBuffer. 2723 | 2724 | 5. ### RegEx v Flag and string properties 2725 | 6. ### Promise withResolvers 2726 | When you are creating a new Promise object, usually you pass `resolve` and `reject` functions to the executor of promise constructor as shown below: 2727 | 2728 | ```javascript 2729 | const promise = new Promise((resolve, reject) =>{ 2730 | setTimeout(() => { Math.random() > 0.5 ? resolve("Success") : reject("Error")}, 1000); 2731 | }); 2732 | promise.then(result => console.log(result)).catch(error => console.error(error)); 2733 | ``` 2734 | 2735 | In this constructor pattern, it is possible call the `resolve` and `reject` functions inside the promise constructor only. But if you want these functions outside of the promise constructor, you often have to write the following boilerplate code. 2736 | 2737 | ```javascript 2738 | let resolve, reject; 2739 | const promise = new Promise((res, rej) => { 2740 | resolve = res; 2741 | reject = rej; 2742 | }); 2743 | 2744 | setTimeout(() => { Math.random() > 0.5 ? resolve("Success") : reject("Error")}, 1000); 2745 | promise.then(result => console.log(result)).catch(error => console.error(error)); 2746 | ``` 2747 | 2748 | The above code is simplified with `Promise.withResolvers()` in ES2024, it's a static method factory that returns an object containing a new Promise along with two functions one for resolve and other one for reject. These two functions corresponding to the two parameters passed to the executor of the Promise() constructor shown in the initial code snippets. 2749 | 2750 | The concise version of earlier code snippet looks like below, 2751 | 2752 | ```javascript 2753 | const { promise, resolve, reject} = Promise.withResolvers(); 2754 | 2755 | setTimeout(() => { Math.random() > 0.5 ? resolve("Success") : reject("Error")},1000); 2756 | promise.then(result => console.log(result)).catch(error => console.error(error)); 2757 | ``` 2758 | 2759 | **[⬆ Back to Top](#table-of-contents)** 2760 | 2761 | ## ES2025 or ES16 2762 | 2763 | ES2025 is planned to be released in June 2025 with several new features and enhancements for JavaScript developers. These features aim to improve developer productivity and code readability. 2764 | 2765 | 1. ### Set Methods 2766 | 2767 | ES2025 introduces several new methods to the Set prototype that make working with sets more convenient and expressive. These methods provide operations commonly found in set theory, such as union, intersection, and difference. 2768 | 2769 | ```javascript 2770 | // Create some sample sets 2771 | const set1 = new Set([1, 2, 3, 4, 5]); 2772 | const set2 = new Set([3, 4, 5, 6, 7]); 2773 | 2774 | // Union - combines elements from both sets 2775 | const union = set1.union(set2); 2776 | console.log([...union]); // [1, 2, 3, 4, 5, 6, 7] 2777 | 2778 | // Intersection - elements present in both sets 2779 | const intersection = set1.intersection(set2); 2780 | console.log([...intersection]); // [3, 4, 5] 2781 | 2782 | // Difference - elements in first set but not in second 2783 | const difference = set1.difference(set2); 2784 | console.log([...difference]); // [1, 2] 2785 | 2786 | // Symmetric Difference - elements in either set but not in both 2787 | const symDifference = set1.symmetricDifference(set2); 2788 | console.log([...symDifference]); // [1, 2, 6, 7] 2789 | ``` 2790 | 2791 | The new Set methods include: 2792 | 2793 | 1. **Set.prototype.union()**: Returns a new Set containing all elements from both sets. 2794 | 2. **Set.prototype.intersection()**: Returns a new Set containing elements present in all sets. 2795 | 3. **Set.prototype.difference()**: Returns a new Set containing elements present in the first set but not in the second. 2796 | 4. **Set.prototype.symmetricDifference()**: Returns a new Set containing elements present in either set but not in both. 2797 | 5. **Set.prototype.isSubsetOf()**: Returns a boolean indicating if the set is a subset of the given set. 2798 | 6. **Set.prototype.isSupersetOf()**: Returns a boolean indicating if the set is a superset of the given set. 2799 | 7. **Set.prototype.isDisjointFrom()**: Returns a boolean indicating if the set has no elements in common with the given set. 2800 | 2801 | These methods can be particularly useful for data processing, filtering, and comparison operations. 2802 | 2803 | ```javascript 2804 | // Practical example: Finding common interests between users 2805 | const user1Interests = new Set(["coding", "reading", "music", "hiking"]); 2806 | const user2Interests = new Set(["gaming", "music", "movies", "hiking"]); 2807 | 2808 | // Find common interests 2809 | const commonInterests = user1Interests.intersection(user2Interests); 2810 | console.log([...commonInterests]); // ["music", "hiking"] 2811 | 2812 | // Find unique interests of user1 2813 | const uniqueInterests = user1Interests.difference(user2Interests); 2814 | console.log([...uniqueInterests]); // ["coding", "reading"] 2815 | ``` 2816 | 2817 | **[⬆ Back to Top](#table-of-contents)** 2818 | 2819 | 2. ### Iterator Helpers 2820 | 2821 | ES2025 introduces Iterator Helpers, which provide a set of utility methods that can be chained together to perform operations on iterators in a more readable and functional way. This feature makes working with iterators more convenient and expressive. 2822 | 2823 | ```javascript 2824 | // Create a generator function 2825 | function* generateNumbers() { 2826 | yield 1; 2827 | yield 2; 2828 | yield 3; 2829 | yield 4; 2830 | yield 5; 2831 | } 2832 | 2833 | // Use iterator helpers to transform the values 2834 | const result = generateNumbers()[Symbol.iterator]() 2835 | .filter(x => x > 1) // Keep only values greater than 1 2836 | .map(x => x * 10) // Multiply each value by 10 2837 | .take(3) // Take only the first 3 values 2838 | .toArray(); // Convert to an array 2839 | 2840 | console.log(result); // [20, 30, 40] 2841 | ``` 2842 | 2843 | The new Iterator Helper methods include: 2844 | 2845 | 1. **Iterator.prototype.map()**: Maps each value in the iterator to a new value. 2846 | 2. **Iterator.prototype.filter()**: Filters values in the iterator based on a predicate. 2847 | 3. **Iterator.prototype.take()**: Takes a specified number of values from the iterator. 2848 | 4. **Iterator.prototype.drop()**: Skips a specified number of values from the iterator. 2849 | 5. **Iterator.prototype.flatMap()**: Maps each value and flattens the result. 2850 | 6. **Iterator.prototype.reduce()**: Reduces the iterator to a single value. 2851 | 7. **Iterator.prototype.toArray()**: Converts the iterator to an array. 2852 | 8. **Iterator.prototype.forEach()**: Executes a function for each value in the iterator. 2853 | 9. **Iterator.prototype.some()**: Checks if some values satisfy a condition. 2854 | 10. **Iterator.prototype.every()**: Checks if all values satisfy a condition. 2855 | 2856 | Iterator Helpers are particularly useful for processing streams of data in a memory-efficient way: 2857 | 2858 | ```javascript 2859 | function* generateUserData() { 2860 | yield { id: 1, name: "Alice", age: 25 }; 2861 | yield { id: 2, name: "Bob", age: 30 }; 2862 | yield { id: 3, name: "Charlie", age: 35 }; 2863 | yield { id: 4, name: "Dave", age: 40 }; 2864 | } 2865 | 2866 | // Find users over 30, extract their names, and take the first 2 2867 | const olderUserNames = generateUserData()[Symbol.iterator]() 2868 | .filter(user => user.age > 30) 2869 | .map(user => user.name) 2870 | .take(2) 2871 | .toArray(); 2872 | 2873 | console.log(olderUserNames); // ["Charlie", "Dave"] 2874 | ``` 2875 | 2876 | **[⬆ Back to Top](#table-of-contents)** 2877 | 2878 | 3. ### Temporal API {#temporal-api-es2025} 2879 | 2880 | The Temporal API is a new date and time API for JavaScript that addresses many of the limitations of the existing Date object. It provides a modern, immutable, and more intuitive way to work with dates and times. 2881 | 2882 | ```javascript 2883 | // Get the current date 2884 | const today = Temporal.Now.plainDate(); 2885 | console.log(today.toString()); // e.g., "2025-06-01" 2886 | 2887 | // Create a specific date 2888 | const date = Temporal.PlainDate.from("2025-06-01"); 2889 | console.log("Year:", date.year); // 2025 2890 | console.log("Month:", date.month); // 6 2891 | console.log("Day:", date.day); // 1 2892 | console.log("Day of week:", date.dayOfWeek); // e.g., 7 (Sunday) 2893 | 2894 | // Date arithmetic 2895 | const futureDate = date.add({ months: 1, days: 15 }); 2896 | console.log(futureDate.toString()); // "2025-07-16" 2897 | 2898 | // Calculate duration between dates 2899 | const duration = date.until(futureDate); 2900 | console.log(duration.toString()); // "P1M15D" (1 month, 15 days) 2901 | ``` 2902 | 2903 | The Temporal API includes several objects for different date/time representations: 2904 | 2905 | 1. **Temporal.Now**: Provides methods to get the current date and time in various formats. 2906 | 2. **Temporal.Instant**: Represents a fixed point in time (like a timestamp). 2907 | 3. **Temporal.PlainDate**: Represents a calendar date without time or timezone. 2908 | 4. **Temporal.PlainTime**: Represents wall-clock time without date or timezone. 2909 | 5. **Temporal.PlainDateTime**: Combines date and time without timezone. 2910 | 6. **Temporal.ZonedDateTime**: Full date, time, and timezone information. 2911 | 7. **Temporal.Duration**: Represents a length of time. 2912 | 8. **Temporal.TimeZone**: Represents a timezone. 2913 | 9. **Temporal.Calendar**: Represents a calendar system. 2914 | 2915 | The Temporal API is particularly useful for applications that need to handle dates and times across different timezones: 2916 | 2917 | ```javascript 2918 | // Calculate flight duration between timezones 2919 | function calculateFlightDuration(departure, arrival) { 2920 | const departureTZ = Temporal.ZonedDateTime.from(departure); 2921 | const arrivalTZ = Temporal.ZonedDateTime.from(arrival); 2922 | return departureTZ.until(arrivalTZ); 2923 | } 2924 | 2925 | const flightDeparture = "2025-06-01T08:30:00-04:00[America/New_York]"; 2926 | const flightArrival = "2025-06-01T22:15:00+02:00[Europe/Paris]"; 2927 | console.log("Flight duration:", calculateFlightDuration(flightDeparture, flightArrival).toString()); 2928 | // e.g., "PT9H45M" (9 hours, 45 minutes) 2929 | ``` 2930 | 2931 | **[⬆ Back to Top](#table-of-contents)** 2932 | 2933 | 4. ### Decorator Metadata 2934 | 2935 | ES2025 enhances JavaScript decorators by allowing them to associate metadata with decorated elements. This provides a standard way to attach and retrieve metadata for classes, methods, and properties. 2936 | 2937 | ```javascript 2938 | // Define a decorator that attaches metadata 2939 | function apiEndpoint(path, method = "GET") { 2940 | return function(target, context) { 2941 | context.metadata = { 2942 | ...context.metadata, 2943 | api: { path, method } 2944 | }; 2945 | return target; 2946 | }; 2947 | } 2948 | 2949 | // Define a class with decorated methods 2950 | class UserService { 2951 | @apiEndpoint("/users", "GET") 2952 | getAllUsers() { 2953 | // Implementation... 2954 | } 2955 | 2956 | @apiEndpoint("/users/:id", "GET") 2957 | getUserById(id) { 2958 | // Implementation... 2959 | } 2960 | 2961 | @apiEndpoint("/users", "POST") 2962 | createUser(name, age) { 2963 | // Implementation... 2964 | } 2965 | } 2966 | 2967 | // Access the metadata 2968 | function generateApiDocs(serviceClass) { 2969 | const methodNames = Object.getOwnPropertyNames(serviceClass.prototype) 2970 | .filter(name => name !== 'constructor'); 2971 | 2972 | for (const methodName of methodNames) { 2973 | const method = serviceClass.prototype[methodName]; 2974 | const metadata = method.context?.metadata; 2975 | 2976 | if (metadata && metadata.api) { 2977 | console.log(`${metadata.api.method} ${metadata.api.path} - ${methodName}`); 2978 | } 2979 | } 2980 | } 2981 | 2982 | generateApiDocs(UserService); 2983 | // Output: 2984 | // GET /users - getAllUsers 2985 | // GET /users/:id - getUserById 2986 | // POST /users - createUser 2987 | ``` 2988 | 2989 | Decorator Metadata is particularly useful for: 2990 | 2991 | 1. **API Documentation**: Automatically generating documentation from code. 2992 | 2. **Validation**: Defining validation rules for method parameters. 2993 | 3. **Dependency Injection**: Specifying dependencies for classes and methods. 2994 | 4. **Serialization**: Defining how objects should be serialized/deserialized. 2995 | 5. **Framework Integration**: Providing metadata for frameworks to use. 2996 | 2997 | ```javascript 2998 | // Example of parameter validation with decorator metadata 2999 | function validateParams(target, context) { 3000 | const originalMethod = target.value; 3001 | const methodMetadata = context.metadata; 3002 | 3003 | target.value = function(...args) { 3004 | if (methodMetadata && methodMetadata.params) { 3005 | const validations = methodMetadata.params; 3006 | 3007 | for (let i = 0; i < validations.length; i++) { 3008 | const validation = validations[i]; 3009 | const arg = args[i]; 3010 | 3011 | if (validation.required && (arg === undefined || arg === null)) { 3012 | throw new Error(`Parameter ${i} is required for ${context.name}`); 3013 | } 3014 | } 3015 | } 3016 | 3017 | return originalMethod.apply(this, args); 3018 | }; 3019 | 3020 | return target; 3021 | } 3022 | ``` 3023 | 3024 | **[⬆ Back to Top](#table-of-contents)** 3025 | --------------------------------------------------------------------------------