Basics
Runtime: Where code gets executed for example Browser or on a Server thanks to Node.Js
JavaScript(JS) got invented to make websites interactive.
JavaScript is a programming language - you can execute it on the fly e.g. in your browser console.
In the relation to HTML,CSS and JS we can say:
- HTML is the content - NOUN
- CSS is the adjectives - How is something? - ADJECTIVE
- JS - dynamic and interactive elements - VERB
JS is interpreted line by line.
V8 is engine under the hood that let's run JS extremely fast.
To run JS on a webpage you need an html document with a <script>...</script>
Tag to run your JS inside.
The advantage is that you don't have to load a seperate file. But it makes sense to seperate the website content from the javascript functionality
You can write directly inside the script tag or you reference an external file
<script src = "app.js"></script>
We usually reference the script at the end of the html file.
Print out statements
If you want to show information in the console you can use
console.log('hi momi')
To print multiple information you can seperate it by a comma
console.log(var1,var2,'Hallo')
Show Alert Popup in browser
alert('string text')
Variables
A value is a piece of data - the smallest piece of information in our program.
A variable is like a box that contains objects. By assigning a value to a variable we define what the box contains.
Defining variables
There are several different ways to define/declare variables
let luckyNumber = 23;
let luckyNumber;
<- undefined value (default value)
When we want to assign two variables we can also do it in one line
let x,y
If you want to assign a specific value to two values in one line you could write x = y = 20*4
-> x =80 y=80
We use let
for variables, that can change later.
JS is a dynamically typed language so no type assignment to variable is necessary.
To define an emtpy value
let luckyNum = null;
To define a variable that will not change in the future we use const
.
const
variable can't reassigned later.
const birthYear = 1992;
Because it can't be changed later we also can't declare empty constant variables.
So as a rule of thumb always declare your variables as const
unless you are 100% your variable is gonna change later then use let
The third way is to use var
but these just should be ignored or used if you really know why.
Under the surface let
and var
are really different although they look like the same.
Naming variables
In JS a variable naming conventing is camel case. The first word is small and to seperate the second we start with a capital letter.
let firstVariable
We can't start variable names with a number.
let 3years = 3
wouldn't work.
Variables only can contain letters,numbers,underscore or the dollar sign.
Don't start a variable with a capital letter because we use it for object oriented programming.
Variables written in uppercase are often reserved for constants that never change.
let PI = 3.1415;
Make your variable names descriptive.
let myFirstJob = 'Programmer'
let currentJob = 'Teacher'
instead of
let job1 = 'Programmer'
let job2 = 'Teacher'
Datatypes
Number: Floating point numbers - used for decimals and integers
let age = 23;
let weight = 22.23;
Strings
Strings are a sequence of characters
const firstName = 'Jonas';
To concatenate strings we can use the +
const jonas = "I'm " + name + ' a ' + year
For more complex string concatenation we can use template literals. We can write a string more normal and then insert the variables into the string. To write template literals we need backtics and then set the variables into ${variableName}
We can write every expression inside the {year - birthyear}
We also can use backticks for all strings
We also can use template literals for multiline strings.
Bool: Logical type that can be true
or false
- used for making decisions
let goodWeather = true
Undefined: Value taken by a variable that haven't been defined yet - "empty value"
let children;
Null: Also means empty value but gets used in other circumstances.
Symbol: Value that is unique and cannot be changed
BigInt: Larger integers than the Number type can hold
JS has dynamic typing which means you don't have to define the data types of the values that get stored. JS automatically stores it for you. In JS the value stores the type not the variable.
To evaluate what datatype some value is we can use typeof
There is a bug inside of JS when you do typeof null
. It returns an object
which is wrong. It should return null
.
Type-Conversion and -Coersion
In JS there is Type-conversion and Type-coersion
- Type-conversion: When we manually convert from one type to another
- Type-coersion: When JS automatically converts types for us in the background
JS can only convert to string,number and boolean.
Convert a String into a Number
You can use the build-in Number()
function.
Using the Number()
function only gives a converted value. It doesn't automatically assign it to the input value.
If we try to convert something into a number that isn't JS returns NaN
for Not a Number/Invalid number
Convert Number into String
You can use the build-in String(23)
function.
or we can use
const numberToString = num => num.toString();
Coersion
Javascript is converting some types internally. That is called Type-Coersion
console.log('I am' + 23 + 'years old')
console.log('1' + 1) => '11'
If you use -
or *
, /
it triggers the conversion from string to number
console.log('23' - '10' -3) => 10
console.log('23' * '10') => 230
Truthy and Falsy
Falsy values are not exactly false but will become false when we convert them into boolean
0,'',undefined,null,NaN
will become false when we convert them into boolean.
console.log(Boolean(0))-> false
console.log(Boolean(undefinied))->
const money = 0
if(money){
console.log("Dont spend it all")
}else {
console.log("You should get a job")
}
JS converts the number 0 into a falsy false Boolean.
Everything else will be truthy values
Variable scopes
Global scope - variable is available everywhere.
If we define a variable in a function, the variable is locally scoped - only available in that function and can't used outside of the function.
If you have statements in your code a variable also can just exist in that statement
let a = 'global'; //global scope
function fun (){
let a = 'function'; //local scope
if(true){
let a = 'block'; // block scope
//when you use a var variable in that statement the variable gets available in the whole function
var b = 'hoisted';
}
}
Comments
We write comments inside our code with eather single line comments or multi line comments
Operators
Operators allow us to transform or combine multiple values.
Arithmetic operators
Add - +
Subtract - -
Multiply - *
Devide - /
Exponent - **
2 ** 3 means: 2 to the power of 3
We can use the +
to join strings
firstName + " " + lastName
Assignment operators
The most basic assignment operator is the =
to assign values to a variable.
x+=10
is a short version of x = x +10
. The same works with the other basic arithmetic operators -=, *= , /=,
If you want to add or subtract just 1 there is another short version x++
and x--
Comparison operators
We use comparison operators to create boolean values.
a>b
- a is greater than b
a<b
- a is smaller than b
a<=b
- a is smaller/equal to b
a>=b
- a is greater/equal to b
We can also assign directly a value to a variable
let isThisBigger = a>b
Operator precedence
The order operators get executed in JS.
The higher values get executed before lower values.
So usally all the math operators get executed before the comparison operators.
Control structure - Conditionals
If-else
To make decisions in our code we can use if else
to decide what happens if something is true or false
const age = 19
if(age >= 18){
console.log(`Sarah is start driving`)
} else {
console.log(`She is not old enough`)
}
You don't have to use the else
part if you don't want anything executed when the condition isn't met.
We can extend the if-else
with an else if
to ask for more conditions
const weather = 'hot'
if(weather === 'cold'){
console.log("I'm so cold')
} else if (weather === 'mild'){
console.log("I'm mild)
} else if(weather === 'hot'){
console.log("uff so hot)
}else {
console.log("no idea")
}
Switch statement
The switch does a strict comparision of a variable and iterates through different cases.
const day = 'monday'
switch (day){
case 'monday': // equal to day === 'monday' strict comparision
console.log("do monday stuff")
break;
case 'tuesday':
console.log("do tuesday stuff")
break;
case 'wednesday':
case 'thursday'://If you do this with two cases it will be executed for both
console.log("Write this")
break;
case 'friday':
console.log("chill mate")
break;
case 'saturday':
case 'sunday':
console.log("Enjoy the weekend")
break;
default:
console.log("WOW a new day that we havent seen before')
}
If we use a switch without break statement the code continues executing
const count = 0
swithc(count){
case 0:
count+=1
case 1:
count+=1
case 2:
count+=1
case 3:
break;
}
console.log(count)
Ternary Operator
The Ternary/conditional operator allows us to write something like an if-else structure in just one line.
age >= ? console.log('I like to drink this') : console.log('I like to drink water')
Operator always produce values - so they are expressions. So we can produce new values with the Ternary Operator.
So because the Ternary operator is an expression we actuall can insert the into string literals
So we use Ternary operators when we need to make a quick decision. We still need if-else for bigger parts of codes.
Equality Operators
Equal
JS has two equality operators which test if two values are equal.
===
strict equality operator - does not perform type coersion - only returns true when both values are exactly the same.
'18' === 18 -> false
==
loose equality operator - does type coersion.
'18'==18 -> true
It is suggested to always use ===
the strict one.
Difference
To check if something is different from another value we can use !==
Boolean Logic
Boolean logic uses true and false values to solve complex logical problems. In order to do that it combines several logical operators to combine true and false values.
AND
&&
AND operator - both needs to be true to return true.
if A AND B are true -> returns true
OR
||
OR operator - one needs to be true to return true
if A OR B are true -> return true
NOT
!
inverts a boolean value
!true -> false
const hasDriversLicence = true
const hasGoodVision = false
if(hasDriversLicence && hasGoodVision){
console.log("You are able to drive")
} else {
console.log("you shouldn't drive")
}
Properties of logical operators
But the logical operators doesn't necessary need to return boolean values
console.log(3||'Jonas') -> 3
They can use and return any data type.
Logic operators do also short-circuiting
or short-circuit-evaluation. It returns the first value that is a truthy value.
Truthy values - all Values except false,0,-0,0n,"",null,undefinied,NaN,document.all
OR operator
The OR operator returns the first truthy value of all the operants or the last value if all other values are falsy. So we can use the OR operator to set default values.
3 || 'Jonas' -> 3
'' || 'Jonas' -> Jonas
true || 0 -> true
undefined || null -> null //because undefined not truthy it automatically returns the other value
false || undefined || null || 'Hallo' -> 'Hallo'
So this allows us to short-circuit the assigning of default values if a value doesn't exist
//instead of
const guest = concert.guests ? concert.guests : 20;
// we can
//if concert.guests undefined it sets 20 as default. If not undefined it sets concert.guests
const guest = concert.guests || 20;
concert.guests
exists but is 0
. Then it sets 20 although concert.guests is defined as 0.
Nullish Coalescing Operator
Nullish Coalescing works with the idea of nullish values which are only null
and undefined
For that case we need to use Nullish Coalescing Operator ??
AND operator
The AND operator works the exact opposite way to the OR operator. It returns the first falsy value or the last value if all of them are truthy.
//When first value is falsy -> return that value
// Because && is only true when both values are true -> so if the first value is false already we don't need to look at the second and we return the first value
0 && 'Jonas' -> 0
//when first value is truthy -> return the second value
7 && 'Jonas' -> 'Jonas'
//this returns the first falsy value
'hello' && '23' && null && 'Jonas' -> null
Logical Assignment Operators
JS has Logical Assignment Operators which makes the assignment event faster
||=
assigns a value to a variable if the current variable is falsy
const house1 = {
street:"street1",
doors: 1
}
const house2 = {
street:"street2",
windows:10
}
house1.doors ||= 2; -> house1.doors = 1
house2.doors ||= 2; -> house2.doors = 2
0
For this we can use the nullish operator ??
const house1 = {
street:"street1",
doors: 0
}
const house2 = {
street:"street2",
windows:10
}
house1.doors ??= 10; -> house1.doors = 0;
house2.doors ??= 10; -> house2.doors = 10;
There is als the &&=
assignment.
This allows us to replace a value when there is actually a value.
const house1 = {
street:"street1",
doors: 1
}
const house2 = {
street:"street2",
windows:10
}
house1.windows &&= 23 -> house2.windows = undefined
house2.windows &&= 23 -> house2.windows = 23
Loops
Loops allow to execute repetitive tasks.
For-loop
For loop keeps running while the condition is true.
Looping through arrays
const arrayNames = ['Jonas','Heinz','Reinhold']
for(let i = 0;i<arrayNames.Count;i++){
console.log(`Name is:${arrayNames[i]}`)
}
Continue
With continue
you exit the current iteration and move on to the next iteration.
for(let i= 0; i<=20;i++){
if(i/2 !== 0){
continue;
} else {
console.log(`${i} is an even number`)
}
}
break
break
is used to leave the whole loop.
Loop backwards
While-Loop
A while loop runs as long as the condition of the loop is true.
For example when we don't know how many times the loop should run we can use a while
-loop.
Role a dice till you get the value = 6
let dice = Math.trunc(Math.random()*6)+1;
while(dice!=6){
console.log(`you rolled a ${dice}`)
dice = Math.trunc(Math.random()*6)+1;
if(dice=== 6) console.log('Loop is about to end...')
}
Expressions vs Statement
Expression is code that produces a value like 3+4
Statement is a bigger piece of code that doesn't produce a value
This doesn't really produces a value it just performes some actions. So JS expect Expressions and Statements in differnt places. In Template Literals we can only insert expressions but not statements.
console.log(``I'm ${2037-1991}``)
Functions
Functions are one of the main building blocks in JS. They work by taking an input, process the input and returning it. We can think of them like being machines that process input we give and return an output.
To write a function declaration we write
function name(parameters){...}
To execute a function is also called calling/running or invoking a function. We then reference the function name and round brackets name()
.
We can also pass values into a function to be processed in a function. These are called Parameters
. Here in the example a
and b
.
//Function declaration
// input params
function add(a,b){
// return a value
return a +b;
}
//calling a function
add(4,5)
With functions we also can assign their return value directly to a variable
let fruitMix = fruitMachine(apples,oranges);
So when coding we should keep our code dry which means we shouldn't repeat ourselves in our code. For that functions are super useful.
Functions are objects so they can be used as expressions(Function expression) allowing them be used as variables. When we write function expressions we don't really define a name for the function so we create anonymous functions.
Sometimes we need to write functions as expressions and sometimes as declarations. So the difference between these two is that we can call the function declaration before they are defined.
We couldn't do that with function expressions.
It is personal preference to decide which way you use your functions.
In JS functions are also a value. That means we can do *Method borrowing - copying a method from another object
const jonas = {
name:"jonas",
calcAge: function () {
console.log("calculate")
}
}
jonas.calcAge()
const matilda = {
year:1992
}
matilda.calcAge = jonas.calcAge
matilda.calcAge()
Another way is to save a function in it's own variable
Anonymous functions
An Arrow function is a shorter version to write a function expression.
//instead of
const calcAge = function(birthYear){
return 2025-birthYear
}
// we can write
const calcAge = birthYear => 2025 - birthYear;
// To call the function
const age = calcAge(1991);
This works only if we just have a one line process in the function.
If we have multiple steps or multiple parameters we still need to go back to the brackets.
// one paremeter
const yearsTillRetirement = birthYear => {
const age = 2025-birthYear;
const retirement = 65-age;
return retirement;
}
yearsTillRetirement(1990);
// multiple parameters for the function.
const yearsTillPayedDebt = (startYear,endYear) => {
const yearsToPay = endYear - startYear;
return `You have to pay ${yearsToPay} years.:(`
}
yearsTillPayedDebt(2020,2099);
We also can construct higher-order functions where a function is used as an argument or a return value.
Functions can also be nested to create closure, that incapsulates data and logic from the rest of the program.
Normally when you call a function that has a variable with a primitive value let a = 10;
it is stalled on the call stack which is the browsers short term memory.
When you call a closure the inner function can still acccess variables in the outer function and even after the initial function call. That happens because JS automatically stores the data in the outer function and the heap memory, which persists after the function call.
JS functions can only return one thing.
Rest Pattern in Funktions
We can use the Rest pattern to unpack an undefined amount auf parameters into a function
const add = function(...parameters){
let sum = 0;
for(let i = 0;i<parameters.length;i++){
sum += parameters[i];
}
console.log(sum);
}
add(2,4)
add(3,2,3)
add(3,4,5,6,6)
const x = [2,68,93]
add(...x);
Arrays
Arrays are a datastructure where we can collect different variables.
const names = ['leon','ro','il'];
another way is
const years = new Array(1990,1991,1992);
Arrays can hold any datatype that we want. We can add expressions into arrays and we can even put whole other arrays into an array.
Another possibility is to put function calls inside an array declaration.
const ages = [calcAge(years[0]),calcAge(years[1]),calcAge(years[2]),calcAge(years[3])]
To pick an element from an array we use square brackets []
const firstFriend = names[0];
.
To get the amount of elements in an array we use .length
const len = name.length;
To get the last element of your array.
const lastElement = names[names.length-1];
We can put any expression that we want inside of the square brackets to pick the element we want.
To change the data inside an array at a specific position.
names[2] = 'Ju';
Even though we defined an array as const
we still can change its elements. But we can't exchange it with a whole new array.
Array operations
Add Element to the end of the array
With .push()
we can add an element to the end of the array.
friends.push('me')
.push()
returns the length of the new array.
const newLength = names.push('name')
Add Element to the beginning of array
The .unshift()
method allows us to put an element to the beginning of an array.
names.unshift('Heinz')
As .push()
the .unshift()
method returns the length of the new array.
.push()
adds an element to the end of the array.
We also can add multiple elements in push
array.push('word1','word2','word3')
Remove last element of an array
To remove the last element of an array we use the .pop()
methods
names.pop()
The .pop()
method returns the removed element from the array.
Remove first element from array
With .shift()
we can remove the first element from an array.
names.shift()
The .shift()
method returns the removed element from the array.
Get position of element in array
To find out which position a certain element in the array has we can use .indexOf('variableName')
names.indexOf('me')
If the element is not in the array the function returns -1
A function from new JS version is .includes('variableName')
.
It returns a boolean if the element is in the array.
Loop through array
So instead of using a for
loop we also can use for(const item of arrayName)
. This is meant to give you the current element.
for of
loop also allows to use break
and continue
If you still want the index
but want to use the for of
loop
for(const item of menu.entries()){
console.log(item);
}
/*
[0, "food1"],
[1, "Food2"],
[2, "food"],
*/
.entries()
creates an array of arrays with each child array having the elements of index
and the item
.
With further destructuring
Merge two arrays
Wie can use the .concat()
function to merge two arrays.
Find biggest/smallest element in Array
let max = testArray[0];
let min = testArray[0];
for(let i= 0; i<testArray.Length;i++){
const curTemp = testArray[i]
if(curTemp>max) max = curTemp;
if(curTemp<min) min = curTemp;
}
Ignore elements that aren't the type you look for
Empty an array
With .splice
we replace the content of the array with nothing.
array.splice(0,array.length)
Unpack all array elements
So the spread operator takes each element out of an array and adds them as a single element into another array.Expand existing array
const oldMenu = ["pizza","pasta","dumplings"]
const newMenu = [...oldMenu,"Choco"]//this creates a new array
To useful application for using the spread operator are creating shallow copies of an array
and merge multiple arrays together into a new one
const menu1 = ["Dumplings","noddles with water"]
const menu2 = ["pizza","burger"]
const menu3 = ["IceCream","Tiramisu"]
const allDishes = [...menu1,...menu2,...menu3]
The spread operator works on all iterables: arrays, strings,maps,sets. But NOT objects.
Rest pattern - Collect unspecified elements in assignment
The rest pattern allows us to pack elements, that aren't specifically assigned, into an array. Always use the rest pattern as the last element.
const [a,b,...others] = [1,2,3,4,5]
console.log(a) -> 1
console.log(b) -> 2
console.log(others) -> 3,4,5
Input multiple values into array via prompt
Array Destructuring
to destructure an array we can use []
to assign elements
Only taking the first and the last element
This makes also switching the order easier
With destructuring we can have a function returning an array and then directly destruct the result into different values
...
const restaurant = {
order: function(starterIndex,mainIndex){
return [this.starterMenu[starterIndex],this.mainMenu[mainIndex]];
}
}
//Destructuring the returned array
const[starter,main] = restaurant.order(2,0)
Sometimes it is useful to set default values when we destructure arrays. For example when we get data from an api
Get Value from prompt
We can use the prompt()
function to ask the user to input a value.
const favNumber = prompt("What is your favorite number")
It returns a string.
this
this
is a keyword that references an object based on how a function is called.
We called from the global scope it references the window
object in the browser.
If that same function is attached to an object and is called by that object this
references the object.
Read more about this
here
You can manually bind a function to some other objects using .bind()
method.
In modern JavaScript we can define functions with the arrow syntax ()=>
Arrow functions don't have their own this
value and they are always anonymous which makes them ideal for function expressions.
When passing arguments in functions a primitive like a number is passed by value which means a copy is created of the original variable.
If the argument is an object that means it is stored in the heap and it is passed by reference. That means multiple parts of the code is mutating the same object.
const num = 23;
//copy of num gets created
someFun(num);
const obj = new Object();
//
someFun(num,obj);
Objects
An object contains a collection of key:value pairs or properties and values.
A key in the object is called property.
The order of the values in an object doesn't matter at all. We reference values by their key
The easiest way to define an object is with the object literal syntax using const name = { }
:
There is also an Object()
Type that is created with a constructor.
const human = new Object();
Objects can inherit properties from each other with human.__proto__.__proto__
Every object has a private property that links to one prototype.
Retrieve data from object
The first way to get a property from an object is the .
notation:
const humDNA = human.dna
The other way is to use the ['keyName']
way
const humDNA = human['keyName']
The difference is that with the brackets way we can put any expression inbetween the brackets. So we can compute it from a calculation
Add new property to the object
To set no properties to the object we again can use both ways
human.eyeColor = 'blue'
human['eyeColor'] = 'blue'
Object methods
In JS functions are just like a value which means we can add functions to an key:value pair in an object. Any function added to an object is called method. We can reference the name of the function as a key and the value as an expression. In Objects we need a function expression instead of a definition.
const human = {
...,
calcAge: function(birthYear){
return 2037 - birthYeah;
}
}
console.log(human.calcAge(2099))
console.log(human['calcAge'](2099))
If we want to access a inside the object a variable from within we can use the this.
keyword. It is equal to the object calling the method.
To avoid multiple calculations we can calculate a value once then store it in the object and then can retrieve it later as a property from the object.
const human = {
birthYear: 1991,
calcAge: function(){
this.age = 2037-this.birthYear;
return this.age;
}
}
Destructure Objects
To destructure objects we use {}
const object = {
name:"me",
location:"world"
}
const {name,location} = object
console.log(name,location)
To change the variable names from the property names we
To set default values for a property that may not exist in the object. If it exists the default values get's overwritten.
Mutating variables while destructuring objects
In order to mutate the variables a,b
while destructuring our object we need to put it inside ()
to make it work.
destructure nested objects
const object = {
feed: {left:43,
right:44
}
}
const {feed:{left:l,right:r}} = object;
console.log(l,r)
this allows to use destructuring inside functions for example
const order = {
food:"pizza",
time:14
}
function orderSomething({food = "none" ,time = 0}){
console.log(`Order received: ${food} at ${time}`);
}
orderSomething(order);
Spread operation on objects
Shallow copy of object
Rest operations on objects - Collect unspecified elements in assignment
The rest pattern allows us to pack elements, that aren't specifically assigned, into an object. Always use the rest pattern as the last element.
const object = {
a:0,
b:2,
c:4,
d:6,
f:8
}
const {myA,myB,...others} = object
console.log(myA) -> 0
console.log(myB) -> 2
console.log(others) -> {c:4,d:6,f:8}
Enhanced Object Literals
With Enhanced Object Literals we don't need to reassing objects from outside an object with the variable name. We just implement it as an element.
const openingHours = {
mon: {
open:0,
close:24
},
tue: {
open:0,
close:24
}
}
const restaurant = {
name:'my restaurant',
openingHours
//dont need openingHours = openingHours
}
Also methods doesn't need to be declared with function
const restaurant = {
...
order(starterIndex,mainIndex){
return [this.starterMenu[starterIndex]]
}
...
}
Another enhancement is that we can compute the variable names inside the object
const weekdays = ['mon','tue']
const openingHours = {
[weekdays[0]]: {
open:0,
close:24
},
[`day-${2+4}`]:{
open:2,
close:20
}
}
Optional Chaining
With optional chaining ?.
if a certain property does not exist undefined
will be returned.
console.log(restaurant.openingHours.mon?.open)
We also can check for multiple properties
console.log(restaurant.openingHours?.mon?.open)
const weekdays = ['mon','tue','wed','thu','fri','sat','son']
const openingHours = {
[weekdays[0]]:
{
open:0,
closed:23
},
[weekdays[1]]:
{
open:0,
closed:23
},
[weekdays[1]]:
{
open:0,
closed:23
},
}
for(const day of weekdays){
const open = openingHours[day]?.open ?? 'closed';
console.log(`On ${day}, we open at ${open}`);
}
This also works for methods
restaurant.oder?.(0,3) ?? 'Method does not exist'
And on arrays to check if an array is empty
const users = [{name:"me",email:"Hallo world"}];
console.log(users[0]?.name ?? "User array is empty")
OOP
JavaScript supports object oriented programming with the class
keyword.
class Human {
constructor(name){
this.dna = 'MMYYDD'
this.name = name
}
get gender() {
return this.gender;
}
set gender(){
this.gender = val;
}
}
But classes are syntactic sugar for prototype and inheritance and objects.
A class can define a constructor that is called when the object is first created. The class also can have properties and getter and setter functions to access the properties.
Classes also encapsulate functions as methods on an objects instance. We also can make the methods global to the class name by defining it static
method
class Human {
constructor(name){
this.dna = 'MMYYDD'
this.name = name
}
get gender() {
return this.gender;
}
set gender(){
this.gender = val;
}
walk(){
console.log('walking')
}
static isHuman(human){
if(human.dna == 'AACTG'){
return true;
}
}
}
Other Datastructures
Array
Hold dynamic collection of index items.
const list = ['aa','bb','cc','cc']
Sets
Hold a collection of unique items
const uniq = new Set(list)
Map
Hold key:value pairs and it is easier to loop over this data collection than objects.
Garbage collection
JS is garbage collected which means it will automatically deallocate objects from memory when they are no longer referenced in the code.
With Maps and Sets all your properties will always be referenced.
For optimization there is WeakMap()
and WeakSet()
that contain properties that can garbage collected and the memory reduced.
Non blocking event loop
Normally when you run your code in a script it gets executed synchronously line by line. The next line can't start before the previous line finished.
With an event loop we can write asynchronous code in javascript which runs in a serparate thread pool while the rest of the application continues to execute. It allows multi task
We can writer async code with a promise()
const promise = new Promise(
(resolve, reject) => {
// Do something async here
if(sucess){
resolve('success');
} else {
reject('failure')
//To raise an error
}
}
)
A promise is a wrapper for a value that is unknow right know but that will resolve to a value in the future. For example a call to a 3rd party api that resolves to some data.
The consumer of the promise can use methods .then()
and .catch()
to handle this outfcomes
promise
.then(success => {
console.log('yay!',success);
})
.catch(err =>{
console.log('oh no!',err)
})
You also can define an async function
that automatically returns a promise
async function asyncFun(){
//waits till the promise is resolved
const result = await promise;
}
// To implement error handling you want to wrap it in a try catch block
async function asyncFun(){
try{
const result = await promise;
} catch(error) {
}
}
To keep bigger code more easy to read we can use modules which is code in an extra file for parts of the code.
By default all the code in a file is private just to that file.
In order to share code with other files we can make the code default export by assigning export default function name
.
Then we can import it in another file with
import functionFun from './help.js';
we also can export and import variables
export const hallo = 344;
import {hallo} from '.help.js';
Node package manager
To use code from other developers we can use npm javascript package manager
We can install packages with npm install
. It downloads the code it in the node_modules
folder. It also provides a package.json
file that list out all the dependencies that you use in your project.
Js in Browser
The browser is based on document object model (DOM) where the UI is based on a structured tree of html elements.
It allows JS to access HTML elements and style to manipulate them
When JS interacts with an element from the website it is called Dom manipulation
A browser provides an api to interact with these elements with window.document
.
The DOM methods and properties are part of the WEB APIs that browser implement.
Grab element from html
The document
allows us to grab an individual html element using the .querySelector()
it takes a css selector as an argument and will find the html elements that has the same classname.className
, id #idName
or tag name. It returns an instance of the element class.
If there are multiple instances with the same className querySelector()
will pick the first the first element.
To grab multiple elements at the same time with .querySelectorAll('.button')
Select and manipulate elements
We can select an element by it's class
document.querySelector(.message).textContent = 'new Text'
or select it by an elements unique id
document.querySelector(#id).textContent = ...
document.getElementById('id').textContent = ...
Get and Set values Input field
document.querySelector(.inputField).value
<- This returns a string
Converting into a number
Number(document.querySelector(.inputField).value))
Setting value of input field.
document.querySelector(.inputField).value = 10
Grab text from element
document.querySelector(.message).textContent
Handling Click Events
When we want our javascript to react to something that happens in the dom we need to listen to the event that happen to the element. For example when a btn is clicked, we can assign a function that gets called when the button gets clicked.
Every time an event happens JS creates an event object which allows us to get information about the specific event.
const btn = document.getSelector(.btn);
const btn2 = document.getSelector(.btn2);
btn.addEventListener('click', () => {
//Only gets called when the event happens
console.log('clicked')
})
btn2.addEventListener('dblclick',function () {
console.log("hallo")
})
Handling KeyPressed Events
The keypress event is a global event. For global events we usually listen to the whole document
document.addEventListener('keydown',handleEscKey)
const handleEscKey = (e)=> {
if(e.key === "Escape"){
console.log(`${e.key} pressed`)
}
}
There are three key events
keydown
- when we put down the key
keypress
- fired while we have finger on the keyboard
keyup
- when we lift our finger up from the keyboard
Manipulate CSS styles
To manipulate the css style we have to access the html element and then the style
property.
Whenever we manipulate a style we have to specify a string
. We can't write a number.
document.querySelector(body).style.backgroundColor = '#60b347'
document.querySelector(.message).style.width = '30rem'
The changes get applied as Inline Styles
so the css files doesn't get changed or manipulated
It looks like div class="message" style="width:30rem">text</div>
The Inline Styles
overwrite the properties of css file.
Why is that new info
Manipulate classes
To check if an element contains a class we can use
if(elemnt.classList.contains('hidden'))
We can use JS to remove
classes from an html element. For example we can have a modal which has the class
We then call myElement.classList.remove('hidden')
and the class is removed and the element is shown on the screen.
To add a class we can call modal.classList.add('hidden')
We also can use myElement.classList.toggle('hidden')
this will add a class if it's not there and remove a class when
it already exists
Math
Cut of decimal part of number
We can call Math.trunc()
Math.trunc(Math.random()*20)
Front-end frameworks
Many developers use front-end frameworks that produce declerative code where the UI is a function of its input data /states.
These frameworks encapsulate JS, HTML,CSS into components. Inside a components the data is reactive. It can be bound from js directly to html
Bundle
Module bundler
Network waterfall
Dynamic imports
Only import bundle when it is needed
Let JS reload automatically when code is changed
We can use the VSCode extension Live Server or we can set up our own node.js server
Run Javascript on a server
With node.js is a javascript runtime. It allows us to run javascript outside of a browser and on a server.
with node
command we can execute javascript.
See if node is installed
node -v
Install live server
npm install live-server -g
-g
install globally
On mac you might need sudo
in front of npm.
Run live server
Call live server
live-server
Strict Mode
Strict mode assures that we write secure JS code.
For that we just add 'use strict'
at the beginning of our code.
It needs to be the first line in the code.
Strict mode also blocks the usage of certain variable names that JS has blocked to use because it might use them in the future. Like interface
or private
Electron node.js with the browser to create app react native to build whole apps
typescripts
DOM
Document Object Model
What every browser uses to model all the html elements on a webpage.
Errors
Unexpected token
You used a wrong variable name.
How to Learn to Code
What can you do wrong
- dont have clear goal
- just copy the code without thinking what the code means
- dont reinforce what you learned by doing small challenges
- dont practise coding
- dont create own project ideas
- quickyl become frustrated
- lose motivation because you think you never can know everything
- learn in isolation
What do write
- write down your specific,measurable,realistic and time-based goal on a piece of paper. I want to become a web developer in 6 month.
- Always type the code
- Understand the code
- Use immediately what you learned.
- Take notes
- Challenge yourself
- practise coding on your own - come up with your own project ideas.
- dont be stuck in "tutorial" hell
- you will never know everything
- Even devs after 20 years struggle to get their code to work and it is a tedious slog.
- Never learn in isolation - explain new concepts to other people.
- Share your learning progress with web dev community #100DaysOfCode #CodeNewbie #webdev
- Keep challenging yourself, run into lots of problems and fix them
How to think like a developer
How to solve problems in coding:
- stay calm and slow down - don't jump at a problem without a plan
- take a logical and rational approach
-
Framework:
-
Take a step back and make sure you understand 100% the problem:
-
Ask right questions to get a clear picture of the problem
- What does ... mean?
-
What should we do if something else happens?
-
Divide and conquer:
-
Break big problem into smaller sub-problems.
-
Do as much Research as possible
-
For bigger problem write pseudo code before writing actual code.
See objects as a Table in Console
console.table(object)
Debugger in Chrome
In the debugger under Sources
we can set breakpoints to evaluate values at a certain stage.
You can find the debugger next to console under sources.
Don't forget to unselect the breakpoint at the end.
Bundler
Bundler is a tool in which you send assets like JavaScript,Css,HTML,images, TypeScript. The bundler handles these assets, applies potential modifications and output a bundle composed for web friendly files like HTML,CSS,images and JS. Like a pip in which you send non-web-friendly assets and at the end of the tube you get web-friendly assets back.
Bundler can do even more. You can use a bundler to create a local server, manage dependencies, improve compatibility, support modules, optimize images, deploy on a server, minify the code, etc.
Webpack
Webpack is most popular bundler.
JS behind the scenes
Every software need to use some hardware resources like cpu and memory to run. In Low-level languages like c you have to manually manage these resources. In high level languages we don't have to manage the resources. They have abstractions that take care for us.
Garbage Collection takes memory management away from the developer. Its an algorithm that automatically removes old unused objects from the computer memory. Like a cleaning guy that cleans up for us.
JS is an interpreted/just-in-time-compiled language. A computers processor only understands 0 and 1 - which is called as machine code. We write human readable javascript code which is an abstraction from machine code. And this code needs to be translated into machine code by the compiler/interpreter. This happens inside the javascript engine.
Javascript is a multi-paradigm language. A paradigm is an approach of structuring code. Three paradigms are: Procedural programming: Organising code in a linear way with some functions in between.
Object-oriented programming(OOP):
In JS OOP is a prototype based object oriented approach: almost everything in JS is an object except primitives like numbers,string etc. Arrays are objects for example.
We create arrays from an array blueprint - which is like a template - and this is called Prototype
- Array.prototype.push
for the .push()
method.
The prototype contains all the methods.
The array in our code then inherits all the methods from the blueprint.
Functional programming(FP):
Many languages are only procedural,oop or functional, but javascript does all of it. That makes it super flexible.
JS is a language with first-class
functions, functions that are treated as variables.
We can pass into other functions and return functions from functions.
Javascript is a dynamic-typed
language - we don't assign datatypes to variables. They only become known when the engine executes the code.
Also the type of variables can easily be changed.
If you want to use JS with types you can use Typescript
Concurrency model is how the engine handles multiple tasks that are happening at the same time. JS runs in one single thread - it only can do one thing at a time. In computing a thread is a set of instructions executed in the cpu. Where the code is executed in the processor.
To avoid that long task are blocking the single thread we create an event loop. The event loop executes these tasks in the background
and puts them back in the main thread once they are finished.
Javascript Engine and Runtime
A Javascript engine is a program that executes JS code. Every browser has its own javascript engine but the most well known is googles v8 engine which powers chrome and node.js.
Any JS engine contains a call stack
and a heap
.
A call stack
is where our code gets executed - using execution context
A Heap
is a unstructured memory pool which stores all the objects that our application needs.
How is the code compiled to machine code? There are two ways how code gets converted into machine code
Compilation
The entire code is converted into machine code at once - written to a binary file that can be executed by a computer. Two steps: Source code -> COMPILATION = machine code -> EXECUTION = program running
Interpretation
Interpreter runs through source code and executes it line by line. Source code -> EXECUTION line by line = program running
The source code gets converted into machine code right before execution - not ahead of time.
Modern JS now uses a mix of interpretation and compilation - Just-in-time compilation Compiles the entire code in machine code once and executes it right away.
JS Engine example
As JS code enters the engine the first step is to
Parse
/read the code.
During the Parsing
the code is parsed into a data structure called abstract syntax tree - AST.
Every line of code that is meaningful to the engine gets spit up in pieces and then saving
all the pieces into the tree in a structured way. It also checks if there are syntax errors.
Next step is the compilation which takes the generated AST and compiles it into machine code.
This machine code gets executed right away. The execution happens in the Call Stack
.
Modern JS engines have optimisation strategies where they first create unoptimised version of machine code so it can start executing as fast as possible. Then in the background code is optimised and recompiled in the background multiple times.
JS Runtime
Browsers are JS Runtime.
A JS runtime is like a big box which includes all the things that we need to use JS.
At the heart is always the JS Engine.
In order to work properly the engine needs access to apis like DOM
, Timers
,FetchAPI
. WebAPIs are functionalities provided to the engine but not part of JS language. JS get's access to theses apis over the gloabel window
object.
A typical JS runtime also contains a Callback queue
which is a data structure that contains all the callback functions.
We attach eventHandler functions to DOM elements to react to certain events. These event handler functions are called Callback functions
.
As the event happens, e.g. click
, the callback function will be called - the callback function is put in the callback queue. When the call stack is empty the callback function is passed into the call stack so it can be executed. This is caused by the event loop - it takes callback functions from the callback queue and puts them into the call stack so they can be executed.
How is JS code executed
When the compilation is finished a global execution context
is created for the top level code - code that is not inside a function. So only the code outside of functions is executed because you only want to execute the functions when they are called.
A global execution context
is an environment where JS is executed. It stores all the necessary information for the execution like variables or arguments passed into a function.
In any JS project there is only one global execution context
.
Once the top level code is processed functions get executed as well. For each function call one execution context is created - containing all the necessary information to run the function. All these execution contexts make up the call stack.
Inside a execution context is:
- Variable environment:
let
,const
andvar
declarations - Functions
arguments
object- Scope chain
this
keyword
Arrow functions don't have arguments
object nor this
keyword.
The Call Stack
is where the execution contexts get stacked on top of each other in order to keep track of where we are in the program execution.
So the execution context that is on the top of the stack gets executed. When it's finished it gets removed from the stack and the next execution context gets processed.
If the program is completely finished also the global execution context
is popped out of the call stack.
Scope and Scope chain
Scoping defines how the variables are organized and accessed in our program. "Where do they live and where can we access a variable"
In JS we have Lexical scoping
- scoping is controlled by placement
of functions and blocks in the code.
A function inside another function has access to the variables of its parent function.
Scope
environment in which a certain variables and functions is declared: global
, function
and block
scope.
global
- Outside of any function or block; variables are accessible everywhere
function
- Each function creates a scope in which its variables are accessible - also called local
scope - local life in the functions and are not accessible outside of the function
block
- from ES6 blocks also create a scope. So for example variables created in an if block have there own scope in the if block. Same for for block etc. This applies only to let
and const
variables. var
would be accessible in the function or global scope.
From ES6 all functions are also block
scoped - so only accessible inside that block
Scope of variable
region of code where variable can be accessed
Every scope has access to all variable from all outer scopes. You can see that second()
has access to the variables of the first()
function and the global
scope.
const myName = "Leo";
function first() {
const age = 30;
if(age>=30){
const decade = 3;
var millenial = true;
}
function second(){
const job = "worker";
console.log(`${myName} is a ${age}-old ${job}`)
}
second()
}
first()
So a function looks in itself if it can find the variable, if not it looks up in the next outer scope.
This is called variable lookup in scope chain. One scope
can only look up in the scope chain not down.
Hoisting
Makes some types of variables accessible before they are declared in the code. Behind the scenes - before the execution the code is scanned for variable declarations - for each variable found, a new property is created in the variable environment object.
It doesn't work the same for all variable types.
function
declarations are hoisted and the initial value is the actual function - we can use function declarations before they are actually declared - because they are stored in the variable environment object before the code starts executing.
var
variables also are hoisted but we don't get the declared value we get the initial value undefined
. One of the reason why we almost never use var
let
and const
are not hoisted - they are set to <uninitialized>
no value at all - not even undefined. These variables are placed in the TDZ
temporal dead zone.
function expressions and arrows
depend on if using var
let/const
console.log(addDecl(2,3));// -> Works
console.log(addExpr(2,3));// -> Doesn't Work
console.log(addArrow(2,3));// -> Doesn't Work -> AddArrow function is undefined(2,3)
function addDecl(a,b) {
return a+b;
}
const addExpr = function(a,b) {
return a+b;
}
var addArrow = (a,b) => a+b;
Temporal dead zone
The
const job = 'teacher'
will only be accessible from the line it is defined because
the lines before lies the Temporal Dead Zone of that job
variable - so we can't use it there. The region where the variable is defined but can't be used anyway - as if the variable doesn't even exist.
In the first console.log()
with the job
variable we get an
ReferenceError: Cannot access 'job' before initialization
error.
The TDZ
got invented to catch errors like accessing variables before declaration.
It also allows const
variables to not be reassigned.
Hosting got invented to make it possible to use functions before there actual declarations.
this keyword
this
keyword is special variable that is create for every execution context / every function.
this
keyword will always take the value of the owner of the function in which the this
is used - it points to the owner of the function.
The value of this
is depends on how the function is called - NOT static.
this
is only assigned when the function is called.
Method
- this
-> Points to Object that is calling the method
const jonas = {
name: 'Jonas',
year: 1889,
calcAge:function () {
return 2037 - this.year;
}
}
jonas.calcAge()
Here this.
points to jonas
Global scope
Simple function call
- this
-> undefinied(in strict mode) Otherwise points to window
Arrow functions
- this
-> this
of the function that surrounds the arrow function
const calculate = (a,b) => {
console.log(this)
}
calculate(1,2)
// returns the window because it is the surrounding "function"/scope of the arrow function
Event listener
- this
-> DOM element that the handler is attached to
this
doesn't point to the function itself nor the variable environment
Never user an arrow function as a method in an object
//Dont do this!!!!
const hans = {
...
greet: ()=> {
}
}
// Use a normal function
const hans = {
...
greet: function() {
concole.log(this)
}
}
Arguments Keyword
Regular Javascript functions have an arguments keyword which store all the passed arguments. It is also possible to pass more arguments than specified into a function. But you can't use it in arrow functions.
const addExpr = function(a,b) {
console.log(arguments);
}
addExpr(2,5,40,45)
//-> agrumentsArray[2,5,40,45]
Memory Management: Primitives and Objects
Memory management is how JS allocates space in memory to create variables and later frees up the memory. Every value in the program goes through a memory lifecycle.
In the lifecycle: - When a variable gets created a peace of memory gets allocated - Then whenever the variable or its value is used the memory gets used. - When the variable is not used anymore the memory is released.
For different types of values memory is allocated in different places in the JS Engine.
Values are ether primitive types or objects in JS.
Objects get stored in the Heap
of the JS Engine and primitive values get stored in Call Stack
.
Object Variables in the call stack don't hold the objects themselves but references to the objects in the heap
References to objects are also stored in the Call Stack
When we copy a variable that contains a reference to an object then we only copy the reference to the object.
But we have to be aware that if we change a value in the copied variable it will also change the value in the original object because we overwrite the reference and both elements have the same reference.
So to properly copy on object and not copying the reference we need to use the spread operator ...
shallow
copy. It copies the primitives
but if the object would contain another object/ a object reference it wouldn't really make a unique copy of the object reference.
To create a Deep copy/clone for example when we want to manipulate a big object in our code and make sure that the original data isn't affected by these changes.
Memory Management: Garbage Collection
Call stack
Variable environment is simply deleted when execution context pops off stack
Heap
In order to clean up old unused objects from the heap JS uses Garbage Collection
. The JS Engine runs Garbage Collection
automatically whenever it thinks it is necessary. The developer can't instruct to do it.
Mark-and-sweep-Alogrithm
1. Mark all objects that are reachable from a root/starting point like global and other execution context as "alive".
If objects can be reached object is considered alive.
2. Sweep - Delete all un-marked objects and reclaim memory for future allocations.
When the getTasks() finishes execution and the execution context pops of the stack, the garbage collection deletes the objects from getTasks() because they aren't reachable any more.
Any globally defined object will never be garbage collected because the Global EC
exists whe whole lifetime of the program.
Memory Leaks When an object that is no longer needed by the program is incorrectly still actively reachable by the garbage collection from one of the roots. So the object is marked as alive and not deleted. Forgetting to throw away stuff that you don't use anymore.
Often old and undesired reference happen from event listener and timers. If a timer creates an object this object will always be reachable unless the developer deletes the timer when not needed anymore. Same for event listeners. Also avoid declare large objects as global objects because they will never be garbage collected