Introduction
Three pillars in web development
- HTML - Content - Nouns
- CSS - Style and layout - Adjectives
- JavaScript - The real programming language to build web applications - Verbs
What applications that JavaScript can build?
- Front-end apps
- React, Angular, Vue, etc.
- Back-end apps
- Node.js
- Native mobile apps
- React Native, Ionic
- Native desktop apps
- Electron
- Front-end apps
JavaScript is a multi-paradigm interpreted language
- Modern JavaScript uses just-in-time compilation (no portable files)
JavaScript runtime
Container including all the things that we need to use JavaScript
Different types of JavaScript runtime
Runtime in browsers
- JavaScript engine
- Program that executes JavaScript code
- Example: V8 engine for chrome and node.js
- Web APIs
- Callback queue
- JavaScript engine
Runtime in node.js
- JavaScript engine
- C++ bindings and thread pool
- Callback queue
JavaScript versions
- ES6 / ES2015: biggest update to this language
- New updates to JS every single year
- Modern JavaScript - after ES6
- Don’t break the web
- Backwards compatible
- Old features are never removed
- How to use modern JavaScript today?
- ES5
- Fully supported in all browsers
- Ready to be used today
- ES6+
- Well supported in all modern browsers
- No support in older browsers
- Can use most features in production with transpiling and polyfilling
- ES5
How to link JavaScript file to the HTML file?
- Incline script
- Put JavaScript code in the
<script>
block
- Put JavaScript code in the
- External JavaScript files
- Put code in the
.js
files in the same folder of HTML files - Tag
<script src=""></script>
- Put code in the
- Incline script
Strict mode
- Put
'use strict';
statement in the beginning of the.js
file - Forbid to do certain things
- Create visible errors
- Put
Script Loading
Async in head
- Scripts not guaranteed to execute in order
- Use for 3rd-party scripts where order doesn’t matter
Defer in head
- Scripts are executed in order
- Overall best solution
Variables and Values
$
is allowed in the variable’s nameDeclaration
let
- Variables
- Block-scope
const
- Constant
- Must be assigned with a value
- Block-scope
var
- Should be avoided
- Old way of declaring variables before ES6
- Function-scope
- Creates property on
window
object
Hoisting
Makes some types of variables accessible/usable in the code before they are actually declared
Hoisted Initial Value function declarations Yes Actual function var
variablesYes undefined
let
andconst
variablesNo <uninitialised>
, TDZ (Temperory Dead Zone)function expressions and arrows Depends on using var
orlet const
Data types
JavaScript has dynamic typing: we don’t have to manually define the data type of the value stored in a variable. Instead, data types are determined automatically.
In JavaScript, it is the value that has a type, not the variable
Primitive data types
Number
- Floating point numbers
NaN
: number value which means invalid number
String
Double quote or single quote
Template literals
Syntax
const str = `text ${varName1} text ${expression}`;
Can also be used to create multiple line strings
// Instead of doing this way const str = "text \n\ text \n\ text"; // Using template literals const str = `text text text`;
Boolean
true
orfalse
- Falsy values
- Values are not exactly false but will become false when we try to convert them into a boolean
0, "", undefined, null, NaN
- Truthy values
- Other values
Undefined
- Empty variable
Null
Symbol
- Value that is unique and cannot be changed
BigInt
- Large integers than the Number type cannot hold
Reference data type
- Object
- Array
- Function
- Etc.
typeof
operatortypeof var
ortypeof value
- Return a string
Type conversion and coercion
Casting syntax
const num = Number("text"); const str = String(10); const bool = Boolean(0);
Coercion
+
: Number converts to String implicitly-
,*
,/
: String converts to Number implicitly- Using logical operators or condition statement, value will be converted to boolean implicitly
Operators
- Mathematical operators
- Exponentiation operator:
**
+
,+=
can be used for string
- Exponentiation operator:
- Assignment operators
- Abbreviating operators
++
,--
- Relational operators
===
,!==
- Strict equality operator
- Doesn’t perform type coercion
==
,!=
- Loose equality operator
- Does perform type coercion
- Avoid using loose equality operator
- Logical operators
- Use ANY data type, return ANY data type, short-circuiting
- OR return the first truthy value or the last falsy value if all flasy
- AND return the first falsy value or the last truthy value if all truthy
- Bitwise operators
- Ternary operator
- Operator precedence
- Modern operators
- Spread operator
...
...
works on iterables not objects- After ES2018,
...
works on objects too
- Nullish coalescing operator
??
- Introduced in ES2020
- Returns its right-hand side operand when its left-hand side operand is
null
orundefined
, and otherwise returns its left-hand side operand - Work with nullish value:
null
andundeifined
without0
- Logical assignment operator
- Introduced in ES2021
||=
,??=
,&&=
- Optional chaining
?.
- Introduced in ES2020
- Check if a property or function is
undefined
or not - Usually used with
??
- Spread operator
Expressions and Statements
Flow control
if-else
This is allowed in JavaScript:
const y = 2; if (y === 3) console.log(x); const x = 1; console.log(x);
switch
for
for-of
const arr = [1, 2, 3]; for (const item of arr) console.log(item); const obj = { name: "John", gender: "male", }; for (const [key, value] of Object.entries(obj)) console.log(key, value); const m = new Map(); for (const [key, value] of m) { }
continue
andbreak
is allowed
while
do while
Functions
Functions in JavaScript are first-class objects, which means they are treated as variables, so in JavaScript we can
- Store functions in variables or properties
- Pass functions as arguments to other functions
- Return functions from functions
- Call methods on functions
Higher-order functions
A function that receives another function as an argument, that returns a new function, or both
The function as an argument is called callback function
funcArg.name
returns the name of the function
The function that is returned is called returned function
Syntax - function declaration
function funcName() {} // example function calcAge1(birthYear) { return 2023 - birthYear; }
Functions can be invoked before their declarations
Anonymous function - function expression
const funcName = function () {}; // example const calcAge2 = function (birthYear) { return 2023 - birthYear; };
- Cannot be invoked before function expression
Arrow function - function expression
- A special form of anonymous function
const funcName = para => exp; // without {}, we don't need to return the value explicitly const funcName = (para1, para2) => {}; // with {}, we define the function in the traditional way // example const calcAge3 = birthYear => 2023 - birthYear;
- Helpful for one-line functions
- Does not get its own
this
keyword
Default parameters
- Use default parameters whenever is possible
function createBooking( flightNum, numPassengers = 1, price = 199 * numPassengers ) {} createBooking("LH123"); createBooking("LH123", 2, 800); createBooking("LH123", undefined, 1000); // skip a parameter to use its default value
Immediately invoked function expression
Called once and never be called again
(function () {})(); (() => {})();
Functions in JavaScript is passed-by-value
arguments
- An Array-like object accessible inside function that contains the values of the arguments passed to that function
this
Depends on how the function is called, which means it is dynamic
Alone,
this
refers to the global object (Window
in web browser)In method,
this
refers to the objectIn regular function,
this
refers toundefined
(in strict mode; otherwise refers towindow
)When a function is used as a callback function,
this
is lostIn arrow function, do NOT get its own
this
In an event handler function (not defined as arrow function),
this
always refers to the element on which that handler is attached toManually tell how it should behave
func.call(thisArg, args)
- Calls the function with a given
this
value and arguments provided individually
- Calls the function with a given
func.apply(thisArg, argsArr)
- Calls the specified function with a given
this
value, andarguments
provided as an array - Equals to
func.call(thisArg, ...argsArr)
- Stick to
call
- Calls the specified function with a given
func.bind(thisArg, <args>)
- Creates a new function that, when called, has its
this
keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called - If pass
args
, it will be preset when thefunc
is called — Partial application - Pass
null
tothisArg
if you want to create a partial application without havingthis
inside
- Creates a new function that, when called, has its
Closures
- A function always has access to the variable environment of the execution context (call stack) in which it was created, even if that execution context is removed from the stack
- Closure has high priority over scope chain
Chaining methods
- Avoid chaining the methods that mutate the original object
Functional programming
- Declarative paradigm
- Based on the idea of writing software by combining many pure functions, avoiding side effects and mutating data
- Pure functions are stateless
Built-in Data structure
Arrays
Syntax
- Literal syntax
const array = [element1, element2];
- Object
const array = new Array(element1, element2); const array2 = new Array(len); // Empty array with length len
Can put values of different data types in one array
Array is also an object
Basic operations
array.length
- Return the length of the array
push
- Add new element to the end of the array
- Return the length of the new array
unshift
- Add new element to the start of the array
- Return the length of the new array
pop
- Remove the last element of the array
- Return the removed element
shift
- Removed the first element of the array
- Return the removed element
indexOf
- Return the index of the element
includes
- Check whether the element is included in the array
Destructing array
const arr = [1, 2, 3]; const [x, y, z] = arr; // skip elements const [x, , z] = arr; // default value const [a = 0, b = 0, c = 0, d = 0] = arr; // without default value, d will be undefined // switch using this technique [a, b] = [b, a];
Spread array (unpack)
const arr = [7, 8, 9]; const newArr = [1, 2, ...arr]; // [1, 2, 7, 8, 9]
Rest pattern (pack)
- The rest element must be the last element
const arr = [1, 2, 3, 4, 5]; const [a, b, ...others] = arr; // a = 1, b = 2, others = [3, 4, 5]
Using spread and rest together can write more general functions
function add (...numbers) { let sum = 0; for(int i = 0; i < numbers.length; i++) sum += numbers[i]; return sum; } // All of following usages are available add(2, 3); add(1, 2, 3, 4, 5); const x = [1, 2, 3]; add(...x);
entries
- Return a new Array Iterator object that contains the key/value pairs for each index in the array
slice(start, end)
- Returns a shallow copy of a portion of an array into a new array object selected from
start
toend
(end
not included) wherestart
andend
represent the index of items in that array start
,end
can be negative- Create a shallow copy of array:
arr.slice()
- Returns a shallow copy of a portion of an array into a new array object selected from
splice(start, deleteCount)
- Works similar as
slice
, but change the original array by taking the extracted elements - Useful for deleting elements
- Works similar as
reverse
- Change the original array
arr1.concat(arr2)
Returns a new array, doesn’t change the original arrays
Same effect:
[...arr1, ...arr2];
join(separator)
- Returns a new string by concatenating all of the elements in an array, separated by commas or a specified separator string
at
- Takes an integer value and returns the item at that index
- Useful for accessing the last element
arr.at(-1)
forEach(callbackFn)
forEach(function(element, index, array) {})
forEach((e, i, arr) => {})
- Executes a provided function once for each array element
element
: the current element being processed in the arrayindex
: the index of the current element being processed in the arrayarray
: the array that callsforEach
- Throw-away parameter: use
_
to name it
map(callbackFn)
- Creates a new array populated with the results of calling a provided function on every element in the calling array
filter(callbackFn)
- Returns a shallow copy of a portion of the given array, filtered down to just the elements from the given array that pass the test implemented by the provided function. If no elements pass the test, an empty array will be returned
callbackFn
returns a boolean value
reduce(callbackFn, initialValue)
reduce(function(accumulator, currentValue, currentIndex, array) {}, initiaValue)
- Executes a user-supplied “reducer” callback function on each element of the array, in order, passing in the return value from the calculation on the preceding element. The final result of running the reducer across all elements of the array is a single value
accumulator
: the value resulting from the previous call tocallbackFn
initialValue
: a value to whichaccumulator
is initialized the first time the callback is called. IfinitialValue
is specified,callbackFn
starts executing with the first value in the array ascurrentValue
. IfinitialValue
is not specified,accumulator
is initialized to the first value in the array, andcallbackFn
starts executing with the second value in the array ascurrentValue
. In this case, if the array is empty (so that there’s no first value to return asaccumulator
), an error is thrown
find(callbackFn)
findLast(callbackFn)
- Returns the first / last element in the provided array that satisfies the provided testing function. If no values satisfy the testing function,
undefined
is returned callbackFn
returns a boolean value
- Returns the first / last element in the provided array that satisfies the provided testing function. If no values satisfy the testing function,
findIndex(callbackFn)
findLastIndex(callbackFn)
- Returns the index of the first / last element in an array that satisfies the provided testing function. If no elements satisfy the testing function, -1 is returned
some(callbackFn)
- Tests whether at least one element in the array passes the test implemented by the provided function
- Returns a boolean value
- It doesn’t modify the array
every(callbackFn)
- Tests whether all elements in the array pass the test implemented by the provided function
flat(depth)
- Creates a new array with all sub-array elements concatenated into it recursively up to the specified depth
depth
: The depth level specifying how deep a nested array structure should be flattened. Defaults to 1
flatMap(callbackFm)
Returns a new array formed by applying a given callback function to each element of the array, and then flattening the result by one level
Identical to a
map()
followed by aflat()
of depth 1 (arr.map(callbackFn).flat()
), but slightly more efficient than calling those two methods separately
sort(compareFn)
sort((a, b) => {})
Sorts the elements of an array in place and returns the reference to the same array, now sorted
The default sort order is ascending, built upon converting the elements into strings, then comparing their sequences of UTF-16 code units values
compareFn(a, b)
return valuesort order > 0 sort a
afterb
< 0 sort a
beforeb
=== 0 keep original order of a
andb
Comparing numbers
// ascending order function compareNumbers(a, b) { return a - b; } // descending order function compareNumbers(a, b) { return b - a; }
fill(value)
fill(value, start, end)
- Changes all elements in an array to a static value, from a start index (default
0
) to an end index (defaultarray.length
) - Returns the modified array
- Changes all elements in an array to a static value, from a start index (default
Array.from(arrayLike)
Array.from(arrayLike, mapFn)
Creates a new, shallow-copied
Array
instance from an iterable or array-like objectArray.from()
lets you createArray
from:Iterable objects (objects such as
Map
andSet
); or, if the object is not iterableArray-like objects (objects with a
length
property and indexed elements)const arr = Array.from({ length: 7 }, () => 1);
Objects
Syntax
- Literal syntax
const obj = { key1: value1, key2: value2, func: function () {}, // Methods };
key
are always stringsIt’s not a block scope
Enhanced object literals
const openingHours = { fri: { open: 11, close: 23, }, }; const arr = ["name", "gender"]; const restaurant = { // First enhancement: if the property name is the same as the variable name openingHours, // Second enhancement: function in object check() {}, // Third enhancement: computing property name [arr[0]]: "John", };
In object methods, we can use
this
keyword- Unlike Java,
this
must be used explicitly if you need to use the reference of the object
- Unlike Java,
Basic operations
Access a value
obj.key
obj["key"]
- The string can be an expression which holds the value of the string of the key name
- Return
undefined
if there is not such a key
Create a new property on the current object
obj.newKey = val;
obj["newKey"] = val;
Object.keys(obj)
- Return an array of object property names
Object.values(obj)
- Return an array of object property values
Object.entries(obj)
- Return an array of object property key-value pairs
Object.freeze(obj)
- Make the object immutable
- However, it’s a shallow freeze
Destructing objects
const restaurant = { name: "YO", menu: ["Ice cream", "burger"], openingHours: { fri: { open: 11, close: 23, }, sat: { open: 0, close: 24, }, sun: { open: 0, close: 24, }, }, }; const { name, openingHours, menu } = restaurant; // order doesn't matter here const { name: x, openingHours: y, menu: z } = restaurant; // new variable name const { menu = [] } = retaurant; // default value const { fri: { open, close }, } = openingHours; // nested objetcs // switch ({ a, b } = { b, a }); // () is required
Spread
const newRestaurant = { ...restaurant, founder: "John", };
Rest
const { fri, ...otherDays } = openHours;
Sets
Syntax
const s = new Set([]);
Basic operations
.size
has
- Check if the set contains the element
add
- Add an element to a set if it is not included
delete
- Delete an element
clear
- Delete all elements
Convert set to array
const arr = [...s];
forEach(function(value, key, set) {})
key
andvalue
are the same
Maps
Syntax
const m = new Map([ [k1, v1], [k2, v2], ]);
Difference from objects:
key
in a map can be any data typeBasic operations
.size
- Return the size of the map
set(key, value)
- Add a new element into map
- Return the changed map
- Can be chained
m.set(a, b).set(c, d)
get(key)
- Return the value according to the key
- Return
undefined
if there is not such a key
has()
- Check if a map has a certain key
delete(key)
- Delete an element according to the key
keys()
- Return an iterable of keys in map
[...m.keys()]
to get the array of keys
values()
- Return an iterable of values in map
entries()
Convert object to map
const m = new Map(Object.entries(obj));
Convert map to array
const arr = [...m];
forEach(function(value, key, map) {})
Strings
Whenever call a method on a string, JavaScript will automatically create a string object (boxing)
Operations
[]
- Access the character (still a string)
indexOf
- Return the index of the first appearing string
lastIndexOf
- Return the index of the last appearing string
slice()
Return the substring
const airline = "TAP Air Portugal"; airline.slice(4); // <Air Portugal> airline.slice(4, 7); // <Air> airline.slice(0, airline.indexOf(" ")); // <TAP> airline.slice(airline.lastIndexOf(" ") + 1); // <Portugal> airline.slice(-2); // <al> airline.slice(1, -1); // <AP Air Portuga>
toLowerCase
toUpperCase
trim
- Removes whitespace from both ends of a string and returns a new string
replace(pattern, replacement)
- Returns a new string with one, some, or all matches of a
pattern
replaced by areplacement
- The
pattern
can be a string or aRegExp
, and thereplacement
can be a string or a function called for each match - If
pattern
is a string, only the first occurrence will be replaced
- Returns a new string with one, some, or all matches of a
replaceAll
includes
startsWith
endsWith
split(pattern)
- Takes a pattern and divides a string into an ordered list of substrings by searching for the pattern, puts these substrings into an array, and returns the array
padStart(targetLength, padString)
- Pads the current string with another string (multiple times, if needed) until the resulting string reaches the given length
- The padding is applied from the start of the current string
padEnd
repeat(count)
- Returns a new string which contains the specified number of copies of the string on which it was called, concatenated together
Copy
Reference copy
const newObj = oldObj;
Shallow copy
const newArr = [...oldArr]; const newObj = { ...oldObj }; const newObj = Object.assign({}, oldObj); newArr = oldArr.slice();
Deep clone
- Use external libraries like
Lodash
- Use external libraries like
DOM and Events
DOM
Document Object Model
Structured representation of HTML documents
Allows JavaScript to access HTML elements and styles to manipulate them
DOM is automatically created by the browser as soon as the HTML page loads
A tree structure
DOM is a Web API that written in JavaScript
DOM objects are unique
*Every single node in the DOM tree is the type
Node
*Represented by JavaScript object
textContent
,childNodes
,parentNode
,cloneNode()
, etcNode
child classesElement
HTMLElemnt
HTMLButtonElement
- …
HTMLDivElement
Text
Comment
Document
- The special object that is the entry node of the DOM
Node
inheritsEventTarget
which has two methodsaddEventListener()
removeEventListener()
DOM traversal
Element.children
- Returns a live
HTMLCollection
which contains all of the childelements
of the element upon which it was called - Includes only element nodes
- Returns a live
Element.childNodes
- Returns a live
NodeList
of childnodes
of the given element where the first child node is assigned index0
- Returns a live
Element.parentNode
Element.parentElement
- Direct parent
Element.closest(selector)
- Traverses the element and its parents (heading toward the document root) until it finds a node that matches the specified selector
Element.previousElementSibling
Element.nextElementSibling
Element.previousSibling
Element.nextSibling
Operations
Select elements
document
- The
Document
object represents your web page
- The
document.documentElement
- Returns the
Element
that is a direct child of the document. For HTML documents, this is normally theHTMLHtmlElement
object representing the document’s<html>
element
- Returns the
document.head
document.body
document.querySelector(selector)
- Returns the first
Element
within the document that matches the specified selector selector
- A string containing one or more selectors to match. This string must be a valid CSS selector string
- Returns the first
document.querySelectorAll(selector)
- Returns a
NodeList
ofElement
s
- Returns a
document.getElementByID(id)
document.getElementsByTagName(name)
- Returns an
HTMLCollection
of elements with the given tag name - The returned
HTMLCollection
is live, meaning that it updates itself automatically to stay in sync with the DOM tree without having to calldocument.getElementsByTagName()
again
- Returns an
document.getElementsByClassName(className)
- Returns an
HTMLCollection
of elements with the given class name
- Returns an
Element.querySelector()
Element.getElementsByTagName(name)
Create and insert elements
Element.insertAdjacentHTML(position, text)
Parses the specified text as HTML or XML and inserts the resulting nodes into the DOM tree at a specified position
position
A string representing the position relative to the element. Must be one of the following strings:
// <!-- beforebegin --> // <p> // <!-- afterbegin --> // foo // <!-- beforeend --> // </p> // <!-- afterend -->
document.createElement(tagName)
- Returns a new created
Element
specified bytagName
- Returns a new created
document.createRange().createContextualFragment(markup)
Node.cloneNode(deep)
Return a new cloned
Node
. The cloned node has no parent and is not part of the document, until it is added to another node that is part of the documentdeep
- If
true
, then the node and its whole subtree, including text that may be in childText
nodes, is also copied - If
false
, only the node will be cloned. The subtree, including any text that the node contains, is not cloned
- If
Element.prepend(para1, paraN)
- Inserts a set of
Node
objects or string objects before the first child of theElement
- String objects are inserted as equivalent
Text
nodes
- Inserts a set of
Element.append(para1, paraN)
- Inserts a set of
Node
objects or string objects after the first child of theElement
- Inserts a set of
Element.before(para1, paraN)
- Inserts a set of
Node
objects or string objects in the children list of thisElement
’s parent, just before thisElement
- Inserts a set of
Element.after(para1, paraN)
- Inserts a set of
Node
objects or string objects in the children list of thisElement
’s parent, just after thisElement
- Inserts a set of
Delete elements
Element.remove()
- Removes the element from the DOM
Manipulate elements
Element.${attribute}
Node.textContent
- Represents the text content of the node and its descendants
- Setting
textContent
on a node removes all of the node’s children and replaces them with a single text node with the given string value
Element.innerHTML
- Gets or sets the HTML or XML markup contained within the element
HTMLInputElement.value
- Returns a String
HTMLElement.focus()
- Sets focus on the specified element, if it can be focused. The focused element is the element that will receive keyboard and similar events by default
HTMLElement.blur()
- Removes keyboard focus from the current element
HTMLElement.style.${property} = ""
- Returns the inline style of an element in the form of a
CSSStyleDeclaration
object - Will not change the CSS file; add an inline style attribute
- Returns the inline style of an element in the form of a
window.getComputedStyle(element)
- Returns an object containing the values of all CSS properties of an element, after applying active stylesheets and resolving any basic computation those values may contain
Element.classList
Returns a live
DOMTokenList
collection of theclass
attributes of the elementcontains()
add()
remove()
toggle()
- Add if not contains, remove if contains
Add and remove classes is the main way to manipulate the styles instead of manipulating
style
directly
ELement.src
- Absolute url
Element.getAttribute("src")
- Relative url
HTMLElement.dataset.${}
- The
dataset
read-only property of theHTMLElement
interface provides read/write access to custom data attributes (data-*
) on elements. It exposes a map of strings (DOMStringMap
) with an entry for eachdata-*
attribute.
- The
Coordinates
Element.getBoundingClientRect()
window.pageXOffset
window.pageYOffset
window.scrollTo()
- Old way
Element.scrollIntoView(scrollIntoViewOptions)
- Modern way
- Scrolls the element’s ancestor containers such that the element on which
scrollIntoView()
is called is visible to the user.
const obsCallbackFn = function (entries, observer) {}; const obsOptions = { root: null, threshold: [0, 0.2], }; const observer = new IntersectionObserver(obsCallbackFn, obsOptions); observer.observe(element);
- Sticky nav
- Lazy loaded images
- Infinite scrolling
Event and event handlers
- An event is something happened on the page
- JavaScript will generate an object which contains all the information about the event when an event occurs
- Event handler: the callback function accepts a single parameter: an object based on
Event
describing the event that has occurred, and it returns nothing - In an event handler function (except arrow function),
this
always points to the element on which that handler is attached to
/* para1: type of the event
para2: event function handler
*/
document.querySelector(".btn").addEventListener("click", function (e) {});
// pass parameters into callback function
function callbackFn(e, para) {}
e.addEventListener("click", function (e) {
callbackFn(e, para);
});
// or
function callbackFn(e) {
console.log(this);
}
e.addEventListener("click", callbackFn.bind(para));
- Response with keyboard events
// keyup, keypress, keydown --- three conditions of interacting with a key on the keyboard
// usually choose keydown, happen as soon as we hit the key
document.addEventListener("keydown", function (e) {});
// use e.key to get which key is pressed
removeEventListener()
e.preventDefault()
e.target
- A reference to the object onto which the event was dispatched
e.currentTarget
- The current target for the event, as the event traverses the DOM
- Event propagation
- Capturing phase
- Event happens, travels all the way down from
document
to the target element - Pass through every single parent of the target
- Event happens, travels all the way down from
- Target phase
- Events can be handled right at the target
- Bubbling phase
- Events bubble up from target element to
document
- Normally
addEventListener
happens in this phase addEventListener(event, callbackFn, true)
will listen to event in capture phase
- Events bubble up from target element to
- Not all events have these phases
- Stop propagation:
e.stopPropagation()
- Capturing phase
- Event delegation
- Take advantage of event propagation that add one event listener to the container of similar events instead of add same event listeners to all of them
- Use
e.target
- Matching strategy
- Guard clause (Guard
null
pointer)
BOM
- The
window
object is supported by all browsers. It represents the browser’s window - All global JavaScript objects, functions, and variables automatically become members of the window object
- Global variables are properties of the window object
- Global functions are methods of the window object
- Even the document object (of the HTML DOM) is a property of the window object
Number, Math, Date, Intl and Timers
Number
Always float numbers
Properties
MAX_SAFE_INTEGER
MIN_SAFE_INTEGER
Methods (Global functions —
Number
can be omitted)Number.parseInt(string, radix)
parses a string argument and returns an integer of the specified radix
string
: a string starting with an integer. Leading whitespace in this argument is ignoredradix
: the base in mathematical numeral systems
Number.parseFloat(string, radix)
Number.isNaN(value)
- Determines whether the passed value is the number value
NaN
, and returnsfalse
if the input is not of the Number type
- Determines whether the passed value is the number value
Number.isFinite(value)
- Determines whether the passed value is a finite number — that is, it checks that a given value is a number, and the number is neither positive
Infinity
, negativeInfinity
, norNaN
- Check if
value
is a number
- Determines whether the passed value is a finite number — that is, it checks that a given value is a number, and the number is neither positive
Number.isInteger(value)
Number.prototype.toFixed(digits)
- Formats a number using fixed-point notation
Math
Do type coercion itself
Math.PI
Math.max(val1, val2, valN)
Math.min(val1, val2, valN)
Math.random()
- Returns a floating-point, pseudo-random number that’s greater than or equal to 0 and less than 1, with approximately uniform distribution over that range — which you can then scale to your desired range
Math.trunc(val)
- Returns the integer part of a number by removing any fractional digits
Math.round(val)
- Returns the value of a number rounded to the nearest integer
Math.ceil(val)
- Always rounds up and returns the smaller integer greater than or equal to a given number
Math.floor(val)
Always rounds down and returns the largest integer less than or equal to a given number
Create a random integer within
[min, max]
const randomInt = (min, max) => Math.floor(Math.random() * (max - min) + 1) + min;
Numeric separators
const diameter = 287_460_000_000; const price = 345_99;
BigInt
- with
n
suffix:56435468453453453453453454344684868n
- Cannot mix
BigInt
and other types except logical operators and+
with String - Division of
BigInt
: works like Cint
- with
Date
new Date()
- Returns a string representing the current time
new Date(dateString)
- ISO 8601 format (
YYYY-MM-DDTHH:mm:ss.sssZ
)
- ISO 8601 format (
new Date(year, monthIndex, day, hours, minutes, seconds, milliseconds)
monthIndex
starts with 0
new Date(value)
value
: an integer value representing the number of milliseconds sinceJanuary 1, 1970, 00:00:00 UTC
(the ECMAScript epoch, equivalent to the UNIX epoch), with leap seconds ignored
getFullYear()
getMonth()
getDate()
getDay()
- 0 represents Sunday
getHours()
getMinutes()
getSeconds()
getTime()
- Returns the number of milliseconds since the epoch
- The return value is called timestamp
set...()
toISOString()
Date.now()
- Returns the current timestamp
Intl
- The namespace for the ECMAScript Internationalization API, which provides language sensitive string comparison, number formatting, and date and time formatting
Intl.DateTimeFormat(locale, options).format(date)
- Formats a date according to the locale and formatting options
options
Intl.NumberFormat(locale, options).format(num)
- Formats a number
Timers
setTimeout(callbackFn, dalay, param1, param2, paramn)
- Sets a timer which executes a function or specified piece of code once the timer expires
- Returns an timeout ID which uniquely identifies the timeout
delay
: in millisecondsparam
: additional arguments which are passed through to the function specified byfunctionRef
setTimeout()
is an asynchronous function, meaning that the timer function will not pause execution of other functions in the functions stack
clearTimeout(timeoutID)
- cancels a timeout previously established by calling
setTimeout()
- If the parameter provided does not identify a previously established action, this method does nothing
- cancels a timeout previously established by calling
setInterval(callbackFn, delay, params)
Returns an interval ID which uniquely identifies the interval
Repeatedly calls a function or executes a code snippet, with a fixed time delay between each call
callbackFn
is not called immediately, actually get called after one secondSolution:
callbackFn(); const timer = setInterval(callbackFn, delay);
clearInterval(intervalID)
OOP In JavaScript
- Prototype and Object
- In terms of Class and Instance (but not the same)
- JavaScript doesn’t have Class
- Objects are linked to a prototype object
- Prototypal inheritance/delegation: the prototype contains methods that are accessible to all objects linked to that prototype
- In terms of Class and Instance (but not the same)
- Three ways of implementing OOP in JavaScript
- Constructor functions
- Technique to create objects from a function
- This is how built-in objects like Arrays, Maps or Sets are actually implemented
- ES6 Classes
- Modern alternative to constructor function syntax
- “Syntactic sugar”: works like constructor functions behind the scenes
- Do NOT behave like classical OOP
Object.create()
- Constructor functions
instanceof
operator
Constructor function
Syntax
const ClassName = function (field1, field2) { this.field1 = field1; this.field2 = field2; }; ClassName.prototype.method1 = function () {}; ClassName.prototype.method2 = function () {}; const obj = new ClassName(para1, para2);
Works done behind
new
- An empty object is created
- The function is called,
this
is linked to the empty object - The empty object is linked to prototype
- Return the newly created object automatically
Never declare methods in constructor functions
Every object in JavaScript automatically has a property
prototype
Every object that is created by a certain constructor function will get access to all the methods and properties that we define on the constructors prototype property
ClassName.prototype
is an object which is the prototype of theobj
obj.__proto__ === ClassName.prototype
ClassName.prototype.isPrototypeOf(obj) === true
ClassName.prototype.isPrototypeOf(ClassName) === false
ClassName.prototype.constructor === ClassName
Prototype chain
// the Behavior of the top prototype chain Object.__proto__ === Function.prototype; Function.__proto__ === Function.prototype; Function.prototype.prototype === undefined; Object.__proto__.__proto__ === Object.prototype; Object.__proto__.__proto__.__proto__ === null;
Static methods
Methods attached to the constructor function instead of its prototype
ClassName.helper = function () {};
ES6 Classes
Not the actual
Class
, just another way of using constructor functionsSyntax
class ClassName { constructor(field1, field2) { this.field1 = field1; this.field2 = field2; } method1() {} method2() {} static helper() {} }
Getter and setter
- Existed to make code more clear
class Person { constructor(birthYear) { this.birthYear = birthYear; } // getter get birth() { return this.birthYear; } // setter set birth(birth) { this.birthYear = birth; } } const sam = new Person(2000); console.log(sam.birth); // not sam.birth() sam.birth = 30; // will change the birthYear
Object.create
Syntax
const ClassName() {} const obj = Object.create(ClassName);
Be aware of the difference: NOT “fake class”
Inheritance
Constructor function
const Person = function (firstName, birthYear) { this.firstName = firstName; this.birthYear = birthYear; }; Person.prototype.calcAge = function () { console.log(2037 - this.birthYear); }; const Student = function (firstName, birthYear, course) { Person.call(this, firstName, birthYear); this.course = course; }; // Linking prototypes Student.prototype = Object.create(Person.prototype); Student.prototype.constructor = Student; Student.prototype.introduce = function () { console.log(`My name is ${this.firstName} and I study ${this.course}`); };
ES6 Classes
class PersonCl { constructor(fullName, birthYear) { this.fullName = fullName; this.birthYear = birthYear; } // Instance methods calcAge() { console.log(2037 - this.birthYear); } greet() { console.log(`Hey ${this.fullName}`); } // Static method static hey() { console.log("Hey there 👋"); } } class StudentCl extends PersonCl { constructor(fullName, birthYear, course) { // Always needs to happen first! super(fullName, birthYear); this.course = course; } introduce() { console.log(`My name is ${this.fullName} and I study ${this.course}`); } greet() { console.log("Hi"); } }
Encapsulation
JavaScript doesn’t have real
private
Protected fields and methods
- Just add a
_
before the name to indicate it is protected - Faked encapsulation
- Just add a
Private Class fields and methods
Truly encapsulation
class Account { // 1) Public fields (instances) locale = navigator.language; // 2) Private fields (instances) #movements = []; #pin; constructor(owner, currency, pin) { this.owner = owner; this.currency = currency; this.#pin = pin; console.log(`Thanks for opening an account, ${owner}`); } // 3) Public methods // Public interface getMovements() { return this.#movements; } deposit(val) { this.#movements.push(val); return this; } withdraw(val) { this.deposit(-val); return this; } requestLoan(val) { if (this.#approveLoan(val)) { this.deposit(val); console.log(`Loan approved`); return this; } } static helper() { console.log("Helper"); } // 4) Private methods #approveLoan(val) { return true; } }
Asynchronous JavaScript
- Synchronous code is executed line by line
- Long-running operations block code execution
- Asynchronous code is executed after a task that runs in the background finishes
- Asynchronous code is non-blocking
AJAX
Asynchronous JavaScript And XML
Allow us to communicate with remote web servers in an asynchronous way
With AJAX calls, we can request data from web server dynamically
XML data format is not used anymore
JSON is the most popular data format now
Old way
const request = new XMLHttpRequest(); request.open("GET", ${url}); request.send(); request.addEventListener("load", function() { const [data] = JSON.parse(this.responseText); ... });
Call back hell
- The problem of nested callback functions for executing asynchronous operations in order
Modern way
Fetch
APIfetch(url)
- Starts the process of fetching a resource from the network, returning a promise which is fulfilled once the response is available
- Only rejects when a network error is encountered
Promise.prototype.then(resolve, reject)
- It immediately returns an equivalent
Promise
object, allowing you to chain calls to other promise methods resolve(value)
- Its return value becomes the fulfillment value of the promise returned by
then()
- The function is called with the following arguments
value
: The value that the promise was fulfilled with - If it is not a function, it is internally replaced with an identity function (
(x) => x
) which simply passes the fulfillment value forward
- Its return value becomes the fulfillment value of the promise returned by
reject(error)
- Its return value becomes the fulfillment value of the promise returned by
catch()
- The function is called with the following arguments
error
: Theerror
object that the promise was rejected with - If it is not a function, it is internally replaced with a thrower function (
(x) => { throw x; }
) which throws the rejection reason it received
- Its return value becomes the fulfillment value of the promise returned by
- It immediately returns an equivalent
Promise.prototype.catch()
- It is a shortcut for
Promise.prototype.then(undefined, reject)
- It is a shortcut for
Promise.prototype.finally(final)
- Promise is consumed and new promise is built and settled during
then()
, therefore asynchronous operations are executed in order - Actually it is still nested, but it is coded in a non-nested way
then()
is actually a new promisification (build -> callback -> settled)
Example
const getJSON = function (url, errorMsg = "Something went wrong") { return fetch(url).then(response => { if (!response.ok) throw new Error(`${errorMsg} (${response.status})`); return response.json(); // create a new promise }); }; const getCountryData = function (country) { // Country 1 getJSON( `https://restcountries.eu/rest/v2/name/${country}`, "Country not found" ) .then(data => { renderCountry(data[0]); const neighbour = data[0].borders[0]; if (!neighbour) throw new Error("No neighbour found!"); // Country 2 return getJSON( `https://restcountries.eu/rest/v2/alpha/${neighbour}`, "Country not found" ); }) .then(data => renderCountry(data)) .catch(err => { console.error(`${err} 💥💥💥`); renderError(`Something went wrong 💥💥 ${err.message}. Try again!`); }); };
Promises
An object that is used as a placeholder for the future result of an asynchronous operation
We no longer need to rely on events and callbacks passed into asynchronous functions to handle asynchronous results
Instead of nesting callbacks, we can chain promises for a sequence of asynchronous operations
A Promise is a JavaScript object that links producing code and consuming code
Lifecycle
Build promises
Promise(function(resolve, reject) {})
- It is primarily used to wrap callback-based APIs (the
executor
) that do not already support promises
- It is primarily used to wrap callback-based APIs (the
Promise.resolve(value)
- Returns a
Promise
object that is fulfilled with a givenvalue
- Returns a
Promise.reject(error)
- Returns a
Promise
object that is rejected with a givenerror
- Returns a
Promisify callback functions
- So that we can chain all promises (callbacks converted to promise workflow)
// The para is the executor const wait = seconds => new Promise(function (resolve) { setTimeout(resolve, seconds * 1000); }); wait(2).then(function () { console.log("I have waited for 2 seconds"); });
Pending
- Execute the executor (equivalent to callback execution)
Settled
Asynchronous tasks are finished
Fulfilled or Rejected
myPromise.state myPromise.result “pending” undefined
“fulfilled” a result value “rejected” an error object
Consume promises
Consuming promises is actually executing functions
then()
,catch()
,finally()
async
andawait
async
andawait
make promises easier to write- No more
then
- Asynchronous operation written in synchronous way
- No more
- The keyword
async
before a function makes the function return a promise (always fulfilled not rejected) - All functions invoked in
async
functions should beasync
not callback functions - The
await
keyword can only be used inside anasync
function - The
await
keyword makes the function pause the execution and wait for a resolved promise before it continues await
returns the fulfillment value of the promise orthenable
object, or, if the expression is notthenable
, the expression’s own value- Use with error handling
try...catch
- Rethrow the
error
incatch
block will manually rejectasync
function
- Rethrow the
// Promisify geolocation const getPosition = function () { return new Promise(function (resolve, reject) { navigator.geolocation.getCurrentPosition(resolve, reject); }); }; const whereAmI = async function () { try { // Geolocation const pos = await getPosition(); const { latitude: lat, longitude: lng } = pos.coords; // Reverse geocoding const resGeo = await fetch( `https://geocode.xyz/${lat},${lng}?geoit=json` ); if (!resGeo.ok) throw new Error("Problem getting location data"); const dataGeo = await resGeo.json(); return `You are in ${dataGeo.city}, ${dataGeo.country}`; } catch (err) { // Reject promise returned from async function throw err; } }; console.log("1: Will get location"); // const city = whereAmI(); // console.log(city); // whereAmI() // .then(city => console.log(`2: ${city}`)) // .catch(err => console.error(`2: ${err.message} 💥`)) // .finally(() => console.log('3: Finished getting location')); // More async way (async function () { try { const city = await whereAmI(); console.log(`2: ${city}`); } catch (err) { console.error(`2: ${err.message} 💥`); } console.log("3: Finished getting location"); })();
Promise.all()
- Used for running Promises in parallel
- Takes an iterable of promises as input and returns a single
Promise
- This returned promise fulfills when all of the input’s promises fulfill (including when an empty iterable is passed), with an array of the fulfillment values
- It rejects when any of the input’s promises rejects, with this first rejection reason
Promise.race()
- Takes an iterable of promises as input and returns a single
Promise
- This returned promise settles with the eventual state of the first promise that settles
- Usually use with a timer
- Takes an iterable of promises as input and returns a single
Promise.allSettled()
- Takes an iterable of promises as input and returns a single
Promise
- This returned promise fulfills when all of the input’s promises settle (including when an empty iterable is passed), with an array of objects that describe the outcome of each promise
- Takes an iterable of promises as input and returns a single
Promise.any()
- Takes an iterable of promises as input and returns a single
Promise
- This returned promise fulfills when any of the input’s promises fulfills, with this first fulfillment value
- It rejects when all of the input’s promises reject (including when an empty iterable is passed), with an
AggregateError
containing an array of rejection reasons
- Takes an iterable of promises as input and returns a single
The Event Loop
- JavaScript only has one thread of execution
- Web APIs environment (building / pending) -> Callback queue or Microtasks queue (settled) -> Call stack (consuming)
- Callback functions go into the callback queue
- Promises go into the microtasks queue
- Microtasks queue has priority over callback queue
- Event loop check if call stack has no execution and then pick a callback function from the callback queue to the call stack
Web APIs
- Browser APIs
- All browsers have a set of built-in Web APIs to support complex operations, and to help accessing data
- DOM
Fetch
APIGeoloaction
APIStrorage
API
- Server APIS (Third party APIs)
Modules
Module
Reusable piece of code that encapsulate implementation details
Usually a standalone file, but it doesn’t have to be
import { ${} } from ${}; // Dependency // module code export { ${} }; // Public API
ES6 module
Modules stored in files, exactly one module per file
ES6 module Script Top-level variables Scoped to module Global Default mode Strict mode “Sloppy” mode Top-level this
undefined
window
Imports and exports Yes No HTML <script type="module">
<script>
File downloading Asynchronous Synchronous by default Modules are imported synchronously
This make bundling and dead code elimination possible
Imports and exports are live connection, not copies
Two types of export
- Named export
- In-line
- All at bottom
- Default export
- Named export
Top-level
await
(ES2022)- Only works in modules
- Use
await
withoutasync
- Will block the execution on call stack, and importing and exporting
- Useful for calling
async
functions and consuming it instead offunc().then()
CommonJS module
- The module system in
Node.js
- The module system in
Development -> Modules -> Bundling -> Transpiling / Polyfilling -> Production
Build Process
- Bundling
- Join all modules into one file
- Polyfilling
- Convert modern JavaScript back to ES5
- Usually done by Babel
import "core-js/stable"
import "regenerator-runtime/runtime"
- Using tools like webpack or PARCEL
- Bundling
Software architecture
- Web application components
- State and database
- HTTP libaray
- For AJAX
- Optional but almost always necessary in real-world apps
- Business Logic
- Code that solves the actual business problem
- Application logic (Router)
- Code that is only concerned about the implementation of application itself
- Handles navigation and UI events
- Presentation logic (UI layer)
- Code that is concerned about the visible part of the application
- Essentially displays application state
MVC
- Model-View-Controller architecture
- Event handling in MVC
- Publisher - Subscriber pattern
- Callback functions (
render()
) andeventListener
inView
as publisher- Codes that know when to react
Controller
as subscriber- Codes that want to react
- Event handler in a nutshell
- Subscribe to publisher by passing in the subscriber function
- Callback functions (
- Publisher - Subscriber pattern
- Look at this article for future read