Principal JS types:
To know the type of something you can use typeof method:
(It won't work with array or null though...)
To convert data types:
A string can be define with ' : 'Hello', or " : "Hello"
Common built-in string methods:
String Interpolation: to insert a variable within a string without using the "+". You need to wrap your string with backticks: ` and interpolate code with ${}
var : variable. Scope is global or local to an entire function (regardless of block scope)
let : variable that can be reassigned. Limited to the scope of a the block statement (block-scoped)
const : variable that can't be reassigned. (But value it holds can change, for example if its an object, you can change elements of the objects but not reassign it to a whole new object)
Naming convention: in JS variables are lower camelCase: myVariable
Tips: x = x + 1 can also be written : x += 1
Same for -=, *=, /=
Same for ++, --, //, **, it means adding, subtract, divise or multiply by 1
In JavaScript, to get a random number between 0 and 1, use the Math.random() function:
console.log(Math.random()); >>> will outputs for ex : 0.5408145050563944
Then if you want a random number between 1 and 10, just multiply the result of Math.random by 10, then round up or down:
Truthy / Falsy Values: If you use an if on a non-bolean value (a string or a number for example), this value is neither "true" nor "false". But it is "truthy" or "falsy":
Short-circuit Evaluation: The pipe comparison tool || can replace an if. Indeed it will return option "A" OR "B". So it will test first the first option. So if "A" exists, it takes "A", otherwise it takes "B". So it works exactly like an if.
Naming convention: in JS methods are lower camelCase: myFunction
Function have "hoisting" caracteristic, meaning you can call it (and use it) before declaring it (even if it is not a good practice).
Since ES6 you can define a default value of a function parameter.
Return: by default a fonction will always return undefined. Even when it work perfectly. In order for a function to return a result you need to use the keword return. With that I tell the function what value it has to return.
The return kw also allows you to stop the execution of a function. Indeed a function will stop when it meets return:
Function Expression: Allows you to store an anonymous function inside a variable and then call this function by calling the variable. You can also add arguments directly inside the variable (we usually use the const kw for that):
Warning: a function expression is not "hoisted". You can't call it before declaring it
Arrow functions: Since ES6, arrow functions synthax allows to define a function without using the "function" kw:
Arrow functions specificities:
It defines a "perimeter" on which you can use a variable. If variable is not define inside a function (outside the { } ), it belongs to the "global scope". It is then a "global variable", you can access it everywhere.
On the contrary if a variable is defined inside a function (inside the block), it belongs to the scope of this function, and you can use it only inside this function. It has a "block scope". It is then a "local variable".
If you have to many global variable, it creates "scope polution". All those variable can have indesirable effect everywhere in your code. It is better to have few global variables and to localize it.
You can have two identical variable with different values depending on which scope you are looking
In JS an Array is defined with the [] notation: const hobbies = ['surf', 'teuf', 'comic books'] (here it is 3 strings but it can be all sorts of elements)
Index: you can then use each elements of an array calling its index. The first one have the index "0": hobbies[1] >> will return "teuf"
Length: return the number of elements in the array. hobbies.length == 3
(Btw you can use the same logic to return letters of a string: 'renodor'[2] >> will return "n")
Common built-in array methods:
For Loop:
for (initialisation; stop condition; to execute at the end of each iteration) { code to execute; }
The "stop condition" is a boulean. The loop continues as long as this bolean is true. And it stops when the boulean is false.
Ex:
While Loop:
while (stop condition) { code to execute; }
You just specify the stop condition. You have to define the initialisation before and the iteration within the code to execute
Ex:
Do...While Loop
do { code to execute; } while (stop condition)
It is similar to the while loop but you specify the code to execute before. So it will execute at least one time (and until the stop condition is false). Whereas a normal while loop can sometimes never run if the stop condition is always false.
Ex:
Break: the break kw allows you to force a loop to stop (even if the stop condition is still true). Inside the block code of your loop you have to add an if condition like that : if (condition) { break; }
(A loop within a loop is called a "nested loop").
You can assign a fonction to a variable:
And so you can then call the function by calling the variable like that: variableA(); (You can even pass arguments to it.)
In JS, functions, like variables, are "first class objects", so like other objects you can assign proprieties to it like (.length() .name(), .toString() etc...)
You can also pass a function as an argument to another function. You call those functions "call back" because they are called during the execution of the high-order function.
When you pass a function as an argument you don't want to call this function. By calling it you would have the result of the function as an argument (and not the function itself). That is why when you put a function as argument you put it without the parenthesis.
Iterators are built-in array methods that allows you to iterate (loop) easily on arrays
.forEach() : apply the same function to all the elements of an array.
Ex:>>> Will apply the console.log() to each elements of 'fruits'. (So we can say that forEach(); takes a call-back function as an argument.)
This function doesn't create a new array or change the original array. It just apply the function to each elements of the array. And it doesn't return anything, it always return undefined.
.map() : also apply a function to all elements of an array, but this time it returns a new array (with the new values). So you can assign the map method to a new variable to store the array that will be created during the execution.
Ex:.filter() : Apply a statement to all elements of an array, and return a new array with only the elements for witch this statement returned true
Ex:.findIndex() : apply a statement to all elements of an array, and return the index of the first element that return true. (It stops at the first it finds).
Ex:If .findIndex() doesn't find any element that fulfill the condition, it will return -1
.reduce() : apply a function to all elements of an array, and return only one value. It takes 2 parameter: an accumulator and the currentValue.
Initially the accumulator is the index 0 of the array, and the currentValue is the index 1 of the array. It will then loop over the array and apply a function using this two parameters. So during the second loop for example, the accumulator will be the returned value of the first loop, and the currentValue will be the index 3 of the array, etc...
ExSo here, initially:
So the first operation will be 1 + 2
Then the function do it again. The new accumulator is 3 (1 + 2), and the new currentValue is 4, so the second operation will be 3 + 4
Etc...
So at the end, summedNumbers will return 17.
You can also add a second argument to the reduce method, which will define an initial value to the accumulator. (Other than the index 0 of the array). So it will add a new step to the operation.
Instead of starting with accumulator = 1, you start with accumulator = 100. And so initialy, currentValue is 1. After that, it is the same:
some() : will look if at least one element of the array fulfill the condition, and will return true or false
every() : will look if all the elements of the array fulfill the condition, and will return true or false
In JS an Object is defined with the { } notation : let spaceship = {};
Objects are used to store data thanks to key/value pairs (called properties). Properties can be anything (string, number, functions etc...)
You then access properties with the .dot operator: spaceship.color >>> will return silver
Or with brackets: spaceship['Fuel Type'] (You have to use brackets actually if the property name has a space, a number or a special character)
If you call a property that doesn't existe, it returns undefined
You can also add, modify or delete properties:
When you store a function inside an object, you call it a method:
When you add a method to an object, the value of the property is an anonymus function:
Or shorter like that:
To call a method inside an object you also use the .dot operator, with the function name and parenthesis: alienShip.invade();
You can nest objects within objects (pass an object as a parameter of an higher object). And you can then call nested objects with a serie of .dot operators: spaceship.nanoelectronics['back-up'].battery;
Pass by Reference: is the fact of modifiying an object property directly via a function.
First you define your object:
Then you create functions that will modify/add a property to this objec:
Then if you call those functions on your object:
It will modify your object like that:
Object loop : You can't use a normal loop on objects (because object key-value pairs are not in order). So you have to use for...in loop.
Ex:Will outputs:
"This" KW: when you define a method inside an object an that this function call properties of this same object, you have to use the this kw, otherwise the scope of the function doesn't allows you to access the property:
Without the this kw, the provideInfo() function doesn't work because it can't access model and energyLevel.
WARNING: the this kw doesn't work the same way, with arrow functions. Because arrow function already have an inherent this which is the one of the global scope. So you can't use this with arrow functions inside objects.
Privacy: In JS there is no built-in privacy for objects. So there is a naming convention to signal that a property is not supposed to be altered:
Getters: You use the "get" functions inside an object to perform more complex operations inside your object. (It is like a normal method property with a get before function name). It allows you to :
Tips:
Setters Methods work exactly like getter methods but allows you to reassign values of existing properties within an object:
Factory functions: represent the "structure" of an object. You use it when you want to create a lot of similar objects. You create a factory function that have the « property » structure of the object you want to create. For that you need to create a function that « return » an object. And you pass your object properties as your function arguments. And then by calling this function and passing it arguments, you create a new object.
Ex: this is a factory function to create a monster
Then you can create a new monster only by calling the function:
Since ES6 you have a shortcut. Because the key and the value of the proprerties where always the same, you can simplify and write only the key, like that:
Destructured assignment: is a shortcut that allows you to extract the property of an object to put it in a variable. Normally you would do like that:
But to save you some keystrokes you can put the name of the property inside {} directly as the name of the variable like that:
Common built-in object methods:
Classes are tool to quickly produce similar objects. Classes are like "templates" used to create objects.
For example if you have this object:
You can create this class to build the same type of objects:
And then you can create new objects thanks to the class. It is called "create an instance". Like that:
Details of how to build a class:
Inheritance: when you have several similar classes you can create a "parent" class that will share the same properties and methods that its children classes. The children classes will have the same properties and methods but you can also add new ones.
So if we take our last example (dog creation). We can actually create a parent Animal class, like that:
And after that create a child Cat class, that will have the same name and behavior properties, but also a new one called usesLitter :
So to create a child class you need:
Tips: if you have several properties with arguments that need to be called in the parent class by the super KW, you all put it in the SAME super kw (not several ones). Like that:
Static Methods: Sometimes you will want a class to have methods that aren't available in individual instances, but that you can call directly on the class itself. (They are similar to class methods in Ruby)
To create a static method you need to use the static kw like that:
We create a static method that return a random name when called. Because of the static kw we can only access this method by appending it to the Animal class.
So you can call the generateName method like that : Animal.generateName()
But if you create instances of Animal (or child classes), for example : const tyson = new Animal('Tyson')
Then you can't do that : tyson.generateName() >> It will return TypeError
In JS, Modules are reusable pieces of code that can be exported from one program and imported for use in another program.
The old way to do it is:
>>> You imported your module and assigned it to a local variable. You then can use all the methods and properties of your module within your file.
You can also wrap your data within the module.exports statement to directly export it:
The new way (ES6) to do it is with export & import kws.
Export Default: if you want to export your entire code into one module:
Named Exports and Imports: in ES6 the export and import kw also allows you to export data through the use of variables. You can create variables, methods, objects or any data and export it with its specific name to be stored in its own variable:
You can also write the export kw before your variable/function to export it directly:
Export as & import as: You can use the export as kw to change the name of the variable you want to export:
After that you have to import it normally with the alias name you created
You can also import as to give an alias or change the alias of a variable when you import it
Import all: you can import an entire module and give it a specific alias. It will store it in an object that you can use later on
You can also combine and mix every export types together (default exports, named exports, direct on declaration export etc…):
Here an example of a promise construction:
The executor function generally starts an asynchronous operation and dictates how the promise should be settled. So normally the resolve() and reject() functions aren't defined by the programmer. When the Promise constructor runs, JavaScript will pass its own resolve() and reject() functions into the executor function.
Promise states: a promise is in one of these 3 states:
Asynchronicity example using setTimeout(): setTimeout() is a functions used to execute some code after a delay
Asynchronous JavaScript uses something called the event-loop. After two seconds, delayedHello() is added to a line of code waiting to be run. Before it can run, any synchronous code from the program will run. Next, any asynchronous code in front of it in the line will run. (This means it might be more than two seconds before delayedHello() is actually executed).
Ex: of how you will use setTimeout() to construct asynchronous promise
>>> We invoked returnPromiseFunction() wich return a promise. We assign that promise to the variable prom. prom will initially have a status of "pending". (it hasn't settled yet). Then after 1 second it will settle and be "fulfilled", and resolve with the message "I resolved!".
Then() method: asynchronous promise always have an initial state of pending and then will settle. We use the then() method to say to the program what after it settled: "I have a promise, here's what I want to happen when it settle". So then() will be fired only after the status of the promise has been settled (fulfilled or rejected).
then() is a higher-order function, it takes two callback functions as arguments. We refer to these callbacks as handlers:
We can invoke then() with one, both or zero handlers
Ex:>>> Since prom resolves, handleSuccess() is invoked with prom‘s resolved value, 'Yay', so 'Yay' is logged to the console.
But with typical promise, we won't know whether it will fulfill or reject, so we need to provide the logic for either case (the success handler and the failure handler):
catch() method: to write cleaner code we use the separation of concerns principle. So we separate our resolved logic from our rejected logic:
And to be even cleaner we use the catch() function. It takes only one argument: the failure handler. It is like using a then() with only the failure handler:
Chaining multiple promises: it is called composition:
If you want to handle failure, you just put a catch() method chained after the last then(). It will be executed if any of the promise reject. (But you won't know which one...)
2 common mistakes:
Promise.all() : is a way to handle multiple promise without caring about the order. Promise.all() accepts an array of promises as its argument and returns a single promise. That single promise will settle in one of two ways:
To sum up about promises:
Since ES8, async…await was introduced and allows a better readability and scalability of promises. async kw is used to write function that handle asynchronous actions. async kw allows to creates functions that return a Promise:
Async functions always return a promise. It will return in 3 possible ways:
So, a normal promise written like that:
Can be written with the async kw like that:
Await: Without the await kw, an async function don't do much. The await kw can only be used inside an async function. It returns the resolved value of a promise. await will pauses the execution of the async function until the desired promise is settled.
Ex:It is particulary usefull when you do dependent promises (a serie of asynchronous action).
Indeed, with native promises you will do a chain of then() functions. Like that:
Whereas with async…wait:
Try...Catch: When catch() is used with a long promise chain, there is no indication of where in the chain the error was thrown. This make debugging challenging.
With async…await, we use the try…catch statements for error handling:
Here we will "try" what is in the body of the try kw. If any of the promised will reject, the catch body will be executed, with the err argument being the reject value of the promise that was rejected.
Handling Independent Promises: If we have several independent promises that we need to await there is a trick. Indeed, we don't want to wait promise1 to be solved, then promise2 to be solved, and then do our resolution…
We want both promises to run (at the same time) and then, when both are solved, we want our resolution. We can do it like that:
Await Promise.all() : We can also use the await Promise.all() to wait for multiple promises. Indeed Promise.all() takes an array of promises as argument and will resolve when all of these promise are fulfilled. So if we put await before it, it will await all those promises to be settled before going further.
One of JavaScript's greatest assets is its non-blocking properties, or that it is an asynchronous language. It uses an event loop to handle asynchronous function calls: when a program is run, function calls are made and added to a stack. The functions that make requests that need to wait for servers to respond get sent to a separate queue. Once the stack has cleared, then the functions in the queue are executed.
Ex:The first and second message are run from the stack. The one in setTimeout() is added to the queue so it will always run after the others. (Even if the setTimeout() is set to 0 milliseconds.
XHR Get Request: the AJAX GET request allows you to retrieve data from a server.
Boilerplate of an AJAX GET request using an XMLHttpRequest:
XHR Post Request: The AJAX POST request allows you to send data to a server.
Boilerplate of an AJAX POST request using an XMLHttpRequest:
Fetch Function:
Boilerplate of a simple fetch GET request:
Boilerplate of a complete fetch GET request:
Boilerplate of a simple fetch POST request:
Boilerplate of a complete fetch POST request:
It is similar to the GET request, only that the initial call takes two arguments: an endpoint and an object, instead of just one. In this object you have the verb and the body of the HTTP request. The body is the info you want to send to the server.
Fetch with Async and await KW: you can also use async, await, try and catch kw with fetch() to use GET and POST XHR requests:
GET:
POST:
DOM: the DOM is the Document Object Model, it is the browser representation of the HTML file. (Basically what you see when you open a website page on your internet browser...). Javascript allows you to interact with the DOM.
Common DOM manipulation methods:
Events: clicks, scrolls, mouse over, select etc... All those interactions are "events" that you can "listen" and react to with Javascript.
Some common events:
Those events can be called on DOM elements (like a div, a form, an input etc...), or directly on the page itself (document, window)
To start listening on an event we use the addEventListener method:
event.preventDefault() prevent the default behavior of an event. For example, by default when a form is submited the form action will be executed (probably sending you to another page). If you want to prevent that, because you want to modify the form data before sending it, you'll do something like: