There's 7 types:
- String
- Number
- Boolean
- Null
- Undefined
- Symbol
- Object
Don't forget: everything in Javascript is an oject. All others than Object are referred to primitive types.
Holding text. Three ways of declaring them:
const single = 'tom';
const double = "tom";
const back = `tom`;
- A string using backquotes is called a template litteral.
- A concatenation is done with the
+
symbol in javascript. - An interpolation is when you put a variable inside of a string.
Favor interpolation instead of concatenations :
const name = "Thomas";
const phrase = `My name is ${name}, nice to meet you.`
const integer = 100;
const float = 100.5;
With numbers there's some helper methods : Math.round()
to round a float to the closest integer, Math.floot()
for the lowest integer, Math.ceil()
for the uppest integer, Math.random()
for a random number.
There addition, soustraction, multiply and divide, there's modulo and power . Modulo (%
) returns the remainder after division of one number by another. Power multiplies the value by itself (use **
for example 10 ** 2
will return 100
).
Beware of floating points! Never store as floats values that needs to be processed, like money.
Infinity and Negative Infinity exist in javascript. If you go higher than what the computer can calculate, it will return Infinity
or -Infinity
. Their type is number.
Nan
is of type Number
but indicates that it's not a number. Yes, not a number is a number.
Everything in Javascript is an object. Objects are used to create collections of data or functionnalities. The basic usage is for example:
const person = {
name = "Thomas";
age = 35;
};
There is no order in objects, as opposed to arrays. They you can access it using person.name
and person.age
.
There's two ways of express nothing in Javascript. They are both nothing
- Undefined is something (a variable for example) that has no value yet.
- Null is something that has a value of nothing.
Boolean is true
or false
and that's it. But Booleans can be calculated and compared.
10 === 10 // Returns true
10 === 100 // Returns false
Beware of type checking. ===
checks for the type, ==
does not!
"10" == 10 // Returns true
"10" === 100 // Returns false
Functions are used to regroup statements and can take in data, called arguments, and sometimes returns a value. The methods of Javascript like Math.max()
are functions existing into the programming language.
Notes on parameters and arguments:
- Parameters are the definition of the values the function accepts :
function test(param1, param2) { ... }
- Argumentes are the values you send to the function when you use it:
test(arg1, arg2);
- You can give a basic value to a parameter that will be overwritten by the argument:
function test(param1, param2 = 2) {...}
Functions are are a first class citizen in Javascript, which means they are a kind of type by themselves, as you are able to pass, return and assign them.
There are several basic ways of creating functions.
//Basic way
function doctorize(firstname) {
return `Dr. &{firstname}`
}
//Anonynous function work in certain cases
function(firstname) {
return `Dr. &{firstname}`
}
//Function expression are using anonymous functions as a type
const doctorize = function(firstname) {
return `Dr. &{firstname}`
}
Using function expressions can create some problems. As they are a variable, they can't be hoistered to the top of the file when the Javascript interpreter reads the file. So if a function expression is used before it is declared, it won't work.
Then they are arrow functions. Their syntax are more simple and they don't have their own scope (the $this keyword). Arrow functions are anonymous by default.
You can replace the word function and replace it by a fat arrow right after the arguments:
const inchToCM = (inches) => {
return inches * 2.54;
}
You can also remove the return statement by putting the function on one line and removing the curly brackets.
const inchToCM = (inches) => inches * 2.54;
If there is only one argument, the parenthesis are useless.
const inchToCM = inches => inches * 2.54;
You can also use function to return an object but it requires a special syntax to avoid that curly brackets of the object are interpreted as a block of code. So this:
function makeABaby(first, last) {
const baby = {
name: `${first} ${last},
age: 0
}
return baby;
}
Becomes this where parenthesis are used:
const makeABaby = (first, last) = > ({ name: `${first} ${last}, age: 0 })
It's not very readable so it's not an obligation to use them.
IIFE is used to immediately run a function when the code is loaded. You put a function inside parenthesis as their are read first, and then use a double parenthesis behind to declare it as a function.
(function() {
console.log('Running IIFE');
return 'Done';
})();
A method is a function that lives inside an object. For example in console.log()
, console
is an object and log()
is the function.
const Thomas = {
name: 'Wes Bos',
//Classic way
sayHi: function() {
console.log('Hey Thomas');
return 'Hey Thomas';
}
//Short hand method
yellHi() {
console.log('HEY THOMAS');
}
//Arrow function methoed
wisperHi: () {
console.log('Hi Thomas, I'm wispering);
}
}
A callback function is an anonymous function called by another function at a later point in time.
button.addEventListener('click', function() {
console.log('Nice job !');
});
setTimeout(function() {
console.log('This is the time');
}, 1000);
setTimeout(() => {
console.log('This is the time');
}, 1000);
You can select something with the inspector and use $0.value
to get its value instead of selecting it by id or class.
You can do a queryselector or queryselectorall with $ or $$.
Basically, where are my functions and variables available? A variable in a file in the wild is called a global function and if is declared using var
, is available throught the window
object. Functions are available throught the window
object too.
- Function scope is means that everything in the function is only available in the function unless we return it.
- Block scope (for ex an if statement) does not allow the use of the variables outside itself except if declared with
var
.
Javascript has lexical scoping / static scoping. It means that variable/scope lookup doesn't happen where they are run but where they are defined. For example:
const dog = "loulou";
function logDog() {
console.log(dog);
}
function go() {
const dog = "fifi";
logDog();
}
go();
//Result is loulou and not fifi
Basically, when javascript is executed, functions declarations are moved to the top, so you can execute a function and declare it after. It really depends of the mindset of the developer. Some prefer running functions first as a "what does this file do" and put functions declarations after as "how does that work".
For variables hoisting, Javascript will create the missing variables when code is hoisted but since it doesn't know their values, they will be set to undefined and then updated as the file is parsed.
A closure is the ability the access the parent scope from a child scope, even after the parent function has been terminated.
function outer() {
const outerVar = "Outer!";
function inner() {
const innverVar = "Inner !";
console.log(innerVar);
console.log(outerVar);
return inner;
}
const innerFunction = outer();
innerFunction();
//Returns "Outter!" and "Inner";
In this example, outer()
is executed when assigned to innerFunction
, and you can now execute inner
. So you could think that, since we now have inner()
instead of outer()
in the variable, you can't access outerVar
. But it actually works as outerVar
is stored in memory for use at a later time.
Closures can be used to create private variables from the same variable. For example:
function createGame(gameName) {
let score = 0;
return function() {
score++;
return `Your ${gameName} game score is ${score}`;
}
}
const footballGame = createGame('football');
//Returns : "Your football game score is 1"
//Executed again : "Your football game score is 2"
//Etc...
const rugbyGame = createGame('rugby');
//Returns : "Your rugby game score is 1"
//Executed again : "Your rugby game score is 2"
//Etc...
Here despite using the same function to create a game, their scores are separated.
In the rare case where you can't move the javascript file after the html, use : document.addEventListener('DOMContentLoadeded', init);
.
Today to select elements on the page:
document.querySelector('p');
returns the first element.document.querySelectorAll('p');
returns a nodelist of elements.
To get the content of an element:
.textContent()
will return both the text but also script and style tags and their content..innerText()
returns only the text..innerHtml()
returns the html inside an element..outerHtml()
returns the html inside an element + the container element.
The insert stuff into the textContent:
.inserAdjacentText()
has multiple positions where you can insert content (beforebegin, beforeend, afterbegin...)
To get data-attributes:
myObject.dataset.nameOfMyAttribute
To create html:
const myP = document.createElement('p');
- Then insert it:
document.body.appendChhild(myP);
or `document.body.insertAdjacentElement('beforebeing', myP);
It's possible to create an html object from a string using a range instead of using .innerHtml()
. The Range interface represents a fragment of a document that can contain nodes and parts of text nodes:
const myHTML = `
<div class="wrapper">
<h2>Thing</h2>
</div>
const myFragment = document.createRange().createContextualFragment(myHTML);
The user might add html into the string you ask him to enter inside an input. This is cross side scripting (XSS). See the security part.
It's possible to traverse the dom nodes or remove them:
.children
.firstElementChild
.lastElementChild
.previousElementSibling
.nextElementSibling
.parentElement
.remove
Dom elements create events when they change. To listen to them, use event listeners. An event listener takes a type of event it needs to listen and a function as a second agument. Connecting a dom element with an event listener is often called 'binding'.
myDomEl.addEventListener('click', function() {
console.log('click');
});
It's possible to remove an event listener with .removeEventListener
but only if a named function or an arrow function stored into a variable is used. Anonymous functions cannot be removed.
It's possible to listen to multiple elements, for example various buttons with the same class.
const buttons = document.querySelectorAll('.btn');
buttons.forEach(function(button) {
button.addEventListener('click', function() {
console.log('click');
});
});
If you pass a parameter in the function of an addEventListener, you can recover the event itself.
myDomEl.addEventListener('click', function(event) {
console.log(event);
});
This code returns a PointerEvent with a lot of objects inside like isTrusted
, pressure
, pointerType
, etc...
To recover the element you just clicked, use event.target
. Once it's done you can recover everything related to the button itself. For example you can also recover data attributes in the event object using event.target.dataSet
;
What happens when you have nested elements inside the element you are listening to? If you use event.target
you'll get both. To get the element that fired the listener, you need to use event.currentTarget
.
When you trigger an event listener, the event will propagate to its parents to allow, in the case of one of them also had an event listener, to be triggered too. This is called event propagation and when it's ascending from the element to its parents, it's called event bubbling. It can be stopped using event.stopPropagation
to only recover the triggered item.
The opposite of event bubbling is event capturing, where the event goes from the parent element to the children elements. You can use it by adding capture : true
as the last argument in the event listener declaration. It's also possible to use event.stopPropagation
but it will then do the opposite: instead of isolating the element triggering the eventm it will isolate the parent.
Some elements in html have a default event when triggered. It's possible to stop them using event.preventDefault();
- Use semantic html, don't missuse buttons and links for example.
- When an element is not clickable by default, use
keyup
instead ofclick
. - When using preventDefault on a button or link that changes the page, you have to vocalize the change. If not, a blind person can't know you changed pages.
- Probably more stuff.
The order in wich operators execute in javascript is the same as in mathematics:
- brackets / parenthesis
- exponent
- division
- multiplication
- addition
- substraction
Regex start and end with a /
. If it ends with /g
it means global, so it will search for all occurences of the terme searched.
By default values in variables, despite of their type, are kinda true or kinda false. Truthy and falsy are a way of simplifying flow statements.
Are thruthy:
1
-10
and all other numbers except0
string
array
even if emptyobject
even if empty
Are falsy:
0
undefined
null
NaN
empty string
The act of transforming a type into a boolean. A single bang !
will give the opposite of their thruthy/falsy, and a double bang !!
will convert them to a real boolean.
Short hand if statement : let word = count === 1 ? 'item : 'items';
You can abuse it by using &&
: isAdmin && showAdminBar();
setTimeout(function() { console.log('Time')}, 500);
allows to console log after 500 milliseconds.
setIntervals(function() { console.log('Time')}, 500);
allows to console log every 500 milliseconds
Everything is an object in Javascript. Objects allow to group different values, giving them a key and a value. Be careful that objects are used when the order of it's values doesn't matter, unlike arrays. If you declare an object in a const, it's possible to modify the values inside but you can't redeclare a value with the same key. To forbid changing an object, you can use object.freeze
.
When using a variable as a value for an object and both the key and the variable have the same name, there's no need to write the key:
const age = 100;
const person = {
age,
name: "thomas",
things: {
thing1: "thing",
thing2: "thing2",
};
const personFroze = Object.freeze(person);
To delete a property, use delete
: delete person.age
.
A method is a function inside an object. When using it, this
takes the parent object. In this case, this
is person
:
const age = 100;
const person = {
name: "thomas",
sayHi: function(hi = "hi") {
return `${hi} ${this.name}`
};
const personFroze = Object.freeze(person);
As opposed to variables, even if two objects have the same content, they are not considered the same and comparing them will return false
.
When objects and arrays are copied (for example const object2 = object1
), any update to object2
will also change object1
. This happens because object2
is not a real copy of object1
, it's a reference pointing to it. It's the same if you pass an object as an argument in a funciton.
So how do you copy an object?
- You can use a spread operator:
const object2 = {...ob ject1}
that will navigate insideobject1
and copy each value. - Or you can use the
assign()
method:const object2 = Object.assign({}, object1);
But doing this goes only one level deep and the deeper levels are still references. This is called a shallow copy. To do a deep copy or clone you need a library like lodash
and its cloneDeep()
function.
To merge two objects, it's also possible to use the spread operator: const object3 = {...object1, ...object2}
. If two keys have the same name, the last one will be the one used.
Maps are different than object has they allow to use any type for the key instead of just a string. This allows to create dictionnaries. You can get the size of a map using map.size
and the order of elements in a map is guaranteed.
const myMap = new Map();
myMap.set('name', 'thomas');
myMap.set(100, 'This is a number');
//Will return:
Map(2)
size: 2
<entries>
0: name → "thomas"
<key>: "name"
<value>: "thomas"
1: 100 → "This is a number"
<key>: 100
<value>: "This is a number"
There are several methods for maps: .set()
, .get()
, .delete()
. Using a map as a dictionnary can be very useful, for example:
const score = 200;
const prizes = new Map();
prizes.set(100, 'bronze');
prizes.set(200, 'silver');
prizes.set(300, 'gold');
console.log(`You win ${prizes.get(score)}`);
//Will return: "You win silver"
It can also be usefull in a loop, using destructuring:
for (const [points, prize] of prizes) {
console.log(`${points} - ${prize})
}
Use a map when the order needs to be maintained. The downside of maps is that there is no literal declaration (you have to use set, get, delete) outside of using an array of arrays: new Map([['name', 'thomas'], ['age', 35]);
which can be a little strange. Also maps cannot store methods and you can't stringify a map into JSON without converting it to an object before, for example: const object = Object.fromEntries(myMap);
.
An array is used to store data in a specific order. Each thing inside an array is called an item and it's position is called the index. There are no keys in arrays. To test if a variable is an array, use the .isArray()
method. Arrays are zero based, which means they start at 0
.
Be careful that some array methods are mutable methods (like reverse()
) can mutate the array and others called immutable methods don't (.slice()
). In the case if you don't want to mutate an array, copy it first into a new variable using the spread operator, then use the mutable method: const array2 = [...array1].reverse();
.
To add items to the end of the array, use .push()
. Since it's a mutable method, use the spread operator: const array2 = [...array1, 'new val'];
. To add at the front of the array, use unshift()
. To add items in the middle, use slice inside a spread operator: const array2 = [...array1.slice(0, 2), "new", ...array1.slice(2)];
.
You can get the index of an item by using the .findIndex()
method. To return an index based on the value of an item, you can use a kind of loop, like this: const thomasIndex = names.findIndex(name => name === "thomas");
.
Static methods are method that live in the Array object.
Array.of('1', '2')
allows to declare an array.Array.from({lenght: 10})
creates an array with 10 items.Array.from({lenght: 10}, function (item, index) {return index;})
returns an array where the value of each item is it's index.Array.entries(object)
will transform the content of an object into items inside the array.Array.keys(object)
will return the keys of an item.Array.values(object)
will return the values of an item.
Methods that lives in all arrays.
join()
will turn all the arrays into a string and can take an argument as a separator.split()
can turn a string into an arraypop()
returns the last item of the arrayshift()
adds an item at the start of the arrayincludes()
allows to check if a value is in an array and returns a boolean
find()
returns the value of the first element in the provided array that satisfies the provided testing function.filter()
creates a new array with all elements that pass the test implemented by the provided function.some()
tests whether at least one element in the array passes the test implemented by the provided function. It returns a Boolean value.every()
tests whether all elements in the array pass the test implemented by the provided function. It returns a Boolean value.sort()
sorts the elements of an array in place and returns the sorted array.
There are several ways of looping on an array.
array.forEach(function(item) {
console.log(item)
});
Map is like a machine that takes data and returns it transformed. It's not mutating the existing array items, it basically mirrors its structure and changes the value according to the task demanded.
const arrayOfNames = ['thomas', 'coralie'];
function capitalize(name) {
return `${name[0].toUpperCase()}${name.slice(1)}`;
}
const capitalizedNames = arrayOfNames.map(capitalize);
console.log(capitalizedNames)
It can be used on an array of objects.
Returns a subset of the items. Creates a new array with all elements that pass the test implemented by the provided function: const over40 = peopleList.filter(person => person.age > 400);
It's when you put a function inside a function.
function findByProp(prop, propvalue) {
return function isStudent(student) {
return student[prop] === propvalue;
}
}
The reduce() method executes a reducer function (that you provide) on each element of the array, resulting in single output value.
Needs more work
Basic looping is with for:
for (let i = 0; i <= 10; i++) {
console.log(i);
}
For of allows to loop over values. It's also useful for sequences of promises.
for(const letter of name) {
console.log(letter)
}
For in allows to loop over the keys.
for(const prop in name) {
console.log(prop)
}
Good to know: when using Object.entries
to get access to the content of an object, you don't acces the data stored inside the prototype. For in allows to see the keys stored in the prototype.
While runs indefinitely until a condition is satisfied.
let cool = true;
i = 0;
while (cool === true) {
console.log("you are cool");
i++;
if (i > 100) {
cool = false;
}
}
Everything is an object in JavaScript because every type has methods attached to it.
When using new
you create an instance of an object. Variables and objects don't need it because they have shortcuts declaration but some types of objects don't, like dates: const myDate = new Date();
. But you can also create you own types of objects.
The think keyword refers to the instance of an object that a function is bound. It means that this is going to be bound to what it is attached to. The this keyword is always bound to a function. This can also be used to store informations inside the object.
function Sandwich(toppings = [], customer) {
this.toppings = toppings;
this.customer = customer;
}
const jambonBeurre = new Sandwich(["jambon", "beurre"], thomas);
If you create an instance of an object with new
but want all instances to share some code (like methods or variables) you can use the prototype to store them. For example for an eat()
function inside Sandwich, instead of duplicating it, store it inside the prototype.
function Sandwich(toppings = [], customer) {
this.toppings = toppings;
this.customer = customer;
this.bites = 10;
}
Sandwich.prototype.eat = function() {
if(this.bites > 0) {
this.bites = this.bites -1;
} else {
console.log("No bites left");
}
}
.bind()
allows to change the this
of a function. For example:
const person = {
name: 'Thomas',
sayHi() {
return `hey ${this.name}`;
}
}
const coralie = {name: "coralie"};
const sayHi = person.sayHi().bind(coralie);
Using sayHi() will return "hey coralie" instead of thomas because the bind changed the this
to coralie's this
.
Does the same thing as bind()
but also call the function.
Same as call but only accepts a single array of arguments.
JavaScript is a single threaded language which means only one thing can be run at the same time. Since JavaScript is asynchronous it doesn't stop when it sees something that might take time like a timeOut() function.
There are three things to remember to get how JavaScript works:
- The
Call Stack
is what the JavaScript interpreter is reading, basically code from to to bottom. - The
Web Apis
is where the JavaScript interpreter store what is waiting or what is listenned to. When something finishes or happen, it's sent to thecallback queue
. - The
Callback Queue
is where theCall stack
comes to get some extra work once it doesn't have anything left to read.
The event loop is the thing that checks if the call stack
is done and if things from the callback queue
can be send to it for execution.