Skip to content

Latest commit

 

History

History
4238 lines (2761 loc) · 57.8 KB

File metadata and controls

4238 lines (2761 loc) · 57.8 KB
Error in user YAML: (<unknown>): did not find expected node content while parsing a flow node at line 2 column 1
--- 

- [[#JavaScript Fundamentals ]]
- [[#JavaScript Functions, Arrays, Objects, and Loops Notes]]

---

JavaScript Fundamentals

This pasted file mainly teaches basic JavaScript fundamentals: variables, data types, operators, strings, if/else, type conversion/coercion, truthy/falsy values, equality operators, logical operators, switch, expressions/statements, and the ternary operator.

Important first point:

/*
Most of your code is inside block comments like this.

That means JavaScript will ignore it completely.

So if you run this file as-is, most sections will NOT execute.

To test a section, remove the surrounding:

/*
  ...
*/

But be careful:
Some sections reuse the same variable names like:

const firstName
const age
const birthYear
const now

If multiple sections are uncommented at the same time, you may get errors like:

Identifier 'firstName' has already been declared

This happens because const and let cannot be declared twice in the same scope.
*/

Linking a JavaScript File

let js = "amazing";
console.log(40 + 8 + 23 - 10);

/*
let creates a variable named js.

js stores the string value:

"amazing"

console.log prints something to the console.

40 + 8 + 23 - 10 is calculated first:

40 + 8 = 48
48 + 23 = 71
71 - 10 = 61

So the console prints:

61
*/

Expected Output

61

Values and Variables

console.log("Jonas");
console.log(23);

let firstName = "Matilda";

console.log(firstName);
console.log(firstName);
console.log(firstName);

/*
"Jonas" is a string value.

23 is a number value.

firstName is a variable.

A variable is like a named box/container that stores a value.

Here:

firstName = "Matilda"

So every time we print firstName, JavaScript looks inside the variable and prints:

Matilda
*/

Expected Output

Jonas
23
Matilda
Matilda
Matilda

Variable Naming Rules and Conventions

let jonas_matilda = "JM";
let $function = 27;

let person = "jonas";
let PI = 3.1415;

let myFirstJob = "Coder";
let myCurrentJob = "Teacher";

let job1 = "programmer";
let job2 = "teacher";

console.log(myFirstJob);

jonas_matilda is valid, but in JavaScript camelCase is more common.

Better style:

let jonasMatilda = "JM";

$function is technically valid because variable names can start with $.

But it is confusing because "function" is a JavaScript keyword. Even though $function works, it is not a good beginner habit.

PI is written in uppercase because it represents a constant-like value.

myFirstJob and myCurrentJob are better names than job1 and job2.

Why?

Because they explain what the values actually mean.

job1 and job2 are vague. myFirstJob and myCurrentJob are clear. */

Main beginner point

let myFirstJob = "Coder";
let job1 = "programmer";

/* Both work.

But myFirstJob is better because the name explains the purpose.

Good variable names make code easier to understand. */


Data Types

let javascriptIsFun = true;
console.log(javascriptIsFun);

console.log(typeof javascriptIsFun);

javascriptIsFun = "YES!";
console.log(typeof javascriptIsFun);

/*
javascriptIsFun first stores true.

true is a boolean value.

A boolean has only two possible values:
true
false

typeof tells us the type of a value.
So:
typeof javascriptIsFun
prints: boolean

Then we assign:
javascriptIsFun = "YES!";
Now the same variable stores a string.
JavaScript allows this because it is dynamically typed.

That means the variable itself does not have a fixed type.
The value has the type.
*/

Expected Output

true
boolean
string

Undefined Variables

let year;
console.log(year);
console.log(typeof year);

year = 1991;
console.log(typeof year);

/*
let year;

This creates the variable, but does not give it a value.

So its value is:
undefined

And its type is also:
undefined

After:
year = 1991;

year now stores a number.

So typeof year becomes:
number

*/

Expected Output

undefined
undefined
number

The Weird typeof null Behavior

console.log(typeof null);

/*
This prints:

object

This is a famous old JavaScript bug.

null means "empty value" or "intentional absence of value".

But typeof null returns "object" because of a historical mistake in JavaScript.

Beginner takeaway:

null is NOT really an object conceptually.

It is an intentional empty value.
*/

Expected Output

object

let, const, and var

let age = 30;
age = 31;

/*
let allows reassignment.

First:

age = 30

Then:

age = 31

This is allowed because let variables can be updated.
*/
const birthYear = 1991;
// birthYear = 1990;
// const job;

/*
const creates a variable that cannot be reassigned.

This is allowed:

const birthYear = 1991;

This is NOT allowed:

birthYear = 1990;

Because const means the binding cannot point to a new value.

Also, this is NOT allowed:
const job;
A const variable must be initialized immediately.
*/
var job = "programmer";
job = "teacher";

/*
var is the older way to declare variables.
It allows reassignment, similar to let.

But var has different scoping rules and can create confusing bugs.

Modern JavaScript usually prefers:

const by default
let when reassignment is needed
avoid var in most new code
*/

Accidentally Creating Variables Without let, const, or var

lastName = "Schmedtmann";
console.log(lastName);

/*
This works in non-strict mode, but it is bad practice.

Why?
Because lastName was not declared using:

let
const
var

JavaScript may create it as a global variable.

That can cause hidden bugs.

Better version:
*/

const lastName = "Schmedtmann";
console.log(lastName);

Main beginner warning

lastName = "Schmedtmann";

/*
Avoid this.

Always declare variables properly:

const lastName = "Schmedtmann";
let age = 30;
*/

Basic Math Operators

const now = 2037;
const ageJonas = now - 1991;
const ageSarah = now - 2018;

console.log(ageJonas, ageSarah);

/*
now stores the current example year: 2037.

ageJonas:

2037 - 1991 = 46

ageSarah:

2037 - 2018 = 19

console.log can print multiple values separated by commas.
*/

Expected Output

46 19
console.log(ageJonas * 2, ageJonas / 10, 2 ** 3);

/*
ageJonas * 2:

46 * 2 = 92

ageJonas / 10:

46 / 10 = 4.6

2 ** 3 means:

2 to the power of 3

2 * 2 * 2 = 8
*/

Expected Output

92 4.6 8

String Concatenation

const firstName = "Jonas";
const lastName = "Schmedtmann";

console.log(firstName + " " + lastName);

/*
+ can add numbers.

But with strings, + joins strings together.

firstName + " " + lastName
becomes:
"Jonas" + " " + "Schmedtmann"

Result:

"Jonas Schmedtmann"
*/

Expected Output

Jonas Schmedtmann

Assignment Operators

let x = 10 + 5; // 15

x += 10; // x = x + 10
x *= 4;  // x = x * 4
x++;     // x = x + 1
x--;     // x = x - 1
x--;     // x = x - 1

console.log(x);

/*
Step-by-step:

x = 10 + 5
x = 15

x += 10
x = 15 + 10
x = 25

x *= 4
x = 25 * 4
x = 100

x++
x = 101

x--
x = 100

x--
x = 99

Final value:

99
*/

Expected Output

99

Comparison Operators

console.log(ageJonas > ageSarah);
console.log(ageSarah >= 18);

const isFullAge = ageSarah >= 18;

/*
ageJonas is 46.
ageSarah is 19.

ageJonas > ageSarah

means:

46 > 19

true

ageSarah >= 18

means:

19 >= 18

true

isFullAge stores the result of the comparison.

So:

isFullAge = true
*/

Expected Output

true
true

Operator Precedence

console.log(now - 1991 > now - 2018);

/*
JavaScript does subtraction first.

now - 1991:

2037 - 1991 = 46

now - 2018:

2037 - 2018 = 19

Then it compares:

46 > 19

true
*/
let x, y;
x = y = 25 - 10 - 5;

console.log(x, y);

/*
Subtraction happens first:

25 - 10 - 5

This is calculated left to right:

25 - 10 = 15
15 - 5 = 10

So:

x = y = 10

Assignment happens right to left.

First:

y = 10

Then:

x = y

So both x and y become 10.
*/

Expected Output

10 10
const averageAge = (ageJonas + ageSarah) / 2;

/*
Parentheses run first.

ageJonas + ageSarah:

46 + 19 = 65

Then:

65 / 2 = 32.5

Without parentheses, division would happen before addition,
which would give a different result.
*/

Coding Challenge #1: BMI Calculation

const massMark = 95;
const heightMark = 1.88;
const massJohn = 85;
const heightJohn = 1.76;

const BMIMark = massMark / heightMark ** 2;
const BMIJohn = massJohn / (heightJohn * heightJohn);
const markHigherBMI = BMIMark > BMIJohn;

console.log(BMIMark, BMIJohn, markHigherBMI);

/*
BMI formula:

BMI = mass / height ** 2

For Mark:

massMark / heightMark ** 2

This means:

95 / 1.88 ** 2

For John:

massJohn / (heightJohn * heightJohn)

This is the same idea:

85 / (1.76 * 1.76)

markHigherBMI stores a boolean:

true  -> Mark's BMI is higher
false -> Mark's BMI is not higher
*/

Expected Output for Test Data 2

26.87867813490267 27.44059917355372 false

Beginner note

const BMIMark = massMark / heightMark ** 2;

/*
The ** operator has high precedence.

So this is treated like:

massMark / (heightMark ** 2)

That is what we want for BMI.
*/

Strings and Template Literals

const firstName = "Jonas";
const job = "teacher";
const birthYear = 1991;
const year = 2037;

const jonas =
  "I'm " +
  firstName +
  ", a " +
  (year - birthYear) +
  " year old " +
  job +
  "!";

console.log(jonas);

/*
This builds a string using +.

The expression:

year - birthYear

is inside parentheses so it calculates first.

2037 - 1991 = 46

Final string:

"I'm Jonas, a 46 year old teacher!"
*/
const jonasNew = `I'm ${firstName}, a ${year - birthYear} year old ${job}!`;
console.log(jonasNew);

/*
This is a template literal.

Template literals use backticks:
` `

Inside them, you can insert variables or expressions using:
${}

This is cleaner than using many + signs.

${firstName} becomes Jonas.
${year - birthYear} becomes 46.
${job} becomes teacher.
*/

Expected Output

I'm Jonas, a 46 year old teacher!
I'm Jonas, a 46 year old teacher!

Multi-line Strings

console.log("String with \n\
multiple \n\
lines");

console.log(`String
multiple
lines`);

/*
\n means new line.

The first version uses escape characters.

The second version uses a template literal.

Template literals make multi-line strings much easier to write.
*/

Expected Output

String with
multiple
lines

String
multiple
lines

if / else Statements

const age = 15;

if (age >= 18) {
  console.log("Sarah can start driving license 🚗");
} else {
  const yearsLeft = 18 - age;
  console.log(`Sarah is too young. Wait another ${yearsLeft} years :)`);
}

/*
if checks a condition.

Condition:

age >= 18

Here:

15 >= 18

false

So the else block runs.

yearsLeft:

18 - 15 = 3

So the message says Sarah must wait 3 more years.
*/

Expected Output

Sarah is too young. Wait another 3 years :)

Using if / else to Assign a Value

const birthYear = 2012;

let century;

if (birthYear <= 2000) {
  century = 20;
} else {
  century = 21;
}

console.log(century);

/*
century is declared with let because we assign it later.

If birthYear is less than or equal to 2000,
century becomes 20.

Otherwise, century becomes 21.

Here:

2012 <= 2000

false

So:

century = 21
*/

Expected Output

21

Coding Challenge #2: BMI With if / else

const massMark = 78;
const heightMark = 1.69;
const massJohn = 92;
const heightJohn = 1.95;

const BMIMark = massMark / heightMark ** 2;
const BMIJohn = massJohn / (heightJohn * heightJohn);

console.log(BMIMark, BMIJohn);

if (BMIMark > BMIJohn) {
  console.log(`Mark's BMI (${BMIMark}) is higher than John's (${BMIJohn})!`);
} else {
  console.log(`John's BMI (${BMIJohn}) is higher than Marks's (${BMIMark})!`);
}

/*
This compares both BMI values.

If Mark's BMI is greater, the if block runs.

Otherwise, the else block runs.

For this test data:

Mark BMI ≈ 27.31
John BMI ≈ 24.19

So Mark's BMI is higher.
*/

Expected Output

27.309968138370508 24.194608809993426
Mark's BMI (27.309968138370508) is higher than John's (24.194608809993426)!

Small correction

console.log(`John's BMI (${BMIJohn}) is higher than Marks's (${BMIMark})!`);

/*
Small grammar issue:

Marks's should be Mark's.

Better:
*/

console.log(`John's BMI (${BMIJohn}) is higher than Mark's (${BMIMark})!`);

Type Conversion

const inputYear = "1991";

console.log(Number(inputYear), inputYear);
console.log(Number(inputYear) + 18);

/*
inputYear is a string:

"1991"

Number(inputYear) converts it into a number:
1991

Important:
Number(inputYear) does not change the original variable.
inputYear is still the string "1991".

So:
Number(inputYear) + 18

becomes:
1991 + 18 = 2009
*/

Expected Output

1991 1991
2009
console.log(Number("Jonas"));
console.log(typeof NaN);

/*
"Jonas" cannot be converted into a valid number.

So JavaScript gives:

NaN

NaN means:
Not a Number

But weirdly:
typeof NaN
is:
number
Because NaN is a special invalid number value.
*/

Expected Output

NaN
number

Converting Numbers to Strings

console.log(String(23), 23);

/*
String(23) converts the number 23 into the string "23".

The second 23 is still a number.

In the console, they may look similar,
but internally they are different types.
*/

Type Coercion

console.log("I am " + 23 + " years old");

This uses type coercion.

When + is used with a string, JavaScript converts the number to a string.

So: "I am " + 23 + " years old"

becomes: "I am " + "23" + " years old"

Result: "I am 23 years old"

console.log("23" - "10" - 3);
console.log("23" / "2");

/*
With -, /, and *, JavaScript usually converts strings to numbers.

"23" - "10" - 3

becomes:
23 - 10 - 3 = 10


"23" / "2"

becomes:
23 / 2 = 11.5

*/

Expected Output

I am 23 years old
10
11.5

Common Coercion Confusion

let n = "1" + 1; // "11"
n = n - 1;

console.log(n);

/*
First:

"1" + 1

Because + sees a string, it joins them:
"1" + "1" = "11"

So:

n = "11"

Then:

n - 1
The - operator converts "11" into a number:

11 - 1 = 10

So final output is:

10
*/

Expected Output

10

Truthy and Falsy Values

console.log(Boolean(0));
console.log(Boolean(undefined));
console.log(Boolean("Jonas"));
console.log(Boolean({}));
console.log(Boolean(""));

/*
Falsy values become false when converted to boolean.

Common falsy values:

0
""
undefined
null
NaN
false

Truthy values become true.

"Jonas" is truthy.
{} is truthy, even though it is an empty object.
*/

Expected Output

false
false
true
true
false

Truthy/Falsy in if Conditions

const money = 100;

if (money) {
  console.log("Don't spend it all ;)");
} else {
  console.log("You should get a job!");
}

/*
money is 100.

100 is truthy.

So the if block runs.
*/

Expected Output

Don't spend it all ;)

Beginner Trap: 0 Is Falsy

let height = 0;

if (height) {
  console.log("YAY! Height is defined");
} else {
  console.log("Height is UNDEFINED");
}

/*
height is 0.

0 is falsy.

So the else block runs.

But this message is misleading.

height is actually defined.
Its value is just 0.

This is a common beginner bug.
*/

Expected Output

Height is UNDEFINED

Better version

let height = 0;

if (height !== undefined) {
  console.log("YAY! Height is defined");
} else {
  console.log("Height is UNDEFINED");
}

/*
This checks whether height is undefined,
instead of checking whether it is truthy.

Now 0 is accepted as a valid defined value.
*/

Equality Operators: == vs ===

const age = "18";

if (age === 18) {
  console.log("You just became an adult :D (strict)");
}

if (age == 18) {
  console.log("You just became an adult :D (loose)");
}

/*
age is the string:

"18"

Strict equality === checks value AND type.

"18" === 18
false

Because one is string and one is number.

Loose equality == allows type coercion.

"18" == 18
true
Because JavaScript converts "18" into 18 before comparing.
*/

Expected Output

You just became an adult :D (loose)

Main rule

/*
Prefer === in most real JavaScript code.

Use == only when you intentionally want type coercion.

For beginners:

Use === by default.
*/

prompt, Number, and User Input

const favourite = Number(prompt("What's your favourite number?"));

console.log(favourite);
console.log(typeof favourite);

/*
prompt asks the user for input in the browser.

Important:
prompt always returns a string.

So if the user types:
23

prompt returns:
"23"

Number(...) can convert it to a number if needed:
23

Then typeof favourite becomes:

number
*/

Runtime note

/*
This code works in browsers.

It may not work in Node.js by default because Node does not have browser prompt().
*/

else if Chains

if (favourite === 23) {
  console.log("Cool! 23 is an amazing number!");
} else if (favourite === 7) {
  console.log("7 is also a cool number");
} else if (favourite === 9) {
  console.log("9 is also a cool number");
} else {
  console.log("Number is not 23 or 7 or 9");
}

/*
This checks multiple possibilities.

Only one branch runs.

If favourite is 23, the first block runs.
If not, it checks 7.
If not, it checks 9.
If none match, the else block runs.
*/

Small typo correction

console.log("Cool! 23 is an amzaing number!");

/*
Typo:

amzaing

Better:
*/

console.log("Cool! 23 is an amazing number!");

Not Equal: !==

if (favourite !== 23) {
  console.log("Why not 23?");
}

/*
!== means strict not equal.

This checks:

Is favourite NOT equal to 23?

If yes, print:

Why not 23?
*/

Logical Operators

const hasDriversLicense = true;
const hasGoodVision = true;

console.log(hasDriversLicense && hasGoodVision);
console.log(hasDriversLicense || hasGoodVision);
console.log(!hasDriversLicense);

/*
&& means AND.

true && true = true

Both sides must be true.

|| means OR.

true || true = true

At least one side must be true.

! means NOT.

!true = false
*/

Expected Output

true
true
false

Combining Multiple Conditions

const isTired = false;

if (hasDriversLicense && hasGoodVision && !isTired) {
  console.log("Sarah is able to drive!");
} else {
  console.log("Someone else should drive...");
}

/*
Sarah can drive only if:

hasDriversLicense is true
hasGoodVision is true
isTired is false

Because isTired is false:

!isTired becomes true

So the full condition is:

true && true && true

Result:

true

The if block runs.
*/

Expected Output

Sarah is able to drive!

Coding Challenge #3: Average Scores and Logical Operators

const scoreDolphins = (97 + 112 + 80) / 3;
const scoreKoalas = (109 + 95 + 50) / 3;

console.log(scoreDolphins, scoreKoalas);

if (scoreDolphins > scoreKoalas && scoreDolphins >= 100) {
  console.log("Dolphins win the trophy 🏆");
} else if (scoreKoalas > scoreDolphins && scoreKoalas >= 100) {
  console.log("Koalas win the trophy 🏆");
} else if (
  scoreDolphins === scoreKoalas &&
  scoreDolphins >= 100 &&
  scoreKoalas >= 100
) {
  console.log("Both win the trophy!");
} else {
  console.log("No one wins the trophy 😭");
}

/*
First calculate averages.

Dolphins:

(97 + 112 + 80) / 3
289 / 3
96.333...

Koalas:

(109 + 95 + 50) / 3
254 / 3
84.666...

Dolphins have a higher score.

But the rule says the winner must also have at least 100.

Dolphins average is below 100.

So no one wins.
*/

Expected Output

96.33333333333333 84.66666666666667
No one wins the trophy 😭

Beginner note

scoreDolphins > scoreKoalas && scoreDolphins >= 100

/*
Both conditions must be true.

Dolphins must:

1. Beat Koalas
2. Have score at least 100

If either condition is false, Dolphins do not win.
*/

switch Statement

const day = "friday";

switch (day) {
  case "monday":
    console.log("Plan course structure");
    console.log("Go to coding meetup");
    break;

  case "tuesday":
    console.log("Prepare theory videos");
    break;

  case "wednesday":
  case "thursday":
    console.log("Write code examples");
    break;

  case "friday":
    console.log("Record videos");
    break;

  case "saturday":
  case "sunday":
    console.log("Enjoy the weekend :D");
    break;

  default:
    console.log("Not a valid day!");
}

/*
switch compares day against each case.

Here:

day = "friday"

So JavaScript jumps to:

case "friday":

and prints:

Record videos

break stops the switch.

Without break, JavaScript would continue running the next cases too.
*/

Expected Output

Record videos

switch Compared With if / else

if (day === "monday") {
  console.log("Plan course structure");
  console.log("Go to coding meetup");
} else if (day === "tuesday") {
  console.log("Prepare theory videos");
} else if (day === "wednesday" || day === "thursday") {
  console.log("Write code examples");
} else if (day === "friday") {
  console.log("Record videos");
} else if (day === "saturday" || day === "sunday") {
  console.log("Enjoy the weekend :D");
} else {
  console.log("Not a valid day!");
}

/*
This does the same job as the switch statement.

switch is often cleaner when checking one variable against many exact values.

if/else is more flexible when conditions are complex.
*/

Statements and Expressions

3 + 4;
1991;
true && false && !false;

/*
These are expressions.

An expression produces a value.

3 + 4 produces 7.
1991 produces 1991.
true && false && !false produces false.
*/
if (23 > 10) {
  const str = "23 is bigger";
}

/*
This is a statement.

A statement performs an action.

An if statement controls whether some code runs.

Important:

You cannot directly place an if statement inside a template literal expression.
*/
const me = "Jonas";
console.log(`I'm ${2037 - 1991} years old ${me}`);

/*
Inside ${}, you put expressions.

2037 - 1991 is an expression.

me is also an expression because it produces the value "Jonas".

So this works.
*/

Ternary Operator

const age = 23;

const drink = age >= 18 ? "wine 🍷" : "water 💧";

console.log(drink);

/*
The ternary operator is a shorter if/else expression.

Syntax:

condition ? valueIfTrue : valueIfFalse

Here:

age >= 18

23 >= 18

true

So drink becomes:

"wine 🍷"
*/

Expected Output

wine 🍷

Ternary Compared With if / else

let drink2;

if (age >= 18) {
  drink2 = "wine 🍷";
} else {
  drink2 = "water 💧";
}

console.log(drink2);

/*
This does the same thing as the ternary version.

The if/else version is longer.

The ternary version is useful for simple value selection.

Use if/else when the logic becomes bigger or harder to read.
*/

Ternary Inside Template Literals

console.log(`I like to drink ${age >= 18 ? "wine 🍷" : "water 💧"}`);

/*
Template literals can contain expressions inside ${}.

A ternary operator is an expression.

So it can be used directly inside a template literal.

Since age is 23, this becomes:

I like to drink wine 🍷
*/

Expected Output

I like to drink wine 🍷

Coding Challenge #4: Tip Calculator

const bill = 430;

const tip = bill <= 300 && bill >= 50 ? bill * 0.15 : bill * 0.2;

console.log(
  `The bill was ${bill}, the tip was ${tip}, and the total value ${bill + tip}`
);

/*
The rule:

If bill is between 50 and 300, tip is 15%.
Otherwise, tip is 20%.

Condition:

bill <= 300 && bill >= 50

For bill = 430:

430 <= 300 is false
430 >= 50 is true

false && true = false

So the second value runs:

bill * 0.2

430 * 0.2 = 86

Total:

430 + 86 = 516
*/

Expected Output

The bill was 430, the tip was 86, and the total value 516

Cleaner condition order

const tip = bill >= 50 && bill <= 300 ? bill * 0.15 : bill * 0.2;

/*
This is the same logic.

But many people find this easier to read:

bill >= 50 && bill <= 300

It reads like:

bill is at least 50 and at most 300
*/

Important Mistakes / Beginner Warnings in This File

/*
1. Most code is commented out.

So it will not run until you uncomment it.

2. Many sections reuse variable names.

Example:

const firstName
const age
const birthYear
const now

If you uncomment too many sections at once,
you may get redeclaration errors.

3. Avoid creating variables without let/const/var.

Bad:

lastName = "Schmedtmann";

Better:

const lastName = "Schmedtmann";

4. prompt() works in the browser, not normal Node.js by default.

5. Use === instead of == in most real code.

6. Be careful with truthy/falsy checks.

A value like 0 is falsy,
but that does not mean it is undefined.
*/

Main Takeaway

This code is a strong JavaScript basics practice file. The biggest concepts are:

/*
Variables store values.

let can be reassigned.
const cannot be reassigned.
Avoid var in modern JavaScript.

JavaScript values have types:
number, string, boolean, undefined, null, object, etc.

Operators let you calculate, compare, and assign values.

if/else, switch, and ternary operators help your program make decisions.

Template literals are the clean modern way to build strings.

JavaScript sometimes converts types automatically,
but this can be confusing.

So prefer:

Number(...) for clear conversion
=== for strict comparison
const by default
let only when needed
*/

Concise beginner takeaway: JavaScript is flexible, but that flexibility can create confusion, so write clear variables, use **const**/**let**, prefer **===**, and be careful with automatic type conversion.


JavaScript Functions, Arrays, Objects, and Loops Notes

This code mainly teaches strict mode, functions, arrow functions, arrays, objects, object methods, **this**, loops, **break**, **continue**, and using loops with arrays.

Important first note:

'use strict';

/*
'use strict' activates strict mode.

Strict mode makes JavaScript catch more mistakes.

For example, without strict mode, JavaScript may allow accidental global variables.

With strict mode, some bad patterns become errors instead of silently working.

This helps beginners because mistakes become easier to find.
*/

Strict Mode Notes

'use strict';

let hasDriversLicense = false;
const passTest = true;

if (passTest) hasDriversLicense = true;
if (hasDriversLicense) console.log('I can drive :D');

/*
hasDriversLicense starts as false.

passTest is true.

This condition runs:

if (passTest)

Since passTest is true, this line runs:

hasDriversLicense = true;

Now hasDriversLicense becomes true.

Then this condition also runs:

if (hasDriversLicense)

So the console prints:

I can drive :D
*/

Expected Output

I can drive :D

Why strict mode matters

// const interface = 'Audio';
// const private = 534;

/*
These are commented out.

In strict mode, some words are reserved for future JavaScript features.

Examples:

interface
private

So using them as variable names can cause errors.

Strict mode helps prevent you from writing code that may conflict with JavaScript rules.
*/

Basic Functions

function logger() {
  console.log('My name is Jonas');
}

logger();
logger();
logger();

/*
function logger() creates a reusable block of code.

The function body is:

console.log('My name is Jonas');

But the function does not run automatically.

To run it, we call it:

logger();

Calling a function is also called:

- running a function
- invoking a function
- executing a function

Since logger() is called 3 times, the message prints 3 times.
*/

Expected Output

My name is Jonas
My name is Jonas
My name is Jonas

Functions With Parameters and Return Values

function fruitProcessor(apples, oranges) {
  const juice = `Juice with ${apples} apples and ${oranges} oranges.`;
  return juice;
}

const appleJuice = fruitProcessor(5, 0);
console.log(appleJuice);

const appleOrangeJuice = fruitProcessor(2, 4);
console.log(appleOrangeJuice);

/*
apples and oranges are parameters.

Parameters are like temporary variables inside the function.

When we call:

fruitProcessor(5, 0)

apples becomes 5.
oranges becomes 0.

The function creates this string:

Juice with 5 apples and 0 oranges.

return sends that value back outside the function.

So appleJuice stores the returned string.

Same with:

fruitProcessor(2, 4)

apples becomes 2.
oranges becomes 4.
*/

Expected Output

Juice with 5 apples and 0 oranges.
Juice with 2 apples and 4 oranges.

Common beginner confusion

function fruitProcessor(apples, oranges) {
  const juice = `Juice with ${apples} apples and ${oranges} oranges.`;
  return juice;
}

/*
console.log prints something.

return gives a value back.

They are not the same.

If a function only console.logs, you can see output,
but you cannot easily reuse the result.

If a function returns a value,
you can store it in a variable and use it later.
*/

Function Declarations vs Function Expressions

// Function declaration
function calcAge1(birthYeah) {
  return 2037 - birthYeah;
}

const age1 = calcAge1(1991);

// Function expression
const calcAge2 = function (birthYeah) {
  return 2037 - birthYeah;
};

const age2 = calcAge2(1991);

console.log(age1, age2);

/*
Both functions calculate age.

birthYeah is probably meant to be birthYear.

The typo does not break the code because the parameter name is just a local variable name.

calcAge1 is a function declaration.

calcAge2 is a function expression stored inside a const variable.

Both are called in a similar way:

calcAge1(1991)
calcAge2(1991)

Both return:

2037 - 1991 = 46
*/

Expected Output

46 46

Important difference

/*
Function declarations are hoisted.
That means you can call them before they appear in the code.

Function expressions are not usable before the variable is initialized.

So this works:
calcAge1(1991);

function calcAge1(birthYear) {
  return 2037 - birthYear;
}

But this does NOT work:
calcAge2(1991);

const calcAge2 = function (birthYear) {
  return 2037 - birthYear;
};

Reason:
calcAge2 is a const variable.
It is in the Temporal Dead Zone before initialization.
*/

Arrow Functions

const calcAge3 = birthYeah => 2037 - birthYeah;

const age3 = calcAge3(1991);
console.log(age3);

/*
This is an arrow function.
It is a shorter way to write a function.

Because there is only one parameter, parentheses are optional:

birthYeah => ...

Because there is only one expression, return is automatic:

2037 - birthYeah

So this returns:

2037 - 1991 = 46
*/

Expected Output

46

Arrow Function With Multiple Parameters and Multiple Lines

const yearsUntilRetirement = (birthYeah, firstName) => {
  const age = 2037 - birthYeah;
  const retirement = 65 - age;

  return `${firstName} retires in ${retirement} years`;
};

console.log(yearsUntilRetirement(1991, 'Jonas'));
console.log(yearsUntilRetirement(1980, 'Bob'));

/*
This arrow function has two parameters:

birthYeah
firstName

Because there are two parameters, we need parentheses:

(birthYeah, firstName)

Because the function has multiple lines, we need curly braces:

{
  ...
}

When using curly braces in an arrow function,
return is NOT automatic.

So we must write:

return ...
*/

Expected Output

Jonas retires in 19 years
Bob retires in 8 years

Beginner note

const calcAge3 = birthYeah => 2037 - birthYeah;

const yearsUntilRetirement = (birthYeah, firstName) => {
  const age = 2037 - birthYeah;
  const retirement = 65 - age;
  return `${firstName} retires in ${retirement} years`;
};

/*
Short arrow function:
parameters => expression
Automatically returns the expression.

Multi-line arrow function:

(parameter1, parameter2) => {
  statements...
  return result;
}

Needs explicit return.
*/

Functions Calling Other Functions

function cutFruitPieces(fruit) {
  return fruit * 4;
}

function fruitProcessor(apples, oranges) {
  const applePieces = cutFruitPieces(apples);
  const orangePieces = cutFruitPieces(oranges);

  const juice = `Juice with ${applePieces} piece of apple and ${orangePieces} pieces of orange.`;
  return juice;
}

console.log(fruitProcessor(2, 3));

/*
cutFruitPieces takes a fruit count and multiplies it by 4.

fruitProcessor receives:

apples = 2
oranges = 3

Then:

applePieces = cutFruitPieces(2)
applePieces = 2 * 4
applePieces = 8

orangePieces = cutFruitPieces(3)
orangePieces = 3 * 4
orangePieces = 12

Then the final juice string is returned.
*/

Expected Output

Juice with 8 piece of apple and 12 pieces of orange.

Small wording issue

const juice = `Juice with ${applePieces} piece of apple and ${orangePieces} pieces of orange.`;

/*
If applePieces is 8, "piece" should be plural.

Better:

const juice = `Juice with ${applePieces} pieces of apple and ${orangePieces} pieces of orange.`;
*/

Reviewing Functions: Returning Early

const calcAge = function (birthYeah) {
  return 2037 - birthYeah;
};

const yearsUntilRetirement = function (birthYeah, firstName) {
  const age = calcAge(birthYeah);
  const retirement = 65 - age;

  if (retirement > 0) {
    console.log(`${firstName} retires in ${retirement} years`);
    return retirement;
  } else {
    console.log(`${firstName} has already retired 🎉`);
    return -1;
  }
};

console.log(yearsUntilRetirement(1991, 'Jonas'));
console.log(yearsUntilRetirement(1950, 'Mike'));

/*
yearsUntilRetirement calls calcAge inside it.

For Jonas:

birthYeah = 1991
age = 2037 - 1991 = 46
retirement = 65 - 46 = 19

retirement > 0 is true.

So it logs the message and returns 19.

For Mike:

birthYeah = 1950
age = 2037 - 1950 = 87
retirement = 65 - 87 = -22

retirement > 0 is false.

So the else block runs and returns -1.
*/

Expected Output

Jonas retires in 19 years
19
Mike has already retired 🎉
-1

Why two lines print for each person

console.log(yearsUntilRetirement(1991, 'Jonas'));

/*
The function itself does a console.log:

Jonas retires in 19 years

Then the outer console.log prints the returned value:

19

That is why you see both the message and the number.
*/

Coding Challenge #1: Average Score Function

const calcAverage = (a, b, c) => (a + b + c) / 3;

console.log(calcAverage(3, 4, 5));

/*
calcAverage is an arrow function.

It takes 3 scores:

a, b, c

Then it calculates:

(a + b + c) / 3

For:

calcAverage(3, 4, 5)

3 + 4 + 5 = 12
12 / 3 = 4
*/

Expected Output

4

Checking the Winner

let scoreDolphins = calcAverage(44, 23, 71);
let scoreKoalas = calcAverage(65, 54, 49);

console.log(scoreDolphins, scoreKoalas);

const checkWinner = function (avgDolphins, avgKoalas) {
  if (avgDolphins >= 2 * avgKoalas) {
    console.log(`Dolphins win 🏆 (${avgDolphins} vs. ${avgKoalas})`);
  } else if (avgKoalas >= 2 * avgDolphins) {
    console.log(`Koalas win 🏆 (${avgKoalas} vs. ${avgDolphins})`);
  } else {
    console.log('No team wins...');
  }
};

checkWinner(scoreDolphins, scoreKoalas);

/*
Dolphins average:

(44 + 23 + 71) / 3
138 / 3
46

Koalas average:

(65 + 54 + 49) / 3
168 / 3
56

Rule:

A team wins only if its score is at least double the other team's score.

Dolphins:

46 >= 2 * 56
46 >= 112
false

Koalas:

56 >= 2 * 46
56 >= 92
false

So no team wins.
*/

Expected Output

46 56
No team wins...

Arrays Introduction

const friend1 = 'Michael';
const friend2 = 'Steven';
const friend3 = 'Peter';

const friends = ['Michael', 'Steven', 'Peter'];

console.log(friends);

/*
Instead of creating separate variables for related values,
we can store them in an array.

An array is an ordered list of values.

friends contains 3 strings:

index 0 -> Michael
index 1 -> Steven
index 2 -> Peter

Important:
Array indexes start at 0, not 1.
*/

Accessing Array Elements

const friends = ['Michael', 'Steven', 'Peter'];

console.log(friends[0]);
console.log(friends[2]);

console.log(friends.length);
console.log(friends[friends.length - 1]);

/*
friends[0] gives the first element.

friends[2] gives the third element.

friends.length gives the number of elements.

friends.length is 3.

The last element is at:

friends.length - 1

So:

friends[3 - 1]
friends[2]

That gives:

Peter
*/

Expected Output

Michael
Peter
3
Peter

Changing Array Elements

friends[2] = 'Jay';
console.log(friends);

// friends = ['Bob', 'Alice'];

/*
This is allowed:

friends[2] = 'Jay';

Even though friends was declared with const.

Why?

const prevents reassigning the variable itself.

It does NOT freeze the array contents.

So this is allowed:

friends[2] = 'Jay';

But this is not allowed:

friends = ['Bob', 'Alice'];

Because that tries to make friends point to a new array.
*/

Arrays Can Store Mixed Values

const firstName = 'Jonas';

const jonas = [
  firstName,
  'Schmedtmann',
  2037 - 1991,
  'teacher',
  friends
];

console.log(jonas);
console.log(jonas.length);

/*
Arrays can store different types of values:

string
number
another array
expressions
variables

Here:

firstName gives "Jonas"
2037 - 1991 gives 46
friends is another array

So jonas is a mixed array.
*/

Using Functions With Arrays

const calcAge = function (birthYeah) {
  return 2037 - birthYeah;
};

const years = [1990, 1967, 2002, 2010, 2018];

const age1 = calcAge(years[0]);
const age2 = calcAge(years[1]);
const age3 = calcAge(years[years.length - 1]);

console.log(age1, age2, age3);

const ages = [
  calcAge(years[0]),
  calcAge(years[1]),
  calcAge(years[years.length - 1])
];

console.log(ages);

/*
years[0] is 1990.
years[1] is 1967.
years[years.length - 1] is the last year, 2018.

We pass those values into calcAge.

age1:

2037 - 1990 = 47

age2:

2037 - 1967 = 70

age3:

2037 - 2018 = 19

Then we store these returned values in a new array called ages.
*/

Expected Output

47 70 19
[47, 70, 19]

Basic Array Methods

const friends = ['Michael', 'Steven', 'Peter'];

const newLength = friends.push('Jay');

console.log(friends);
console.log(newLength);

/*
push adds an element to the end of an array.

Before:

['Michael', 'Steven', 'Peter']

After:

['Michael', 'Steven', 'Peter', 'Jay']

push also returns the new length of the array.

So newLength is 4.
*/
friends.unshift('John');
console.log(friends);

/*
unshift adds an element to the beginning of an array.

Before:

['Michael', 'Steven', 'Peter', 'Jay']

After:

['John', 'Michael', 'Steven', 'Peter', 'Jay']
*/

Removing Array Elements

friends.pop(); // removes last element

const popped = friends.pop();

console.log(popped);
console.log(friends);

/*
pop removes the last element.

First pop removes:

Jay

Second pop removes:

Peter

The second removed value is stored in popped.

So popped is:

Peter

Now friends contains:

['John', 'Michael', 'Steven']
*/
friends.shift();
console.log(friends);

/*
shift removes the first element.

Before:

['John', 'Michael', 'Steven']

After removing John:

['Michael', 'Steven']
*/

Finding Values in Arrays

console.log(friends.indexOf('Steven'));
console.log(friends.indexOf('Bob'));

/*
indexOf tells you the index of a value.

If Steven is found at index 1, it returns:

1

If Bob is not found, it returns:

-1
*/
friends.push(23);

console.log(friends.includes('Steven'));
console.log(friends.includes('Bob'));
console.log(friends.includes(23));

/*
includes checks if the array contains a value.

It returns true or false.

includes uses strict checking.

So:

friends.includes(23)

is true if the number 23 exists.

But:

friends.includes('23')

would be false if the array contains number 23, not string "23".
*/

Using includes in an if Statement

if (friends.includes('Steven')) {
  console.log('You have a friend called Steven');
}

/*
includes returns a boolean.

If Steven exists in the array, the condition is true.

So the message prints.
*/

Coding Challenge #2: Tip Calculator With Arrays

const calcTip = function (bill) {
  return bill >= 50 && bill <= 300 ? bill * 0.15 : bill * 0.2;
};

const bills = [125, 555, 44];

const tips = [
  calcTip(bills[0]),
  calcTip(bills[1]),
  calcTip(bills[2])
];

const totals = [
  bills[0] + tips[0],
  bills[1] + tips[1],
  bills[2] + tips[2]
];

console.log(bills, tips, totals);

/*
calcTip returns:

15% if bill is between 50 and 300
20% otherwise

bills:

125, 555, 44

Tip for 125:

125 * 0.15 = 18.75

Tip for 555:

555 * 0.2 = 111

Tip for 44:

44 * 0.2 = 8.8

totals means bill + tip.
*/

Expected Output

[125, 555, 44] [18.75, 111, 8.8] [143.75, 666, 52.8]

Objects Introduction

const jonasArray = [
  'Jonas',
  'Schmedtmann',
  2037 - 1991,
  'teacher',
  ['Michael', 'Peter', 'Steven']
];

const jonas = {
  firstName: 'Jonas',
  lastName: 'Schmedtmann',
  age: 2037 - 1991,
  job: 'teacher',
  friends: ['Michael', 'Peter', 'Steven']
};

/*
Arrays are ordered lists.

Objects store data using property names.

In jonasArray, you need to remember:

index 0 means first name
index 1 means last name
index 2 means age

In jonas object, the meaning is clear:

firstName: 'Jonas'
lastName: 'Schmedtmann'
age: 46

Objects are better when each value has a clear label.
*/

Dot vs Bracket Notation

const jonas = {
  firstName: 'Jonas',
  lastName: 'Schmedtmann',
  age: 2037 - 1991,
  job: 'teacher',
  friends: ['Michael', 'Peter', 'Steven']
};

console.log(jonas.lastName);
console.log(jonas['lastName']);

/*
Both lines access the same property.

Dot notation:

jonas.lastName

Bracket notation:

jonas['lastName']

Dot notation is simpler.

Bracket notation is useful when the property name is calculated or stored in a variable.
*/

Expected Output

Schmedtmann
Schmedtmann

Why Bracket Notation Is Useful

const nameKey = 'Name';

console.log(jonas['first' + nameKey]);
console.log(jonas['last' + nameKey]);

/*
'nameKey' stores:

'Name'

So:

'first' + nameKey

becomes:

'firstName'

jonas['firstName'] gives:

Jonas

And:

'last' + nameKey

becomes:

'lastName'

jonas['lastName'] gives:

Schmedtmann
*/

Expected Output

Jonas
Schmedtmann

Invalid syntax example

// console.log(jonas.'last' + nameKey)

/*
This is invalid.

Dot notation cannot use expressions.

This does NOT work:

jonas.'last' + nameKey

Use bracket notation for computed property names:

jonas['last' + nameKey]
*/

Reading Object Properties From User Input

const interestedIn = prompt(
  'What do you want to know about Jonas? Choose between firstName, lastName, age, job, and friends'
);

if (jonas[interestedIn]) {
  console.log(jonas[interestedIn]);
} else {
  console.log('Wrong request! Choose between firstName, lastName, age, job, and friends');
}

/*
prompt gets input from the user.

If the user types:

job

then:

jonas[interestedIn]

becomes:

jonas['job']

which gives:

teacher

This only works with bracket notation.

jonas.interestedIn would look for a property literally called interestedIn,
which does not exist.
*/

Runtime note

/*
prompt works in browsers.

It does not work in normal Node.js by default.
*/

Small beginner warning

if (jonas[interestedIn]) {
  console.log(jonas[interestedIn]);
}

/*
This checks truthiness.

It works for this object because all valid values are truthy.

But if a valid property had a value like:

0
false
''

then this condition would incorrectly go to else.

A more precise check would be:
*/

if (interestedIn in jonas) {
  console.log(jonas[interestedIn]);
} else {
  console.log('Wrong request!');
}

Adding New Properties to Objects

jonas.location = 'Portugal';
jonas['twitter'] = '@jonasschmedtman';

console.log(jonas);

/*
You can add new properties after creating an object.

Dot notation:
jonas.location = 'Portugal'

Bracket notation:
jonas['twitter'] = '@jonasschmedtman'

Both add new properties to the object.
*/

Object Challenge

console.log(
  `${jonas.firstName} has ${jonas.friends.length} friends, and his best friend is called ${jonas.friends[0]}`
);

/*
jonas.firstName gives:

Jonas

jonas.friends gives the friends array:

['Michael', 'Peter', 'Steven']

jonas.friends.length gives:

3

jonas.friends[0] gives the first friend:

Michael

Final output:

Jonas has 3 friends, and his best friend is called Michael
*/

Object Methods and this

const jonas = {
  firstName: 'Jonas',
  lastName: 'Schmedtmann',
  birthYeah: 1991,
  job: 'teacher',
  friends: ['Michael', 'Peter', 'Steven'],
  hasDriversLicense: true,

  calcAge: function () {
    this.age = 2037 - this.birthYeah;
    return this.age;
  },

  getSummary: function () {
    return `${this.firstName} is a ${this.calcAge()}-year old ${jonas.job}, and he has ${
      this.hasDriversLicense ? 'a' : 'no'
    } driver's license.`;
  }
};

console.log(jonas.calcAge());

/*
A function inside an object is called a method.

calcAge is a method.

Inside a method, this refers to the object that called the method.

Here:

jonas.calcAge()

So inside calcAge:

this === jonas

Therefore:

this.birthYeah

means:

jonas.birthYeah

which is 1991.

this.age = 2037 - this.birthYeah

creates a new property:

jonas.age = 46

Then it returns 46.
*/

Expected Output

46

Storing a Calculated Value in an Object

console.log(jonas.age);
console.log(jonas.age);
console.log(jonas.age);

/*
After calling:

jonas.calcAge()

the object now has an age property.

So jonas.age gives:

46

This avoids recalculating the age every time.

Important:

If you try jonas.age before calling jonas.calcAge(),
it would be undefined because the age property has not been created yet.
*/

Expected Output

46
46
46

Object Method Calling Another Object Method

getSummary: function () {
  return `${this.firstName} is a ${this.calcAge()}-year old ${jonas.job}, and he has ${
    this.hasDriversLicense ? 'a' : 'no'
  } driver's license.`;
}

/*
getSummary is another method.

this.firstName gives:

Jonas

this.calcAge() calls the calcAge method from the same object.

this.hasDriversLicense ? 'a' : 'no'

means:

if hasDriversLicense is true, use 'a'
otherwise, use 'no'

Since hasDriversLicense is true, it uses:

a

Final sentence:

Jonas is a 46-year old teacher, and he has a driver's license.
*/

Small improvement

return `${this.firstName} is a ${this.calcAge()}-year old ${jonas.job}, and he has ${
  this.hasDriversLicense ? 'a' : 'no'
} driver's license.`;

/*
This works, but inside the object method it is better to use:

this.job

instead of:

jonas.job

Better:
*/

return `${this.firstName} is a ${this.calcAge()}-year old ${this.job}, and he has ${
  this.hasDriversLicense ? 'a' : 'no'
} driver's license.`;

/*
Why?

Using this.job makes the method depend on the current object,
not specifically on a variable named jonas.
*/

Coding Challenge #3: BMI With Objects

const mark = {
  fullName: 'Mark Miller',
  mass: 78,
  height: 1.69,

  calcBMI: function () {
    this.bmi = this.mass / this.height ** 2;
    return this.bmi;
  }
};

const john = {
  fullName: 'John Smith',
  mass: 92,
  height: 1.95,

  calcBMI: function () {
    this.bmi = this.mass / this.height ** 2;
    return this.bmi;
  }
};

mark.calcBMI();
john.calcBMI();

console.log(mark.bmi, john.bmi);

/*
Each object stores:

fullName
mass
height
calcBMI method

When we call:

mark.calcBMI()

this refers to mark.

So:

this.mass means mark.mass
this.height means mark.height
this.bmi creates mark.bmi

Same for john.

BMI formula:

mass / height ** 2
*/

Expected Output

27.309968138370508 24.194608809993426

Comparing Object BMI Values

if (mark.bmi > john.bmi) {
  console.log(`${mark.fullName}'s BMI (${mark.bmi}) is higher than ${john.fullName}'s BMI (${john.bmi})`);
} else if (john.bmi > mark.bmi) {
  console.log(`${john.fullName}'s BMI (${john.bmi}) is higher than ${mark.fullName}'s BMI (${mark.bmi})`);
}

/*
mark.bmi is about 27.31.
john.bmi is about 24.19.

So Mark's BMI is higher.

The first if block runs.
*/

Expected Output

Mark Miller's BMI (27.309968138370508) is higher than John Smith's BMI (24.194608809993426)

Small improvement

/*
You could round the BMI values to make the output cleaner:
*/

console.log(
  `${mark.fullName}'s BMI (${mark.bmi.toFixed(1)}) is higher than ${john.fullName}'s BMI (${john.bmi.toFixed(1)})`
);

/*
toFixed(1) shows 1 digit after the decimal.

Example:

27.3
24.2
*/

The for Loop

for (let rep = 1; rep <= 30; rep++) {
  console.log(`Lifting weights repetition ${rep} 🏋️‍♀️`);
}

/*
A for loop repeats code.

It has 3 main parts:

let rep = 1

Start rep at 1.

rep <= 30

Keep looping while this is true.

rep++

After each loop, increase rep by 1.

So this prints repetitions from 1 to 30.
*/

Beginner structure

for (initial value; condition; update) {
  // code to repeat
}

/*
The loop keeps running while the condition is true.

When the condition becomes false, the loop stops.
*/

Looping Through Arrays

const jonas = [
  'Jonas',
  'Schmedtmann',
  2037 - 1991,
  'teacher',
  ['Michael', 'Peter', 'Steven'],
  true
];

const types = [];

for (let i = 0; i < jonas.length; i++) {
  console.log(jonas[i], typeof jonas[i]);

  types.push(typeof jonas[i]);
}

console.log(types);

/*
i starts at 0 because array indexes start at 0.

The loop keeps going while:

i < jonas.length

This means it visits every valid index in the array.

jonas[i] reads the current array element.

typeof jonas[i] gives the type of that element.

types.push(...) adds the type into the types array.
*/

Expected Output Concept

Jonas string
Schmedtmann string
46 number
teacher string
['Michael', 'Peter', 'Steven'] object
true boolean

['string', 'string', 'number', 'string', 'object', 'boolean']

Important note

typeof ['Michael', 'Peter', 'Steven']

/*
This gives:

object

In JavaScript, arrays are a special kind of object.

So typeof array returns "object".

To properly check if something is an array, use:

Array.isArray(value)
*/

Creating a New Array Using a Loop

const years = [1991, 2007, 1969, 2020];
const ages = [];

for (let i = 0; i < years.length; i++) {
  ages.push(2037 - years[i]);
}

console.log(ages);

/*
The loop goes through each birth year.

For each year, it calculates:

2037 - year

Then pushes the result into ages.

Step-by-step:

2037 - 1991 = 46
2037 - 2007 = 30
2037 - 1969 = 68
2037 - 2020 = 17

So ages becomes:

[46, 30, 68, 17]
*/

Expected Output

[46, 30, 68, 17]

continue

console.log('--- ONLY STRINGS ---');

for (let i = 0; i < jonas.length; i++) {
  if (typeof jonas[i] !== 'string') continue;

  console.log(jonas[i], typeof jonas[i]);
}

/*
continue skips the current loop iteration.

This line means:

If the current value is NOT a string,
skip the rest of this loop round.

So only strings get printed.

Numbers, arrays, and booleans are skipped.
*/

Expected Output Concept

--- ONLY STRINGS ---
Jonas string
Schmedtmann string
teacher string

break

console.log('--- BREAK WITH NUMBER ---');

for (let i = 0; i < jonas.length; i++) {
  if (typeof jonas[i] === 'number') break;

  console.log(jonas[i], typeof jonas[i]);
}

/*
break stops the entire loop.

The loop prints values until it finds a number.

jonas[0] is string -> print
jonas[1] is string -> print
jonas[2] is number -> break

So the loop stops before printing the number.
*/

Expected Output

--- BREAK WITH NUMBER ---
Jonas string
Schmedtmann string

Looping Backwards

const jonas = [
  'Jonas',
  'Schmedtmann',
  2037 - 1991,
  'teacher',
  ['Michael', 'Peter', 'Steven'],
  true
];

for (let i = jonas.length - 1; i >= 0; i--) {
  console.log(i, jonas[i]);
}

/*
jonas.length - 1 gives the last valid index.

The array has 6 elements.

So the last index is:

6 - 1 = 5

The loop starts at index 5 and goes down to 0.

i-- means decrease i by 1 after each loop.
*/

Expected Output Concept

5 true
4 ['Michael', 'Peter', 'Steven']
3 teacher
2 46
1 Schmedtmann
0 Jonas

Loops Inside Loops

for (let exercise = 1; exercise < 4; exercise++) {
  console.log(`-------- Starting exercise ${exercise}`);

  for (let rep = 1; rep < 6; rep++) {
    console.log(`Exercise ${exercise}: Lifting weight repetition ${rep} 🏋️‍♀️`);
  }
}

/*
This is a nested loop.

Outer loop:

exercise = 1, 2, 3

Inner loop:

rep = 1, 2, 3, 4, 5

For every one exercise,
the inner loop runs 5 times.

So total repetitions printed:

3 exercises * 5 reps = 15 rep messages
*/

while Loop

let rep = 1;

while (rep <= 10) {
  rep++;
}

/*
A while loop keeps running while the condition is true.

This loop starts with:

rep = 1

Each time, rep increases by 1.

When rep becomes 11, the condition becomes false:

11 <= 10

false

Then the loop stops.

This version does not print anything because console.log is commented out.
*/

for vs while

for (let rep = 1; rep <= 10; rep++) {
  console.log(`Lifting weights repetition ${rep} 🏋️‍♀️`);
}

/*
A for loop is good when you know how many times you want to loop.

Example:

Loop from 1 to 10.
*/
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...');
}

/*
A while loop is good when you do NOT know how many times you need to loop.

Here, we keep rolling dice until we roll a 6.

Math.random() gives a random decimal from 0 up to less than 1.

Math.random() * 6 gives a number from 0 up to less than 6.

Math.trunc(...) removes the decimal part.

Then + 1 makes the result between 1 and 6.
*/

Runtime-dependent output

/*
The dice output changes every time you run the program.

Example possible output:

You rolled a 3
You rolled a 1
You rolled a 5
Loop is about to end...

But it could be different every run because Math.random() is random.
*/

Coding Challenge #4: Tip Calculator With Loops

const calcTip = function (bill) {
  return bill >= 50 && bill <= 300 ? bill * 0.15 : bill * 0.2;
};

const bills = [22, 295, 176, 440, 37, 105, 10, 1100, 86, 52];
const tips = [];
const totals = [];

for (let i = 0; i < bills.length; i++) {
  const tip = calcTip(bills[i]);

  tips.push(tip);
  totals.push(tip + bills[i]);
}

console.log(bills, tips, totals);

/*
bills contains 10 bill values.

tips starts empty.
totals starts empty.

The loop goes through every bill.

For each bill:

1. Calculate the tip using calcTip.
2. Push the tip into tips.
3. Push bill + tip into totals.

This is better than manually writing:

calcTip(bills[0])
calcTip(bills[1])
calcTip(bills[2])

because the loop works for any array length.
*/

Expected Output Concept

bills:
[22, 295, 176, 440, 37, 105, 10, 1100, 86, 52]

tips:
[4.4, 44.25, 26.4, 88, 7.4, 15.75, 2, 220, 12.9, 7.8]

totals:
[26.4, 339.25, 202.4, 528, 44.4, 120.75, 12, 1320, 98.9, 59.8]

Calculating the Average of an Array

const calcAverage = function (arr) {
  let sum = 0;

  for (let i = 0; i < arr.length; i++) {
    sum += arr[i];
  }

  return sum / arr.length;
};

console.log(calcAverage([2, 3, 7]));
console.log(calcAverage(totals));
console.log(calcAverage(tips));

/*
calcAverage takes an array.

Example:

[2, 3, 7]

sum starts at 0.

Loop steps:

sum = 0 + 2 = 2
sum = 2 + 3 = 5
sum = 5 + 7 = 12

Then:

sum / arr.length

12 / 3 = 4

So the average is 4.
*/

Expected Output for First Call

4

Beginner warning

return sum / arr.length;

/*
This works if the array has at least one value.

But if arr is empty:

[]

then arr.length is 0.

sum / 0 gives Infinity or NaN-like bad results depending on the situation.

A safer version could be:
*/

const calcAverageSafe = function (arr) {
  if (arr.length === 0) return 0;

  let sum = 0;

  for (let i = 0; i < arr.length; i++) {
    sum += arr[i];
  }

  return sum / arr.length;
};

Important Mistakes / Warnings in This File

/*
1. birthYeah is probably a typo.

The code still works because the parameter name can be anything.

But birthYear is clearer.

2. avgDolhins in the challenge text is a typo.

It should be avgDolphins.

3. Many const names are reused.

Examples:

const jonas
const friends
const calcAge

If you uncomment multiple sections together, you may get redeclaration errors.

4. prompt() only works in browsers by default.

It will not work in plain Node.js unless you use another input package.

5. Arrays declared with const can still be mutated.

This is allowed:

friends.push('Jay');
friends[2] = 'Jay';

This is not allowed:

friends = ['Bob'];

6. In object methods, prefer this.property instead of hardcoding the object name.

Better:

this.job

Instead of:

jonas.job
*/

Main Takeaway

/*
Functions let you reuse logic.

return gives a value back from a function.

Arrow functions are shorter, but multi-line arrow functions need explicit return.

Arrays store ordered lists of values.

Objects store labeled data using properties.

Methods are functions inside objects.

this usually refers to the object calling the method.

for loops are useful when you know how many times to repeat.

while loops are useful when you repeat until some condition changes.

continue skips one loop round.

break stops the loop completely.

Loops are very powerful with arrays because they let you process many values without repeating code manually.
*/

Concise beginner takeaway: functions organize logic, arrays store lists, objects store labeled data, and loops let you repeat work efficiently instead of writing the same code again and again.