Basic JavaScript syntax
If you want to learn from a book, this is a great one https://exploringjs.com/impatient-js/toc.html
Variable definition
const a = 123;
let b = 123;
b = a;
var c = 123;
c = a;
Function definition
// function definition
function myFunction() {}
// function as a variable
const myFunction = function myFunction() {};
// anonymous function
const myFunction = function () {};
// arrow function
const myFunction = () => {};
Strings
const a = "Hello, world";
a.length; // 12
a.slice(3, -3); // "lo, wo"
a.split(","); // ["Hello", " world]
a.charAt(0); // "H"
a[0]; // "H"
a.replace("world", "you!"); // "Hello, you!"
a.slice(6).trim(); // " world" trims into "world"
const b = a + ". Sup?"; // "Hello, world. Sup?" (single quotes are also accepted)
const c = `${b}`; // Template strings can interpolate variables
Arrays
const a = ["Hello,", "world"]
a.length // 2
a.join(" "); // "Hello, world"
a[0] // "Hello,"
a[a.length -1] // "world"
a.concat("!") // ["Hello,", "world", "!"]
a.find(e ⇒ e === "!") // "!"
a.find(e ⇒ e === "definitely not in array") // undefined
a.indexOf("world") // 1
a.push("Sup?") // returns new length of mutated array ["Hello,", "world", "!", "Sup?"]
a.pop() // removes last item
b.shift() // removes first item, shifts left, ["world", "!"]
b.unshift("Hello!") // puts new item to beginning ["Hello!", "world", "!"]
// destructuring
const [first, second] = ["Hello", "world"] // first is Hello", second is "world"
const [first, ...rest] = [1, 2, 3, 4] // first is 1, rest is [2, 3, 4]
// spreading
const c = [first, ...rest] // c is [1, 2, 3, 4]
Objects
JSON is first class citizen. The quotes around key aren't necessary.
const o = {
name: "Human",
age: 200000,
greet () {
console.log(`Hi! I'm ${this.name} and I'm ${this.age} years old`);
}
}
o.greet(); // prints "Hi! I'm Human and I'm 200000 years old"
[o.name](http://o.name) // "Human"
o['age'] // 200000
const key = condition ? "name" : "age";
o[key] // depending on condition it'll be "Human" or 200000
o.newKey = [1,2,3]; // o now has name, age and newKey
Object.keys(o) // ["name", "age", "newKey"]
Object.entries(o) // [["name", "Human"], ["age", 200000], ["newKey", [1,2,3]]
// destructuring
const {name} = o; // name is Human
const {
greet,
...restOfObject
} = o; // greet is function, restOfObject is {name: "Human", age: 200000}
// spreading
const myNewPerson = {
...restOfObject,
greet () {
console.log(`Yo! This is ${this.name} over here. Imma ${this.age} year old!`);
}
}
// property shorthands
const myName = "is khan";
const object = { myName } // same as { myName: myName }
Conditional Constructs
if (condition) {
a();
} else if (condition2) {
b();
} else {
c();
}
condition ? a() : condition2 ? b() : c();
condition && a(); // iff condition is truth-y, a would be executed
a() || b(); // if result of a() is truth-y, b won't be executed
Looping Constructs
// for loop
for (let i = 0; i < arr.length; i++) {}
//while loo
let i = 0;
while (i < arr.length) {
i++;
}
// do-while
let i = 0;
do {
i++;
} while (i < arr.length);
// for-in loop to loop through keys of an object
for (let key in object) {
}
// for-of loop to loop through values of an array
for (let value of array) {
}
Functional
// loops through all items of arr array
arr.forEach((index, value, array) => {});
// loops through all items of arr array and returns modified array
arr.map((index, value, array) => modifiedItem);
// returns array with items where cond(value) is true
arr.filter((index, value, array) => cond(value));
// returns true if any item has cond(value) true
arr.some((index, value, array) => cond(value));
// returns true if all items have cond(value) true
arr.every((index, value, array) => cond(value));
// returns undefined or first item matching cond(value)
arr.find((index, value, array) => cond(value));
Classes
class A {
constructor(name) {
this.name = name;
}
static variable = 123;
static utilityMethod() {}
method() {}
}
const a = new A("Human");
a.method();
A.utilityMethod();
A.variable;
// Shorthand for instance variables
class B {
counter = 0;
increment = () => {
this.counter += 1;
return this.counter;
};
}
// same as
class B {
constructor() {
this.counter = 0;
this.increment = () => {
this.counter += 1;
return this.counter;
};
}
}
// Difference between defining instance method and class method (class A#method)
// is that instance methods are created for each instance
// while class method reference is reused between all instances
// so if you're dealing with a lot of instances of same class
// method will be better memory-wise
Asynchronous code
// Callbacks
function executeAfterOneSecond (fn) {
setTimeout(() => {
fn();
}, 1000);
}
executeAfterOneSecond(() => console.log("b"));
console.log("a");
// unlike blocking languages, setTimeout won't block execution
// we'll see "a" and "b" after a second, even if we've b being executed before
// Learn more about event loop: https://www.youtube.com/watch?v=8aGhZQkoFbQ
// In this example we saw setTimeout accepting a function and so did executeAfterOneSecond
// This makes it a higher order function, and fn is called a callback
// as in, once you're done, call [me] back.
// Promises
// Imagine everything being executed via callbacks
callApi(apiUrl, (result) => {
convertToJson(result, (json) => {
transformData(json, (data) => {
callAnotherApi(apiUrl2, data, (finalResult) => {
notifyUser(finalResult);
});
});
});
});
// While this is good as our code doesn't block any user action like scrolling
// it's hard to read and is usually referred to as "Callback hell"
// Promises were designed to make this pyramid shape more flat
// A promise is an instance of class Promise that has two main methods then & catch
const callApi = apiUrl => new Promise((resolve, reject) => {
lowLevelFunctionToCallNetwork(apiUrl, (err, data) => {
if (err) reject(err);
else resolve(data);
})
});
callApi(apiUrl)
.then(convertToJson)
.then(transformData)
.then((data) => callAnotheApi(apiUrl2, data))
.then(notifyUser);
// Async/await makes it even more easier
async function () {
const result = await callApi(apiUrl);
const json = await transformData(result);
const finalResult = callAnotherApi(apiUrl2, data);
notifyUser(finalResult);
}();
Module system
In my opinion, frontend code grows much faster than backend code. Not only you deal with calling various APIs and data transformation techniques, you also bind all that to UI, while taking care of navigation, animations, performance, accessibility and so on.
Without the ability of converting code snippets into tiny reusable modules, you'll end up in a great spaghetti code with business logic, API logic, performance optimizations, UI, animations etc. all in one file.
Let's first cover imports
// assuming "react" is in package.json's dependencies
// present in <root>/node_modules
// imports only the the default export.
import React from "react";
// imports all the named and default export as React.
import * as React from "react";
// you can change the name as you find fit
// note: in case of React you really need to name it React for JSX transpilation
import ICanChangeTheNameToAnything from "react";
// you can destructure from modules
import { Component, useState, useEffect } from "react";
// you can rename a destructured value from a module using `as` keyword.
import { Component as ReactComponent } from "react";
// for local imports, you need to give relative path (in context of current file)
import * as mypackage from "./my-package";
// All import paths have to be statically known, so you can't put a variable in there.
Now that we know how to import, let's focus on exports
export const a = 1; // imported as import {a} from './my-package';
export function print(...args) {
// imported as import {print} from "./my-package";
console.log(...args);
}
const b = 2;
const c = 3;
const d = 4;
export { b, c, d }; // imported as import { b, c, d } from "./my-package";
export default a; // imported as import a from "./my-package";
export * from "./some-other-package"; // exports evertyhing in some-other-package