Variables are a container used to store data.
The most used keywords for variables are let and const.
let variables allow you to change the assigned value, whereas const variables cannot be changed later in code.
Example no. 1:
let message = 'Hello!'; // define the variable and assign the value
Example no. 2:
We can also declare multiple variables
let user = 'John';
let age = 25;
let message = 'Ola';
There are two limitations for a variable name in JavaScript:
- The name must contain only letters, digits, symbols
$
and_
. - The first character must not be a digit.
When the name contains multiple words, camelCase is used. Meaning each word starts with a capital letter : myNameIsRazvan
.
When declaring a value that will not be changed later on (constant), we use const
keyword:
const myBirthday = '18.04.1982';
Constants are usually used as aliases for difficult-to-remember values that are known prior to execution.
Such constants are named using capital letters and uderscores, like this:
const COLOR_ORANGE = "#FF7F00";
// ...when we need to pick a color
let color = COLOR_ORANGE;
There are 7 basic data types:
- number
for any kind of numbers
Example:
let n = 94;
n = 19.94;
- string
we will find it in between quotes (single quotes ''
, double quotes ""
) or backticks ``
.
Example:
let name = 'Razvan';
let dog = "Husky";
let longPhrase = `My dog's owner is ${name}`;
- boolean
can only express true
and false
.
Example:
let myIphoneIsXs = true; // yes, I have an Iphone Xs
let myOldIPhone = false; // no, I didn't have any other Iphone
- null
it has a special value of nothing / empty / unknown value.
Example:
let dog = null; // dog is unknown or empty
- undefined
the value of undefined
is 'value not assigned'
Example:
let age;
console.log(age); // output will be *undefined*
- objects
object
is a special data type used to store collections of data.
- symbol
symbol
is used to create unique identifiers for objects
typeof
returns the type of the argument. It can be used with or without parantheses and the result will be the same.
Example:
typeof 'animal' // output will be "string"
typeof '241.94' // output will be "string"
typeof 95 // output will be "number"
is used to convert a value to a string form
Example:
let name = true;
alert(typeof name); // output will be 'boolean'
name = String(name); // output will be a string 'true'
alert(typeof name); // output will be 'string'
In mathematical functions and expressions, numeric conversion happens automatically
Number(value)
function is used to convert a value. For example:
let word = '88';
alert(word); // output will be a string
let wordToNumber = Number(word);
console.log(wordToNumber); // output will be a number : 88
alert( Number( " 999 " )); // output will be a number : 999
alert( Number("765z")); // output will be : NaN(not a number) because of 'z' letter at the end
alert( Number(true)); // output will be a number : 1
alert( Number(false)); // output will be a number : 0
All mathematical operations convert values to numbers, except +
. If one of the value has a string form, the other values are also converted to string when using +
operator. Example:
alert ('54' + 26); // output will be a string : '5426'
Any value that's 'empty' fe: 0, an empty string, null, undefined, NaN become false
. All other values convert to true
. Example:
alert( Boolean(41)); // output will be true
alert( Boolean("")); // output will be false
Most operators are similar to the ones we used in school, like : +
to add, *
to multiply, -
to subtract, etc.
Operators are applied to operands Example: 9 * 7
-> we have left operator which is 9 and right operandor which is 7.
Operands are also sometimes called 'arguments'.
unary means that an operator has a single operand.
let dry=5
dry = -5;
console.log(dry); // output will be -5 and the operator is unary
binary means that an operator has 2 operands.
let a = 5, b = 8;
console.log(a-b); // output will be -3 and the operator is binary
If +
is applied to strings, it concatenates (merges) them. Example:
let razvan = 'my' + ' name';
console.log(razvan); // my name
Note: If either operand is a string, then the output will be a string too and the operations run from left to right. Example:
alert(4+2+'6'); // '66' and not '426'
Unary + or +
operator when applied to an operand that is not a number, it converts it into one. Example:
let great = 2;
alert(+great); // 2 . Nothing happens because the operand is a number
let amazing = true;
alert(+amazing); // 1 . It has changed it into a number
It acts the same as Number(..)
, but its shorter. Example:
let beers = 6;
let wine = 2;
alert(+beers + +wine); // 8
alert(Number(beers) + Number(wine)); // 8 - same output, longer variant.
Just like in school where multiplication is calculated first in this example 2 + 5 * 9
, the higher precedence acts the same in JavaScript.
Parantheses override any precedence and we can learn more about order in here
An assignment =
is also an operator. It is possible to do simple calculations and even chain assignments. Example:
let i = 8 * 8 + 9;
alert(i); // 73 - simple calculation that returns a value stored in 'i'
let i,f,g;
i = f = g = 8 * 8;
alert(i); // 64
alert(g); // 64
The results of b % c
is the remainder of the integer division of b
by c
. Example:
alert (8 % 3); // 2 is the remainder of 8 divided by 3
The exponentiation operator **
works for both integer and non-integer numbers. b ** c
is b
multiplied by itself c
times. Example:
alert ( 5 ** 5 ); // 3125
alert ( 2 ** 3 ); // 8
alert ( 9 ** (1/2) ); // 3 (power of 1/2 is the same as a square root from maths)
++
increment increases a variable by 1:
let measure = 5;
measure++;
alert(measure); // 6
--
decrement decreases a variable by 1:
let measure = 5;
measure--;
alert(measure); // 4
Increment and decrement can only be applied to a variable!
These ++
and --
operators can be placed both after and before the variable. When used before the variable its called "prefix form" and if used after the variable its called "postfix form".
- If the result of increment/decrement is not used, then both forms gives the same results;
- If the result of increment/decrement is used now, we need the prefix form;
- If we need to use the old result, then we need the postfix form.
Examples:
let distance = 6;
let newDistance = ++distance;
alert(newDistance); // 7 - it returns the new value
let distance = 6
let newDistance = distance++;
alert(newDistance); // 6 - it returns the old value
It is used to store the new result of an operator applied to a variable. Example:
let f = 5;
f = f + 6; // result is 11 ( 5 + 6)
f = f * 2; // result is 22 ( 11 * 2)
We can write the same lines of code using +=
and *=
operators:
let f = 5;
f += 6; // 11 ( same as above where f = f + 6 )
f *= 2; // 22 ( same as above where f = f * 2 )
Some comparisons signs are similar to those we have in maths, like:
- Greater/ less than :
a > b
anda < b
; - Greater/ less than or equals :
a >= b
anda <= b
; - Equals :
a == b
// notice the difference between = and double == ; - Not equals :
a != b
A comparison returns a value, in this case the returned value is a boolean. Example:
alert ( 2 > 1 ); // true
alert ( 2 > 1 ); // true
alert(dog); // true because 5 is does not equal 4
Strings are compared letter by letter in JavaScript according to Unicode order. Example:
alert ( 'Razvan' > 'Andrei' ); // true because R > A
Values are converted to numbers when different types are compared. For boolean true
becomes 1
and false
becomes 0
. Example:
alert ( true == 1 ); // true because 1 = 1
<- boolean compared with number;
alert ( 5 == '5' ); // true because string is converted to number
<- string compared with number;
A strict equality check ===
compares values without any type conversion. It is used because equality check ==
cannot differentiate 0
from false
or an empty string ''
from false
If we use ==
for null
and undefined
, the result will be true
and they don't equal anything else when checked for equality!
alert(null==undefined)
This is only true when we check for equality.
When checked for other comparisons, null
becomes 0
and undefined
becomes NaN
When alert
is called, a mini-window appears on the screen, which is called modal window. This will block the user to interact with the rest of the page until this window is dealt with.
alert('My name is Razvan');
prompt
accepts two arguments and it gives the user the possibility to input something. In this case, the modal window will have a text message that is seen by the user and a default parameter which is the initial value for the imput field and is optional. Example:
let dog = prompt('How many dogs do you have?', 1); // string being the displayed text and 1 the initial value
alert(`You have ${dog} dog/s`);
confirm
displays a modal window with a question and two buttons : OK and Cancel. It returns true
if OK is pressed and false
otherwise. Example:
let question = confirm('Is the computer you are typing from yours?');
alert(question); // true if OK is pressed, false otherwise.
We use if
and ?
when we need to execute some actions based on some conditions.
When condition is true
inside an if
statement, the engine will execute the block of code inside the {}
. For example:
let myName = 'Razvan';
if (myName !== 'Razvan') {
return 'Nah, try again, please.';
}
The if
statement evaluates the expression in ith parentheses and converts the result to a boolean.
else
block is an optional keyword for if
statements. It is only executed when the condition is false
. For example:
let iphone = prompt('What is the name of the company that produces iphone smartphones?');
if (iphone == 'Apple') {
return 'Well done!';
} else {
return 'That is not quite right.'
}
}
In case we want to test more than one condition, else if
is used and it allows us to do just that.
Ternary operator ?
lets us assign a variable depending on a condition. It is also the only operator in JavaScript that has three operands.
Example:
let question = condition ? value1 : value; // this is how the syntax looks
let accessAllowed;
let location = prompt('Are you from Europe?', '');
if (location == 'yes') {
accessAllowed = true;
} else {
accessAllowed = false;
}
alert(accessAllowed);
A sequence of question mark operators ?
can return a value that depends on more than one condition. Example:
let location = prompt('Where are you from?', 'Europe')
let message = (location == 'Europe') ? 'Welcome' :
(location == 'US') ? 'Wazzap, Mr. Trump?' :
(location == 'Asia') ? 'I hope I can learn chineese some day' :
'Never been there before.';
alert(message);
In JavaScript, there are three logical operators ||
(OR), &&
(AND), !
(NOT). They can be applied to values of any type!
The 'OR' operator is represented by two vertical line symbols ||
. It's job is to look for any true
value and it will return true
when it finds it, otherwise will return false
.
let a = 1;
let b = 0;
alert (a || b); // will return 1 becasue is true and 0 is false;
The OR operator is converting everything to boolean, then is looking for the first true
value and returns it. If no such value is found, it returns the last value. Example:
let name = undefined;
let nob = null;
let age = 0;
alert (undefined || null || 0); // will return 0 because none of these values are true when converted to boolean.
The AND operator is represented by two ampersands &&
. This operator will return true
if all operands are truthy and false
otherwise. When a falsy value is found, it will return the first value, no matter how many falsy values we have.
The AND operator evaluates operands from left to right ( like OR), then converts all values to boolean and compare them (like OR) and returns the original value of the first falsy value when found. If all values are truthy, it returns the original value of the last operand. Examples:
let hour = 15;
let minute = 35;
if (hour == 15 && minute == 35) {
alert( 'The time is 15:35' ); // - Will return this string because both values are truthy
}
alert (1 && 0 && null && undefined); // will return 0 because that is the first falsy value.
The NOT operator is represented by exclamation sign !
. It accepts a single argument and converts the operand to boolean and returns the inversed value. Example:
alert ( !1 ); // will return false because 1 is true, and the inversed value of true is false
Loops are a way to repeat the same code multiple times.
While the condition (what is inside the parantheses) is true
, the code that is inside the body loop is executed. Example :
let i = 5;
while (i);
alert (i); // code will show 4,3,2,1 then it stops because 0 is falsy and so the condition is not met anymore;
i--;
The above code can be written like this too:
let i = 5;
while (i < 5) {
alert (i);
i--;
}
i++
or i--
is used so that the loop will know when to stop, otherwise we will have an infinite loop and our code won't work properly.
When using this loop, the code will execute first time without checking any conditions, only after running once will check the condition and if true
will run again, else will stop. Example :
let i = 5;
do {
alert(i);
i--;
} while (i > 2); // code will show 5,4 and 3 because 2 = 2 and condition becomes falsy.
This condition is used when we want to execute the code once regardless of the condition. But, the preferred loop is while(...) {...}
.
This is the most used loop. This loop contains (begin; condition; step)
and then it has the code inside the body.
This is how it works:
- Run begin;
- if condition is true -> run body and run step;
- if condition is true -> run body and run step;
- and so on until condition becomes falsy.
Any part of the for
loop can be skipped. If we delete all parts, then we will have an infinite loop :
for (;;){
// repeats without limits
}
Example of for loop:
for (let i = 0; i < 5; i++) {
alert(i); // code will show 0,1,2,3,4 and stops when it gets to 5 because condition becomes falsy
}
The condition becoming falsy is NOT the only way to stop a loop. Using the special break
directive will achieve the same result. Example:
for (let i = 0; i < 5; i++) {
if ( i == 3) break;
alert(i);
}
alert(" This string will show when break is activated. ");
When break
is activated, the engine will execute the next line of code after the loop. See above.
Another special directive is continue
which is a ligher version of break
. When continue
is activated, the loop won't be stoped all together, but instead, it just stops the current iteration and forces the loop to start a new one if condition is truthy. Example:
for (let i = 0; i < 14; i++) {
if (i % 2 !== 0) continue;
alert(i); // will show 0,2,4,6,8,10,12
}
A switch
statement can be a replacement for multiple "if" checks. It contains one or more case
blocks and an optional default
.
Example:
let a = 2 + 8;
switch (a) {
case 1:
alert ('Way to small');
break;
case 5:
alert ('You are getting closer but still small');
break;
case 9:
alert ('Really hot!');
break;
case 10:
alert ('You got it!');
break;
default:
alert ('What value is that?!');
}
Let's examine how the switch
statement works:
- a = 10 as described in our global variable above.
- Now , the switch starts to replace
a
value in every case and it compares the values. - It runs every case until a match is found.
- When the match is found, it executes the line of code for that case.
- If there is a
break;
statement, then it stops. - If there is no
break;
statement after the match, it keeps executing the next cases without stopping. - In case of no match, then the
default
code is executed.
Very important :
- Any expression can be a
switch/ case
argument!!! - The equality check is always strict! Values must be the same type to match.
Functions allow users to write code that can be used in many places of the script. They allow the code to be called many times without repetition. Some built-in functions include alert(message)
, prompt(message, default)
and confirm(question)
. Let's see how we can create functions of our own.
Example of a simple function :
function hiThere() {
alert('Hello and welcome! I hope you will enjoy your time with us!');
}
hithere();
hithere();
hithere();
Now let's explain the function above and how it works :
- A function starts with the
function
keyword followed by the name, in this case we named our functionhiThere
. - After it's name, in parentheses we can include parameters. Parameters are usually generic, so that we can use the function with multiple values without writing it again, this is the whole purpose of a function in the first place.
- Then, in between the curly brakes
{}
we write the "function body" which is nothing else than the code that the function will execute. - To call a function, we write it's name and the given parameters outsite it's body. Just like above.
Functions can use global variables or have they own local variables. The difference between them is that local variables can only be used inside the function whereas the global ones can be used anywhere in our code.
Example of function with parameters(also called arguments):
function sum(a, b) {
alert (a + b);
}
sum(8, 2); // output will be 10
sum(21, 4); // output will be 25
As we can see in the example above, the arguments a
and b
are given some values and the function executes the code alert (a + b)
.
A function accepts return
directives in any place, multiple times if needed. For example :
function checkAge(age) {
if (age > 18) {
return true;
} else {
return confirm('Do you have permission from your parents?');
}
}
let age = prompt('How old are you?', 18);
if ( checkAge(age) ) {
alert( 'Access granted' );
} else {
alert( 'Sorry, try again when you are 18+' );
}
Some important information about functions:
- When naming a function, you should use a verb, because functions usually represents actions.
- A function name should be very brief and intuitive, such as
get
,calc
,show
,create
etc. - Never add a newline between
return
statement and the value. This is because in JS, it assumes a semicolon afterreturn
and the code will stop there. - A function with an empty
return
or without it, returnsundefined
Example:
function doNothing() {
// empty in here
}
alert (doNothing() === undefined ); // will return true!
function doNothing() {
return;
}
alert (doNothing() === undefined ); // will return true as well
- A function should do exactly what is suggested by its name and no more. One function = One action
Functions have 3 types of syntax in JavaScript. We have just seen function declaration above, now we will dive intro function expression and arrow functions.
This is how the syntax for Function Declaration looks like:
function sayHi() {
alert ('Salut');
}
Now let's see the syntax for Function Expression:
let sayHi = function() {
alert ('Salut');
};
And finally, the syntax for Arrow Function:
let func = (arg1, arg2, ...argN) => expression
Which is equivalent to this, but is much more concise:
let func = function(arg1, arg2, ...argN) {
return expression;
}
- As a rule of thumb, Function Declaration is the preferred syntax used in JavaScript. This is because it looks clean and it's easy to look up to and also we can call such functions before they are declared!
- If a Function Declaration doesn't suit us for some reason, then we can use Function Expressions instead.
- Arrow functions are handy for mostly one-line actions and callbacks.
Function Expressions are created when the execution reaches them.
When a Function Declaration is made within a code block, it is visible everywhere inside that block, but NOT outside of it.
Let's see an example with Function Expression:
let age = prompt("What is your age?", 18);
let welcome;
if (age < 18) {
welcome = function() {
alert("Hello!");
};
} else {
welcome = function() {
alert("Greetings!");
};
}
welcome();
In the example above, the code runs as expected because Function Expressions is stored in a variable that is outside of if
and has global visibility.
This type of functions are handy for one-liners (actions and callbacks) and they come in two types:
- Without curly braces
(...args) => expression
- the right side is an expression. The function evaluates it and returns the result. - With curly braces:
(...args) => { body }
- brackets allow us to write multiple statements inside the function, but we need to have areturn
statement to return something.
Example of arrow function:
let sum = (a, b, c) => {
let result = a + b + c;
return result;
}
alert( sum(1, 5, 4) ); // 10
And now an example without curly braces:
let sum = (a, b, c) => a + b + c;
alert( sum(1, 5, 4) ); // 10
Statements are delimited with a semicolon:
alert('Hi, I am Razvan'); alert('This is a new statement');
Semicolons are not required after code blocks {}
and syntax constructs with them like loops:
function doNothing() {
} // no semicolon needed
for (;;) {
} // no semicolon needed
They can be declared using three keywords and they can store any value:
let
- you can change it's stored value later in the code;const
- constant, cannot be changed;var
- old-style.
There are 7 data types:
number
for both floating-point and integer numbers;string
for strings;boolean
for logical valuestrue/false
;null
a type with a single valuenull
, meaning empty or does not exist;undefined
a type with a single valueundefined
, meaning not assigned;object
andsymbol
- for complex data structures and unique identifiers.type of
- returns the type for a value.
prompt(question[, default])
- it is used to ask a question and the user input ornull
if cancel is pressed;confirm(question)
- it is used to ask a question and suggest to choose between OK and Cancel. Answer is returned astrue/false
;alert(message)
- it is used to display a message.
- Arithmetical :
+, -, *, /, % and **
. The binary+
concatenates strings as well. - Assignments :
a = b
ora *= 2
which is equal toa = a * 2
. - Ternary has three parameters:
cond ? resultA : resultB
- If cond is truthy, resultA is displayed, otherwise resultB. - Logical operators : OR
||
, AND&&
, NOT!
. - Comparisons: We have to types of equality checks for comparisons,
==
converts the values to numbers and compares them and===
compares the type and also the value without conversion.
There are 3 type of loops studied so far:
-
while (condition) { ... body }
-
do { ... body } while (condition);
-
for (let i=0; i < 4; i++) { ... body }
Switch is used to replace multiple if
checks and it uses strict equality checks ===
for comparisons. Example :
let age = prompt('How old are you?', 15);
switch (age) {
case 15:
return("Won't work"); // result of prompt is a string, not a number
case '15':
return("Not it works!");
break;
default:
alert("All other values");
}
We have learned 3 types of functions so far:
- Function Declaration:
function multiply(a, b) {
return a * b;
}
- Function Expression:
let multiply = function(a, b) {
return a * b;
}
- Arrow function:
let multiply = (a, b) => a * b;
A polyfill is a browser fallback that allows functionality you expect to work in modern browsers to work in older browsers too.
Babel is a transplier, it rewrites modern JavaScript code into the previous standard so that even the old versions on browsers can read it.
There are two parts in Babel:
- The transplier program, which rewrites the code.
- The polyfill. For new functions we need to write a special script that implements them. This is where polyfills comes into play, they 'fill in' the gap and add missing implementations.
In JavaScript, objects are used to store keyed collections of various data. Objects can be created with figure brackets {}
or with new Object();
statement. For example:
1. let user = {};
2. let user = new Object();
Objects can have an optional list of properties. A property is a key with a value pair, where they key
can only by a string but value
can be anything.
Example:
let user = {
name: 'Razvan', // the key is name and value is Razvan
age: 24, // the key is age and value is 24
sex: 'male',
};
We can add key and values to objects like so : user.origin = 'Romania';
To add a multiword property we have to use quotes. for example : "is short": true;
Dot notation doesn't work for multiword properties. Instead we can use squre bracket notation : user["is short"] = true;
We can remove key and value like this : delete user.name;
We can call a property by using object name dot key, for example : alert(user.age);
To walk over all keys of an object we use the following syntax:
for(key in object) {
// executes the body for each key among object properties
}
One of the fundamental differences of objects vs primitives is that they are stored and copied "by reference".
Primitive values are assigned/copied "as a whole value" wheres objects are stored somewhere in the memory and variables have a "reference" to them.
All we do in JavaScript takes memory, but memory management is performed automatically and invisibly to us.
Reachability is the main concept of memory management in JavaScript. When a value is not reachable anymore (cannot be accessed or used), it is removed by a background process called Garbage collector.
Let's have a look at an example to see how an object becomes unreachable:
let user = {
name: "Razvan"
}; // user has a reference to the object
user = null; // this modifies the object and now " Razvan " cannot be accessed anymore
In the example above, there is no way we can access " Razvan " anymore so the Garbage collector will delete it and free some memory!
So far we have learned that object property keys can only be string type. Now we will learn that it can also be symbol type.
Symbol value represents a unique identifier that can be created using Symbol()
syntax:
let id = Symbol();
Symbols can also be given a description, also known as "symbol name". We have to remember tho that even if two or more symbols have the same description, they are different values, for example:
let id1 = Symbol("mat");
let id2 = Symbol("mat");
alert(id1 == id2); // will returne false
Symbols don't auto-convert to string!!! To call a symbol, we have to first use the .toString()
method on it, then it will work.
The purpose of a Symbol is to create "hidden" properties of an object.
This comes in handy when we import a library for example and don't want to mess up the code. If we were to use a string value instead of a Symbol, when keys share the same name, they can be overwritten! For example:
let user = { name : "Razvan" };
user.id = "Value inside";
// .. now if someone wants to use "id" for their purpose, this will happen:
user.id = "New value inside";
// value of user.id has been overwritten !
To use symbol in an object literal, we have to use square brackets, like this:
let age = Symbol("age");
let user = {
name: "Razvan",
[age]: 24
};
Symbols properties doesn't participate in for..in
loop. That's because they are hidden.
There is a way tho access the same property within a symbol, even if we said that every symbol is unique.
To achieve that, there is a global symbol registry where we can create symbols and access them later, in return having the exact same symbol.
Symbol.for(key)
is used to create or read a symbol in the registry. If there is already one created, it returns it, otherwise it creates a new one.
Example:
let id = Symbol.for("id"); // if it doesn't exist, it has just been created
let idAgain = Symbol.for("id"); // reads it again
alert( id === idAgain); // true
Functions that are stored in object properties are called "methods". Example:
let user = {
name: "Razvan",
age: 24
sayHi: function(){
alert("Hi there");
}
}; // sayHi property is a function inside an object and it's called method
To access the object, a method can use the this
keyword which value is the object before dot. For example:
let user = {
name: "Razvan",
age: 24,
sayHi() {
alert(this.name);
}
};
user.sayHi(); // output will be Razvan
In the example above, during the execution of user.sayHi()
, the value of this
will be user
.
In JavaScript, the value of "this" is evaluated during the run-time and it can be anything and used in any function. We can even call the function without an object at all, but the return will be undefined
:
function sayHi() {
alert(this);
}
sayHi(); // undefined
When "this" is accessed inside an arrow function, the value it's taken from outside (outer function).
To convert an object to a primitive value, we have to use the ToPrimitive
keyword. There are three variants, also called "hint" to do this :
- "string" - when an operation expects a string, for object-to-string conversions. Example :
alert(obj);
// output
anotherObj[obj] = 33; // using object as a property key
- "number" - when an operation expects a number, for object-to-number conversions. Example :
let num = Number(obj); // explicit conversion
let n = +obj; // unary plus
let delta = date1 + date2;
let greater = user1 > user 2 // for comparison
- "default" - it rarely happens when operator is " not sure " what type to expect.
Some operands can work with both strings and numbers, like +
, or when comparing something using > / <
. For example:
let sum = date1 + date2; // binary +
if ( user == 1) {...} // obj == string/number/symbol
*To do the conversion, JavaScript tries to find and call three object methods:
- Call
obj[Symbol.toPrimitive](hint)
if method exists; - Otherwise if hint is a
string
- tryobj.toString()
andobj.valueOf()
whatever exists; - Or if hint is
number
ordefault
- tryobj.valueOf()
andobj.toString()
whatever exists.
Is a built-in symbol that shoud be used to name the conversion method:
obj[Symbol.toPrimitive] = function(hint) {
// hint can be "string", "number" or "default" and the return will be a primitive value
}
let user = {
name: "Razvan",
age: 24,
[Symbol.toPrimitive](hint) {
alert(`hint: ${hint}`);
return hint == "string" ? `{name: "${this.name}"}` : this.age;
}
};
//lets see how the conversion works :
alert(user); // hint: string -> {name: "Razvan"};
alert(+user); // hint: number -> 24;
alert(user + 500); // hint: default -> 524;
If there is no Symbol.toPrimitive
then JS tries to find them in this order:
toString -> valueOf
for "string" hint;valueOf -> toString
otherwise.
Example:
let user = {
name: "Razvan",
age: 24,
// for hint = "string"
toString(){
return this.name;
},
// for hint = "number" or "default"
valueOf() {
return this.age;
}
};
alert(user); // toString -> "Razvan"
alert(+user); // valueOf -> 24
alert(user + 500); // valueOf -> 524
Constructor functions and "new" operator are used when we need to create many similar objects.
Constructor functions are real functions that start with capital letter first and should only be executed with "new" operator. Example:
function User(name) {
this.name = name;
this.isAdmin = false;
}
let user = new User("Razvan");
alert(user.name); // Jack
alert(user.isAdmin); false
Now let user = new User("Razvan")
from above, does the following things:
- Creates an empty object that is assigned to
this
. - Executed function body; usually modified
this
and adds new properties to it. - The value of
this
is returned.
So, let user = new User("Razvan")
becomes :
let user = {
name: "Razvan",
isAdmin: false
};
The main purpose of constructors is to implement reusable oject creation code.
Usually they don't have a return
statement. Their job is to write all necessary data into this
that automatically becomes the result.
If there is a return
statement, then:
- If
return
is called with object, then it is returned insted ofthis
. Example:
function NewUser() {
this.name = "Razvan";
return {name: "Ionut"}; // <-- returns an object
}
alert(new NewUser().name); // <-- returns "Ionut"
- If
return
is called with a primitive, it's ignored. Example:
function NewUser() {
this.name = "Razvan";
return 8; // finishes the execution, return this
// ...
}
alert(new NewUser().name); // <-- returns "Razvan"
We can add to this
methods as well, not only properties. Example:
function User(name) {
this.name = name;
this.sayHello = function() {
alert("How are you, " + this.name + "?");
}; // sayHello is the method in this case
}
let michael = new User("Michael");
michael.sayHello(); // How are you, Michael?
In JavaScript it is possible to work with primitives as if there were objects, meaning that we can apply methods on them, even if they are not objects.
Primitives are : string
, number
, boolean
, symbol
, null
and undefined
.
An object is created with {}
and can store multiple values as properties, such as functions. Example:
let user = {
name: "Razvan",
sayHello: function() {
alert("Hi friend!");
}
};
user.sayHello(); // Hi friend!
We can apply methods on primitives for a short period of time. When this happens, the primitive in transformed into an "object wrapper" that provides the extra functionality, after which is destroyed. For example:
let str = "Time to become an object wrapper";
alert(str.toUpperCase()); // TIME TO BECOME AN OBJECT WRAPPER
After the method is applied on the primitive, and it is run by the engine, it gets destroyed, meaning that it will go back to it's original form, a primitive.
Null and undefined are exception to the rule, they can only be primitive.
To reduce the number of mistakes in JavaScript, we avoid writing large numbers such as let billion = 1000000000
. Instead, we shorten the number by appending the letter "e"
to the number and specifying the zeroes count: let billion = 1e9
.
In other words, "e"
multiplies the number by 1 with the given zeroes count.
This works for negative numbers as well, for example 1 microsecond (one millionth of a second) would be 0.000001
or 1e-6
, it's exactly the same.
Hexadecimal numbers are widely used in JavaScript to represent colors,encode characters and many others. For example :
alert(0xff); // 255
alert(0xFF); // 255, case doesn't matter
Binary and octal numeral systems are rarely used, but still supported using 0b
and 0o
prefixes:
let a = 0b11111111; // binary form of 255
let b = 0o377; // octal form of 255
alert( a == b ); // true, the same number 255 at both sides
This method returns a string representation of num
in the numeral system with the given base
. Example :
let num = 255;
alert(num.toString(16)); // ff - hexadecimal base
alert(num.toString(2)); // 11111111 - binary base
- base=16 is used for hex colors, etc, digits can be
0...9
orA...F
- base=2 is mostly used for debugging bitwise operations, digits can be
0
or1
- base=36 is the maximum, digits can be
0...9
orA...Z
. Used to shorten url for example.
We can call a method directly on a number using ..
two dots after it. For example : 12345556..toString(36)
For this we have some built-in functions, like :
Math.floor
- rounds down :3.4
becomes3
and-1.3
becomes-2
Math.ceil
-rounds up :3.4
becomes4
and-1.3
becomes-1
Math.round
- rounds to nearest integer3.4
becomes3
,-1.3
becomes-1
.Math.trunc
- removes everything after decimal point without rounding.3.4
becomes3
and-1.3
becomes-1
These two methods "read" a number from a string until they can't. This is why they are better then "+"
or Number()
which are only doing numeric conversions.
parseInt
returns a number/ an integer, example:
alert( parseInt("58inch") ); // 58
alert( parseInt('1.7m') ); // 1, only the integer is returned
parseFloat
returns a floating-point number, example:
alert( parseFloat('1.7m') ); // 1.7
alert( parseFloat('13.5.8') ); // 13.5, the second point stops the reading
Math.random()
- returns a random number from 0 to 1, not including 1. Example:
alert( Math.random() ); // 0.32123123123
alert( Math.random() ); // 0.75231234323
Math.max(a, b, c...)
/Math.min(a, b, c...)
- returns the greatest/smallest from the number of arguments. Example:
alert( Math.max(5, 3, 124, 932, -65) ); // 932
alert( Math.min(4, 8, 15) ); // 4
Math.pow(n, power)
- returnsn
raised to the given power. Example:
alert( Math.pow(2, 3) ); // 8
Strings can be represented in three ways:
- Single quotes
' '
. Example :
let str = 'Razvan';
- Double quotes
" "
. Example :
let str2 = "Razvan";
- Backticks
let str3 = `Razvan`;
If we want quote something inside a string, we have to use different type of quotes than the ones used to represent the string itself.
Example:
let quote = "Iphone is made by 'Apple'"; // different type of quotes used and code will run as expected.
Backticks allow us to display strings on multiple lines. They also allow us to embed any expression or function calls into the string Example:
function multiply (a, b) {
return a * b;
}
alert (`${multiply(5, 5)} = 5 * 5`)
Length and index are two properties that can be very handy in most cases.
- Length tells us the length of the string. For example:
let str = "Razvan";
alert(str.length); // 6
- Index is used to get a character from a specific position. We use "[]" for that. It's important to remember that the count always starts from 0, so the first character will always be called with
[0]
Example:
let str = "Razvan";
alert(str[1]); // a
Strings have two useful methods for changing the case. These are:
.toLowerCase()
- to change the case into lower case..toUpperCase()
- to change the case into upper case.
We can also change only one character's case if we want so using the index method. Example:
let str = "Razvan";
alert(str[0].toLowerCase()); // R becomes r
str.indexOf(substr, pos)
method looks for a substr
starting from the given pos
and returns the position where the match was found, otherwise -1
. Example :
let sentence = "My name is Razvan";
alert(sentence.indexOf("is")); // 8
str.lastIndexOf(substr, pos)
method searches from the end to the beginning. Example:
let sentence = "My name is Razvan";
alert(sentence.lastIndexOf("a")); // 15
str.includes(substr, pos)
returns true/false
depending if the given substr
is included or not. pos
is optional. Example:
let sentence = "My dog is young";
alert(sentence.includes("dog")); /true
strstartsWith
and str.endsWith
do exactly what their names says:
alert("Razvan".startsWith("Raz")); // true
alert("Razvan".endsWith("van")); // true
str.slice(start [, end])
- returns the part of the string fromstart
toend
(not including end). If we omit theend
argument, the method will go until the end of the string. If we use negative values, it means the position is counted from the end of the string. Examples:
let str = "unbelievable";
alert(str.slice(2, 4)); // "be"
alert(str.slice(2)); // "believable"
alert(str.slice(-2, -4)); // "ab"
str.substring(start [, end])
- returns the part betweenstart
andend
. This method doesn't accept negative arguments.start
can be greater thanend
. Example:
let str = "unbelievable";
alert(str.substring(2, 5)); // bel
alert(str.substring(5, 2)); // bel
str.substr(start [, length])
- returns the part fromstart
, with the givenlength
. First argument can have negative value.
let str = "unbelievable";
alert(str.substr(2, 5)); // belie, from pos 2 get 5 characters.
Strings are compared character-by-character in alpabetical order, but:
- a lowercase letter is always greater than the uppercase;
- letters with diacritical marks are "out of order".
In JavaScript, all strings are encoded using UTF-16, meaning that each character has a corresponding numeric code. There are some methods that allow us to get the character for the code and back. Example:
str.codePointAt(pos)
-returns the code for the character atpos
:
alert("Q".codePointAt(0)); // 81
alert("q".codePointAt(0)); // 113
String.fromCodePoint(code)
- creates a character by its numericcode
:
alert(String.fromCodePoint(81)); // Q
alert(String.fromCodePoint(113)) // q
Other string methods
str.repeat(n)
- repeats a string for n
times.
str.trim()
- removes spaces from the beginning and end of a string.
Arrays are lists that allow us to store ordered collections of data of any type.
Declaration:
let arr = new Array(); // or
let arr = []; // this is used 99% of the time
Same as strings, arrays have two specific properties : length
and index
.
Length property is the array length, meaning the last numeric index plus one. If the length is shorten manually, the array gets truncated.
pop
- extracts the last element of the array and returns it:
let arr = ["1", "2", "3", "9"];
alert(arr.pop()); // 9
push
- adds elements at the end of the array:
let arr = ["2", "5"];
arr.push("Raz");
alert(arr); // 2, 5, Raz
shift
- extracts the first element and returns it:
let arr = ["Raz", "van"];
alert(arr.shift()); // Raz
unshift
- add elements to the beginning of the array:
let arr = ["van"];
arr.unshift("Raz");
alert(arr); // Raz, van
for
loop is on of the oldest and most used loops:
let arr = [0, 5, 8, 200];
for (let i=0; i<arr.length; i++) {
alert(arr[i]);
}
for..of
loop doesn't give acces to the number of the current element, just its value but it is shorter.
let arr = [4, 9, 12, 43, 55];
// iterates over array elements
for (let element of arr) {
alert(element);
}
arr.push(...items)
-adds items to the array;arr.pop()
-extracts an item from the end;arr.shift()
-extracts an item from the beginning;arr.unshift(...items)
-adds items to the beginning;arr.splice(index[, deleteCount, elem1, ..., elemN])
- adds, removes and inserts elements. Returns the array of removed elements.
index
- starting position,
deleteCount
- number of elements to be deleted,
elem, ..., elemN
- items to be inserted in place of deleted ones.
Example :
let arr = ['2', '5', 'John', 'Winston'];
// start at index 0 and remove the first 2 elements. Then, replace them with the new elements
arr.splice(0, 2, 'My', 'name', 'is');
alert(arr); // output will be : [My,name,is,John,Winston]
Negative indexes are allowed. They specify the position starting from the end with -1 being the before last.
arr.slice(start, end)
- Similar to splice method but shorted and makes subarrays instead of substrings. Can also accept negative indexes. Example:
let arr = ['w', 'o', 'r', 'd'];
alert(arr.slice(1,3)); // output : o,r -Because end is not included
arr.concat(arg1, arg2, ...argN)
-joins the array with other arrays/items. Example:
let arr = ['w', 'o', 'r'];
alert(arr.concat('d','s')); // output : w,o,r,d,s
arr.forEach(function(item, index, array))
- allows you to run a function for every item of the array. Example:
let arr = ['w', 'o', 'r', 'd', 's'];
arr.forEach(function(element) {
alert(element);
}); // 'w', 'o', 'r', 'd', 's'
arr.indexOf(item, from)
- searches foritem
starting from indexfrom
and returns the index where found, otherwise-1
;arr.lastIndexOf(item, from)
- same as above, but searches from right to left;arr.includes(item, from)
- same asindexOf
but returnstrue
if found.
All these 3 methods uses strict comparison ===
when searching!!!
arr.find(function(item, index, array))
- useful when searching in an array of objects for a specific condition. It returnstrue
if found and stops the search, otherwise returnsundefined
.
item
is the element;index
is the index(position);array
is the array itself.
Example:
let users = [
{id: 1, name: "Johnny"},
{id: 2, name: "Petrescu"},
{id: 3, name: "Sullivan"}
];
let FindUser = users.find(item => item.id == 2);
alert(user.name); // Petrescu
-
arr.findIndex(function(item, index, array))
- same asarr.find
but returns the index where the element was found or-1
if not found. -
arr.filter(function(item, index, array))
- continues to iterate for all elements even iftrue
is already returned. Example:
let users = [
{id: 1, name: "Johnny"},
{id: 2, name: "Petrescu"},
{id: 3, name: "Sullivan"}
];
// returns the array of the first two users
let smallUsers = users.filter(item => item.id < 3);
alert(smallUsers.length); // 2
arr.find(function(item, index, array))
- same asfilter
but stops whentrue
is found.
arr.map(function(item, index, array))
- calls the function for each element of the array and returns the array of results. Example:
let lengths = ["Razvan", "John", "David", "Ion"].map(item => item.length);
alert(lengths); // 6,4,5,3
arr.sort()
- sorts the array in place. Items are sorted as strings by default. To use our own sorting order, we need a function of two arguments as the agument ofarr.sort()
. Example:
function ascendingOrder(a,b) {
if (a > b) return 1;
if (a == b) return 0;
if (a < b) return -1;
}
let arr = [2,1,5,99,-33,-52,22];
arr.sort(ascendingOrder);
alert(arr); // -52,-33,1,2,5,22,99
arr.reverse()
- reverses the order of elements in array and returns the reversed array. Example:
let arr = [1,5,7,9,22];
arr.reverse();
alert(arr); //22,9,7,5,1
str.split(separator, delim)
- splits a string into an array by the given delimiterdelim
which is optional. Example:
let arr = 'John, Bob, Robert, Castravete, Ceausescu'.split(', ', 3);
alert(arr); // John, Bob, Robert
Split into letters is achieved by using an empty ''
as argument. Example:
let name = "Razvan";
alert(name.split('')); // R,a,z,v,a,n
arr.join(separator)
- creates a string ofarr
items glued by selectedseparator
. Example:
let arr = ["John", "David", "Pascal"];
alert(arr.join(';')); // John;David;Pascal
arr.reduce(function(previousValue, item, index, array)), initial)
- used to calculate a single value based on the array.
item
is the current arrat item;index
is the position;array
is the array;previousValue
is the result of the previous function call,initial
for the first call;initial
is the initial value and it can be skipped.
Example:
let arr = [2,5,6,4];
let total = arr.reduce((sum, current) => sum + current, 0);
alert(total); // 17
arr.reduceRight
does the same thing asarr.reduce
but goes from right to left.
Arrays are based on objects and so if we use typeof
for them, we will get 'object' as a result. Example:
alert(typeof []); // object
This is why we use Array.isArray(value)
to check if type of an item is array or not. Example:
alert(Array.isArray([])); //true
Most array methods that call functions - like find
, filter
, map
- except sort
, accept an optional additional parameter called thisArg
.
Let's see the syntax for these methods:
arr.find(func, thisArg);
arr.filter(func, thisArg);
arr.map(func, thisArg)
Example where thisArg
comes in handy:
let user = {
age: 24,
younger(otherUser) {
return otherUser.age < this.age;
}
};
let users = [
{age: 12},
{age: 25},
{age: 21},
{age: 55},
{age: 9}
];
// find all users younger than user
let youngerUsers = users.filter(user.younger, user);
alert(youngerUsers.length); // 3
In the example above, user
is basically thisArg
which gives the function context. Without it, the user.younger
would not work as a standalone function, because user.younger
is a method inside an object and it would assume this = undefined
giving an error.
Set is a collection of unique values. Its main methods are:
new Set(iterable)
- creates the set, optionally from an array of values;set.add(value)
- adds a value, returns the set itself;set.delete(value)
- deletes a value, returnstrue
ifvalue
existed at the moment of the call, orfalse
otherwise;set.has(value)
- checks if avalue
is included inset
and returnstrue
if so andfalse
otherwise;set.clear()
- removes everything from the set;set.size
- is the elements count.
Example using set to check for unique values:
let set = new Set();
let marry = { name: "Marry" };
let john = { name: "John" };
let rob = { name: "Rob" };
let tom = { name: "Tom" };
// lets see how many times each person comes to visit us
set.add(john);
set.add(marry);
set.add(rob);
set.add(john);
set.add(tom);
set.add(rob);
set.add(marry);
// lets see the size of our set. Remember, set only stores unique values
console.log(set.size); // 4
// sets allow us to iterate over them
for (let user of set ) {
console.log(user.name); // John, Marry, Rob, Tom
}
We can use for..of
or forEach
to iterate over sets. Example:
let set = new Set(["milk", "cow", "banana", "lamb"]);
for (let value of set) {
console.log(value); // milk, cow, banana, lamb
}
// same with forEach
set.forEach((value, valueAgain, set) => {
console.log(value); // milk, cow, banana, lamb
});
Map is a collection of keyed data items, just like Object
, but Map
can store keys of any type. The main methods are:
new Map()
- creates the map;map.set(key, value)
- stores the value by the key;map.get(key)
- returns the value by the key,undefined
ifkey
doesn't exist;map.has(key)
- returnstrue
if thekey
exists,false
otherwise;map.delete(key)
- removes the value by thekey
;map.clear()
- removes everything from theMap
;map.size
- returns the number of elements inside theMap
.
Example:
let map = new Map();
map.set('1', 'str1'); // a string key;
map.set(1, 'num1'); // a numeric key;
map.set(true, 'bool1') // a boolean key;
// Unlike objects, keys are not converted to strings!
console.log(map.get(1)) // 'num1'
console.log(map.get('1')) // 'str1'
console.log(map.size) // 3
Map can use objects as keys!
let rob = {name: "Rob"};
let map = new Map();
// rob is the key for the map
map.set(rob, 10);
console.log(map.get(rob)); // 10
Every map.set
call returns the map itself, so we can "chain" the calls like so :
map.set('1', 'str1)
.set(1, 'num1')
.set(true, 'bool1');
For iteration, we have 3 methods:
map.keys()
- returns an iterable for keys;map.values()
- returns an iterable for values;map.entries()
- returns an iterable for entries[key, value]
, it's used by default infor..of
.
Example:
let shoppingList = new Map([
['cucumber', 2],
['tomatoes', 1],
['garlic', 3],
['onion', 1],
['potatoes', 4]
]);
//iterate over keys (products)
for (let products of shoppingList.keys()){
console.log(products); // cucumber, tomatoes, garlic, onion, potatoes
}
//iterate over values (price)
for (let price of shoppingList.values()){
console.log(price); // 2, 1, 3, 1, 4
}
//iterate over [key, value] entries
for (let entry of shoppingList) {
console.log(entry); // ["cucumber", 2],["tomatoes", 1] and so on
}
//Map also has a `forEach` built-in method
shoppingList.forEach( (value, key, map) => {
console.log(`${key}: ${value}`); // "cucumber: 2", "tomatoes: 1" and so on
});