Table of Contents |
---|
Variables |
Anonymous Function |
Control Structures |
Strings |
Numbers |
Arrays |
Events |
Objects |
Classes |
Json |
Asynchronous JavaScript |
DOM API |
Variables
Two types of relevant declarations: let
and const
. The former is for changing variables, the latter for arrays, objects and constants.
Anonymous Functions
Functions without a name. Useful for separating large functions into blocks and passing blocks to functions expecting a callback.
001 function use_callback(function (x, y) {
002 console.log(x+y);
003 });
A shorthand called arrow functions also exists.
001 function use_callback((x, y) => {
002 console.log(x+y);
003 });
- If anonymous function is on one line, curly brackets may be omitted
- If it has only one parameter, the parentheses may be ommited
- Given a return value and only one, the keyword
return
may be omitted
The shortest possible form therefore yields
001 function use_callback( x => x * 2);
Control Structures
For the most part JavaScript control structures behave like they do in C.
Conditionals
001 if (condition) {
002 } else if (condition) {
003 } else {
004 }
005
006 // shorthand for single line expressions
007 if (condition)
008 _expression_
Triple equals are used to also compare types. Double equals perform implicit type conversions.
001 (0 == "0") // true
002 (0 === "0") // false
Looping
001 for (let i = 0; i < Collection.length;i++) {
002 console.log(Collection[i]);
003 }
004
005 const a = [0,1,2,3]
006
007 for (const value of a) {
008 console.log(value) // 0 1 2 3
009 }
010
011 break; // jumps out of a loop
012 continue; // skips ahead to next iteration of a loop
Switch Statements
001 switch(value) {
002 case 0:
003 break;
004 case 1:
005 break;
006 default:
007 return undefined;
008 }
Strings
Delimited by either " "
or ' '
. Both representations are the same internally. Neither supports interpolation. Strings are objects and possess methods.
001 "MyString".length // pre-computed property
002 "MyString".toUpperCase()
003 "MyString".toLowerCase()
String characters are accessible by indices.
001 "String"[0] // 'S'
002 "String".at(-1) // 'g' // String.at() supports negative indices to access from the end of a string
Substrings
001 "JavaScript".substring(0, 4) // "Java"
002 "Test".substring(1) // "est"
Index for the end of the substring is not required. If given, the last character included is the one right before the given index.
Concatenation
Strings are concatenated with the +
operator.
001 "str1" + "_" + "str2" // "str1_str2"
Template Strings
Support newlines and interpolation. Delimited by backticks `
.
001 let str = `This
002 string
003 keeps its
004 newlines`
005
006 let var = "World";
007 `Hello ${var}!` // "Hello World!"
Numbers
JavaScript supports the numeric separator _
and features Number.toString
for explicit string conversions.
001 let num = 1_000
002 num.toString // "1000"
When a non-numeric type is given, but a number is expected, the interpreter throws a NaN
error.
001 "string" * 4 // NaN
Also supports the increment and decrement operators:
001 let i = 0 // 0
002 i++ // 0
003 console.log(i) // 1
004 --i // 1
005 console.log(i) // 0
Conversion of Strings to Numbers
Often needed when working with user input.
001 Number.parseInt(str, radix) // radix: base of the required number system (usually 10, 2, 16)
The radix parameter is optional, but does not always default to 10. Therefore it shouldn't be omitted. The root-level function parseInt()
still works to support legacy code. It shouldn't be used anymore.
Remainder
The modulo operator %
finds the remainder of a division.
001 8 % 2 // 0
002 3 % 2 // 1
Exponent Operator
001 5 ** 2 // 25
Other Important Function
001 Math.round()
002 Math.floor()
003 Math.ceil()
Arrays
Arrays may contain values of any and different types.
001 const my_array = []; // declares an empty array
002 my_array.length // pre-computed property
Changing the value of the Array.length
property expands or truncates the array, depending on whether the new value is higher or smaller then the previous one.
Accessing Array Values
Array members can be accessed the same way string characters are accessed.
001 my_array[i]
002 my_array.at(-1)
Appending Values
Array.push(<data>)
appends a value to the end of an array and returns its new length.
001 const my_array = [1,2,3]
002 let val = 9
003 my_array.push(val) // 4
Arrays and Function
Function may return empty arrays. Function arguments are not references to the original arrays. When a function modifies an array, it has to return the modified data.
001 function func() {
002 return []
003 }
004
005 function mod(array) {
006 array.push(array.length + 1)
007 return array
008 }
Iteration
001 my_array.forEach(function(member) {
002 // member holds value of the current element
003 console.log(member)
004 });
005
006 // modern syntax
007 // callback functions have access to the scope they are contained in
008 const numbers = [1,2,3,4,5,6,7]
009 let sum = 0;
010 numbers.forEach((n) => {
011 sum += n
012 });
013 sum // 28
Sparse Arrays
Array may contain empty members that may not always be considered to be of type undefined
.
001 const a = Array(2) // [<two empty members>]
002 a = [1,2,,,4,5] // [1,2,<two empty members>,4,5]
003 a[a.length+2] = 7 // an empty member is inserted before 7
004 a.length++ // pushes an empty member to the array
The callback function passed to Array.forEach()
is not invoked for empty members.
Useful function
001 const array = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]
002 // returns an array with all members that satisfy a condition (shallow copy)
003 array.filter(n => n > 10 );
004
005 // returns the first member that satisfies the condition
006 array.find(n => n == 5)
007
008 // returns index of some value, or -1 if it wasn't found
009 array.indexOf(6) // 5
010 array.indexOf("hello") // -1
011
012 // transforms each item and returns a new array with modified values
013 const array2 = array.map(n => n * 2)
014 array.length == array2.length // true
015
016 // returns true or false depending on whether the value is present in the array
017 array.includes(1) // true
018
019 // returns a string with members separated by the argument
020 array.join('-') // "1-2-3-..."
Events
Events fire when the user interacts with the webpage. Examples among others:
- by clicking on or hovering over elements
- resizing or closing browser windows
- when websites are loaded
Event listerners (or event handlers) are blocks of code that run when their associated events are triggered.
001 element.addEventListener("type", () => { ... });
Event types include among others:
- click
- dblclick
- focus
- blur (element loses focus)
- mouseover
- mouseout
If a handler is registered with a named function it can be removed from the element and trigger:
001 element.removeEventListener(<"type">, <callback-name>)
Abort signals facilitate another way to remove event listeners:
001 const controller = new AbortController()
002 element.addEventListener(<type>, <callback>, { signal: controller.signal })
003 controller.abort(); // removes and or all event listeners associated with the controller
Alternative Handlers
Handlers may be registered via node properties. However this approach is inflexible because only one handler can be registered per property. They may also be registered inline in HTML, but handlers are easier to maintain in JavaScript.
001 element.onclick = function()
002 <button onclick="function()" />
Event Objects
001 el.addEventListener("keydown", (event) => event.key )
Events can pass objects to handlers. event.target
for example refers to the node object that fired the event. event.preventDefault()
can prevent default behavior – for example it prevents form submit elements from automatically sending requests on click.
Event Bubbling
By default event listeners that are registered on some node propagade down to all of its child nodes. Useful for delegation (see example below).
001 // an event listener registered on the <div> element
002 // will trigger when a user clicks on any of its buttons
003 <div>
004 <button></button>
005 <button></button>
006 <button></button>
007 <button></button>
008 </div>
009 document.querySelector("div").addEventListener("click", (event) => );
This behavior is disabled by element.stopPropagation()
Objects
Empty JavaScript objects are declared in a similar way to arrays. Their properties may be data or functions.
001 const obj = { }; // declares an empty object
002 obj = {
003 name: "Ken",
004 age: 22,
005 walk: function () {
006 },
007 };
Objects can be nested in other objects.
001 const obj = {
002 obj2 = {
003 property: value
004 }
005 }
A shorthand syntax for methods is supported.
001 const obj = {
002 func() { }
003 }
004 obj.func() // calls the method
Objects created in this static way are called object literals. They're useful for passing options to functions and sending data over HTTP.
Accessing Properties and Methods
001 obj.property
002 obj.method()
003
004 // bracket notation faciliates dynamic acces
005 let prop = 'name'
006 obj[prop] // obj.name
007
008 obj['obj2']['name'] // also works with nested objects
Object literals can always be extended with new properties and methods.
this
Object methods may refer to their objects by this
.
Constructors
JavaScript features two ways to implement object constructors for dynamic creation of objects.
001 function createObject(prop) {
002 const obj = {};
003 obj.prop = prop;
004 return obj;
005 }
006
007 const my_object = createObject("object_a")
008
009 // 'Object' here could be any other word starting with a capital letter
010 function Object(prop) {
011 this.prop = prop
012 this.method = function { }
013 }
014
015 const my_other_object = new Object("object_b")
Prototypes & The Prototype Chain
Each object in JavaScript inherits from another object, called its prototype.
- handled by the
__proto__
property - the last object in a chain has its prototype set to
null
The __proto__
property is hidden and accessed by Object.getPrototypeOf(my_object)
.
JavaScript searches throughout the whole prototype chain when a property or method is called on an object.
Setting Prototypes
The argument of Object.create(obj)
becomes the prototype of the otherwise empty object returned by the function.
001 // setting the prototype of objects created by constructors
002 function Decimal(value) {
003 value: value
004 }
005 Object.assign(Decimal.prototype, Number) // Number is now prototype of Decimal
Properties
Object.hasOwn(<object>, <property>)
returns true
when the property is found on object directly. Properties not meant to be accessed outside of its own methods are sometimes marked with a _
prefix.
Classes
Classes in JavaScript are built on the prototype chain. They provide a notation that mimics the syntax of popular object-oriented programming languages.
001 class Car {
002 // properties (optional)
003 brand;
004 #steeringWheel = 0; // default value and private property
005
006 constructor(brand) {
007 this.#brand = brand
008 }
009
010 startEngine() {
011 }
012 }
Inheritance
001 class Car extends Vehicle {
002 constructor(id) {
003 super(id); // calls the same method defined in parent class
004 }
005 }
Sub classes can overwrite behavior of their super class by defining a method that's defined by their super class.
JSON
Notation for storing and transmitting data. Resembles JavaScript object literals.
- only holds properties, not methods
- only double quotes encapsulate strings
- property names are always qyoted strings
JavaScript's JSON
object is responsible for handling json data.
001 JSON.parse(json) // returns an object that represents the json data
002 JSON.stringify(obj) // returns a json representation of some object
Asynchronous JavaScript
Used for slow operations that would otherwise block the execution of JavaScript.
Legacy API
Works like event handlers.
001 const xhttp = new XMLHttpRequest()
002 xhttp.addEventListener('loaded' () => {
003 // runs when the request has completed
004 }
005 xhttp.open(method, url)
006 xhttp.setRequestHeader() // e.g. "Content-type","application/x-www-form-urlencoded" for forms
007 xhttp.send() // begins the request, may be passed a data string for post request (x=y&z=a...)
Promises
Modern API to prevent hard to maintain code with nested async calls.
001 const promise = fetch(url)
002
003 promise.then((response) => { });
Unlike the old API promises can be chained by having callbacks return promises.
001 promise.then((response) => {
002 const promise = reponse.json()
003 promise.then((data) => {});
004 });
005
006 // or better yet
007 promise
008 .then((response) => response.json())
009 .then((data) => {});
To confirm that a single request was successfull:
001 if (!response.ok)
002 throw new Error(`HTTP error: ${response.status}`);
003 return response.json()
And to handle errors:
001 // attach to the end of a promise chain
002 .then(() => {})
003 .catch((error) => {
004 // is called whenever an error occurs anyplace in the promise chain
005 console.log(error)
006 }
Terminology
Possible statuses of promises:
- pending
- fulfilled (then() handler is called)
- rejected (catch() handler is called)
A promise is settled when it's either fulfilled or rejected, and resolved when it's settles or its then()
handler finished with returning another promise.
Combined Promises
When one handler is meant to run after a set of promises is fulfilled:
001 const p1 = fetch(url)
002 const p2 = fetch(url2)
003 Promise.all([p1,p2]).then((response) => {});
And if only one of many needs to be fulfilled:
001 Promise.any([p1,p2]).then((response => {});
async / await
Keywords that allow a function use promises and appear like synchronous code.
001 async function fetchData() {
002 try {
003 const response = await fetch(url)
004 if (!response.ok)
005 throw new Error(`HTTP error: ${response.status}`)
006
007 const data = await response.json()
008 } catch (error) {
009 console.log(error)
010 }
011 }
Note that an async function's return data is always wrapped inside a Promise
object.
DOM API
The JavaScript object representation of a webpage is a tree of nodes representing the page's HTML elements.
- root node:
<html>
- child node: a node that is a direct descendant of another
- descendant node: a descendant anywhere in the ancestor's structure
- parent node: the direct parent of a node
- sibling nodes: nodes that share the same parent
Fetching Nodes
001 // match first result by CSS selector
002 document.querySelector(string)
003
004 // match all results by CSS selector
005 document.querySelectorAll(string)
006
007 // legacy selectors, supported by archaic browsers
008 document.getElementById(str)
009 document.getElementsByClassName(str)
010 document.getElementsByTagName(str)
Node Lists
Internal objects representing lists of nodes. May be static or live.
- static lists: dom changes have no effect on the list
- live lists: changes to the dom are synchronized with the list
Node.childNodes
is always a live list. document.querySelectorAll()
always returns a static list.
001 // iterating node lists
002 for (let i = 0; i < NodeList.length; i++)
003 NodeList.item(i) // or NodeList[i]
004 for (const e of NodeList.entries)
005 e
006 NodeList.forEach(() => {})
Manipulation
001 Node.textContent = "new text"
002 const new Node = document.createElement('p')
003 Node.appendChild(newNode)
004
005 const textNode = document.createTextNode("some text")
006 Node.appendChild(textNode) // also moves a node from some parent to the end of another's children list
007
008 // node objects always reference one html element
009 Node.cloneNode() // creates a new html element from another node
010
011 // deleting nodes
012 parent.removeChild(node)
013 Node.remove() // not supported by legacy browsers
014
015 // insertion of HTML
016 Node.innerHTML(str) // overrides all child nodes and contents
017 Node.insertAdjacentHTML(<position>, <html>) // see (1) for position values
(1)https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML
Style Manipulation
Styles of elements are manipulated via Node.style.<property>
. However this adds styles inline. The better alternative is to adjust a node's classes via Node.setAttribute("class", <class>)
.