JavaScript (JS)
JavaScript (JS) is a popular language used to develop
- π³ Dynamic websites (DOM, jQuery...)
- ποΈ Servers and APIs (Node.js, Express.js...)
- π₯οΈ Desktop applications (Electron.js...)
- π±οΈ Mobile applications (React.js, Vue.js...)
- π§ͺ Programming styles like JsFuck (7.3k β)
- 𧨠Blockchains (Solidity...)
- ...
Where to learn?
- MDN/JavaScript (βͺ)
- W3Schools/JavaScript (βͺ)
- javascript.info (π)
- javascripttutorial (βοΈ)
- ...
See also my notes specific to websites.
Most environments support console.log
to print something.
console.log("message")
console.error("error message")
console.warn("warning message")
console.info("informative message")
// see https://en.wikipedia.org/wiki/ANSI_escape_code#Colors
console.error('\x1b[31m%s\x1b[0m', 'Shown in RED');
// print arrays/... in one go
console.log("array:", [], ". object: ", {})
// if available, used to clear the console
console.clear();
Some JavaScript tools
- Documentation β : JSDoc
- Guidelines βπ : clean-code-javascript
- Benchmarks π: jsben.ch / jsperf
- Minifier π²: javascript minifier or terser
- Lint π§Ή: eslint and/or unsupported browser features linter
Basics
JavaScript (JS) is based on ECMAScript. See JavaScript versions.
There are 3 ways to declare a variable: const
, let
, and var
.
// β
use CONST as much as possible (unmodifiable)
const five = 5
// β
let is used for scoped-variable (="normal" variables)
let str = "five"
// β var is used for global variables
var x = 5
- π Semicolon are optional. Be consistent if you use them.
- π Use
//
or/* */
for comments. - π Use
"use strict";
to enforce a strict code policy.
You can use every usual operator.
-
+
,-
,>
,>=
,&&
,||
,!
... -
^
(modulo),**
(exponentiation/power) -
+=
/-=
/...var++
andvar--
-
==
/!=
: non-strict, compare the value (5=='5'
is true) -
===
/!==
: strict, compare the value and the type (5==='5'
is false)
Variables and Types
Types are implicit in JavaScript. You can use typeof or instanceof to check the type of something.
// Boolean | typeof(b) === 'boolean'
const b = true || false
// Number | typeof(n) === 'number'
const n = 5 // β‘οΈ Number.isInteger(xxx), parseInt(xxx)
const n = 5.0 // β‘οΈ Number.isFinite(xxx), parseFloat(xxx)
const n = NaN // β‘οΈ Number.isNan(xxx)
// String | typeof(str) === 'string'
const str = "string"
const str = 'string'
const str = `template literals/string`
// Object | typeof(o) === 'object'
const o = {}
// Array | a instanceof Array | Array.isArray(a)
const a = [] // β οΈ An array is also an Object
There are two "null" values. Both are equals (null == undefined
).
- undefined: not defined
- null: defined, but null
You can use the operator ?.
and ??
to write null-safe code
// β Nullish coalescing operator
const x = null ?? default_value // default_value
const x = undefined ?? default_value // default_value
// β Optional chaining
// call on null/undefined, return null/undefined
const x = undefined?.toString() // undefined
const x = null?.toString() // null
// mix both
const x = null?.toString() ?? default_value // default_value
β‘οΈ An alternative to ??
is ||
, for instance, undefined || 50
returns 50. It's not quite used, and it's most likely a "hack".
ποΈ Control flow and usual functions ποΈ
β οΈ It's important to note that any non-boolean value will be cast to a boolean. For instance, " "
is equals to false
(" " == true
is false
).
β‘οΈ As always, you can use break
/continue
in loops.
If and Switch
if (boolean) { /* ... */ }
if (boolean) { /* ... */ } else { /* ... */ }
if (boolean) { /* ... */ } else if (boolean) { /* ... */ }
switch (value) {
case xxx: /* ... */; break;
case yyy: /* ... */; break;
default: /* ... */;break;
}
// switch + function call
const value = "";
switch (true) {
case value.includes("toto"): /* ... */ break;
}
While / Do while
while (cond) { /* ... */ }
do { /* ... */ } while (boolean)
for (i) and for (each)
for(let i = 0; i < 10; i++) { /* ... */ }
for(let i = 0, j = 0; /* ... */; i++) { /* ... */ }
for(i = 0; i < 10; i++) { /* β implicit var */ }
for(const i in array) { /* i is an index of array */ }
for(const i of array) { /* i is a value of array */ }
Concatenation
let variable = 5
console.log("the variable value is "+variable)
console.log('the variable value is '+variable)
console.log(`the variable value is ${variable}`)
// the variable value is 5
Has property/element
// same as 'array.hasOwnProperty(0)'
if (0 in [0, 1]) {} // true
// same as 'obj.hasOwnProperty("x")'
if ("x" in { "x": "..." }) {} // true
Advanced notes on usual types
Arrays
const array = []
// get info
array.length // 0
array[0] // undefined
array.includes(100) // false (=not inside)
// edit
array[0] = 25
array.pop() // []
array.push(10, 13) // [10, 13]
array.splice(0, 1) // remove 1 value from index 0
array.splice(0, 0, xxx) // insert at 0
// generate a new array
array.concat([25]) // [10, 13, "10"]
array.reverse() // [13, 10]
array.sort() // [10, 13] | array.sort((a,b) => a - b)
array.slice(0, 1) // clone with 1 values from index 0
Well-known operations
const array = [10, 13]
array.forEach(v => { /* ... */ }) // iterate (...)
array.filter(v => v === 10) // filter values ([10])
array.reduce((a,b) => a+b, 0) // reduce to one value (23)
array.join(" ") // merge separated by ' ' ("10 13"]
Create an Array from an Iterable
// ex: HTML NodeList, Map.entries/...
const array = Array.from(anIterable)
const array = [...anIterable]
Strings
const xxx = "xxx"
// get info
xxx.length // 4
xxx.indexOf("x") // 0
xxx.lastIndexOf("/") // -1
xxx.includes("xxx") // true
xxx[0] // 'x'
// convert
xxx.toLowerCase() // "xxx"
xxx.toUpperCase() // "XXX"
(" "+xxx+" ").trim() // "xxx"
// replace
xxx.replace('x', 'y') // "yxx"
xxx.replaceAll('x', 'y') // "yyy"
xxx.replaceAll(/x/g, 'y') // "yyy"
// other
xxx.split("xx") // ['', 'x']
Maps
const map = new Map()
map.set('key', 5) // add
map.get('key') // get
map.has('key') // check
for(const [k,v] of map) { /* ... */ } // iterate
// otherwise, generate an Iterable
map.entries() | map.keys() | map.values()
Create a Map from an array/object
new Map([["key", "value"], /* ... */])
new Map(Object.entries(object))
Functions
There are two ways to declare functions:
// global function
function xxx() {}
// scoped function
const xxx = function () {}
// scoped arrow/anonymous function
const xxx = () => {}
// π₯ call
const result = xxx(args)
// π typeof
if(typeof(xxx) === 'function') {}
β‘οΈ Every function returns something: undefined
, or a value. Also, if the expected arguments aren't passed, they will be undefined
.
// β
All of these are doing the same (v * v)
function pow(v) { return v * v }
const pow = function (v) { return v * v }
const pow = (v) => { return v * v }
const pow = (v) => { v * v }
const pow = (v) => v * v
const pow = v => v * v
// β οΈ for objects, you need parenthesis
const pow = v => ({ key: 'value' })
You can give default values to arguments.
function pow(x, k = 1) { return x ** k; } // 5 ** 2 = 25
Objects
JavaScript supports JSON natively, which some changes.
const object = {
// β
you don't need to quote keys
key: "value",
// β
a trailing ',' is allowed
myFunction: () => 5,
}
const object = [{}, [ {}, {} ]] // JSONArray
// access a property
object.key
object["key"] // π₯ also works
Deconstruct objects/arrays
You can deconstruct an object to easily access its values.
const {x1, x2} = { x1: 'xxx', x2: 'yyy' }
const [a, b] = [0, 1]
Convert a JSON to a String
const jsonString = JSON.stringify(object)
// pretty format (2/4 spaces)
const jsonString = JSON.stringify(object, null, 2)
const jsonString = JSON.stringify(object, null, 4)
Convert a String to a JSON
const object = JSON.parse(jsonString)
Other conversions
const array = Object.entries(object) // object to array
const map new Map(Object.entries(object)) // object to map
const object = Object.fromEntries(map) // map to object
const keys = Object.keys(object) // get keys
const values = Object.values(object) // get values
Merge objects
Object.assign({name: 'toto', age: 10}, {age: 15})
// {name: 'toto', age: 15}
Classes
β‘οΈ As a reminder, a class can be said to be a template used to create objects.
You can create and instantiate a class with a PHP-like syntax.
class Person {
x = 12; // property declared outside the constructor
// only one per class
constructor(firstname, lastName = "Doe") {
this.firstname = firstname
this.lastName = lastName
}
toString() {
// you must use "this"
return this.firstname+" "+this.lastName
}
}
const johnDoe = new Person("John")
johnDoe.firstname // "John"
johnDoe.toString() // "John Doe"
Prototypes
JavaScript is a prototyping language. Each variable has access to its definition by using the attribute __proto__
.
const x = 5; x.__proto__;
(5).__proto__; // same without variable
If you edit the prototype, then every object using this prototype will be updated. In short, you're dynamically updating their class.
x.__proto__.square = function () { return this * this }
x.square() // 25!
const y = 10;
y.square() // 100!
You can also dynamically add properties to an object
const image = document.images[0] // get an image
image.wasSelected = true // add a property 'wasSelected'
image.wasSelected // true
Static/class members
class X {
static XXX_TOKEN = "XxX"
static XXX() {
// you must use "this." or "X."
return this.XXX_TOKEN
}
}
X.XXX_TOKEN // "XxX"
X.XXX() // "XxX"
Inheritance
class John extends Person {
constructor() {
super("John")
}
toString(){
return "John!" // change the behavior
}
}
new Person().toString() // "John Doe"
new John().toString() // "John!"
Utilities
Catch exceptions
Exceptions are used to report that something unexpected occurred, which is mostly an error (Error
, EvalError
, RangeError
...).
// raise an exception
throw new Error();
// capture an exception
try {} catch {}
// capture and deal with an exception
try {} catch (e) {}
Date and time
The library moment.js (47k β) is quite used to deal with dates/...
new Date().getTime() // time since 1970
new Date().toTimeString().substring(0,8) // hh:mm:ss
Utilities
// call after xxx ms (1000 = 1s)
setTimeout(function () { }, 5000);
// repeat every xxx ms (1000 = 1s)
const it = setInterval(function () { }, 5000);
clearInterval(it)
Spread syntax
The spread syntax allows us to expand something.
const powArgs = [5, 2]
const pow = (n, k) => n ** k
// instead of
pow(powArgs[0], powArgs[1]) // 25
// use
pow(...powArgs) // pow(5, 2) = 25
You can use it
- β‘οΈ To deep clone an array with
[...array]
- β‘οΈ To deep clone an object with
{...obj}
- β‘οΈ Create an array of unique elements with
[...new Set(array)]
- ...
πͺ¨ Asynchronous work and Promises πͺ¨
JavaScript executed in the main thread will delay events, and prevent any other script from running. To avoid that, we can use asynchronous functions with async
.
async function doRequestToTheAPI() {
/* ex: fetch something from the API */
return result
}
// or
const doRequestToTheAPI = async () => { /* ... */ }
These functions return a Promise in which we can write code that will be executed when the asynchronous function has finished.
doRequestToTheAPI()
.then(res => /* do something, return xxx */)
.then(xxx => /* do something */)
.catch(err => console.error(err))
- Each function passed to
then
/catch
is called a callback. - You can chain
.then(...)
. The value returned in the previous.then
is passed to the next one. - If any
.then(...)
raises an exception, then the callback passed to.catch
is called, if there is one.
To avoid the "callback hell", we use await. But, π await can only be used inside an async
function, so it's mainly to avoid excessive nesting of Promises.
-doRequestToTheAPI().then(result => /* ... */)
+const result = await doRequestToTheAPI()
Working example using the Fetch API to get something from an API.
async function doRequestToTheAPI() {
const response = await fetch('https://jsonplaceholder.typicode.com/todos/1')
return await response.json()
}
We can improve the code by replacing return await
with return
, as the latter will also wait for the asynchronous call to generate a value.
- return await response.json()
+ return response.json()
Promises
Promises can be created manually. They are taking two callbacks, one in case of success, and another in case of failure.
β‘οΈ The latter is the same as raising an exception.
// example: function sleep in JavaScript
function sleep(ms) {
return new Promise((resolve, reject) => {
setTimeout(resolve, ms) // call resolve in xxx ms
})
}
async function callSleep() {
await sleep(5000)
console.log("Print this message after 5 seconds")
}
callSleep() // ignore .then / .catch
π» To-do π»
Stuff that I found, but never read/used yet.
-
match (/xxx/ and /x/g /x/i)
- airbnb guidelines
- interesting code
- browserify
- ESM import:
<script type="module">
(see socket.io). See also Node.js notes. Top-level await possible... ESNext.
<script src="./popup.js" type="module"></script>
c.innerText.trim().replace(/, (.*)/, "$1")
("xxx").replaceAll(/([^-.~?!]) ([^ ]+) /g, (v, v1, v2) => console.log(v, v1, v2))
- javascript.com
- Arrow functions, or anonymous functions: there is no "this".
- Closures / nested functions
-
delete xxx
-
Array.find+index
-
Symbols, yield, function*()
- 1loc
-
const element = template.content.firstElementChild.cloneNode(true);
-
const collator = new Intl.Collator(); tabs.sort((a, b) => collator.compare(a.title, b.title));
- rollupjs
- i18next, localizejs
- project-guidelines
- storybook
- validatorjs
Repositories