Rozwiązania do zadań: JavaScript od podstaw

Cześć! To jest fragment książki JavaScript od podstaw, która ma pomóc w nauce programowania od zera.

Stringi

W pierwszym przypadku zwróć uwagę na zastosowanie pojedynczych cudzysłowów, aby w środku umieścić podwójne.

console.log('Kim jest "ona"?'); // Kim jest "ona"?

W drugim przypadku nie użyliśmy tej sztuczki, więc podwójny cudzysłów w środku kończy stringa, a następnie otwiera nowy. Na szczęście w środku znajduje się znak +, który łączy dwa teksty w jeden. Zauważ podwójną spację pomiędzy "Użyję" a "by" w wyświetlonym tekście.

console.log("Użyję "+" by dodać teksty");
// Użyję  by dodać teksty

Jeśli chcielibyśmy wypisać "+", to powinniśmy otoczyć cały tekst cudzysłowami pojedynczymi.

console.log('Użyję "+" by dodać teksty');
// Użyję "+" by dodać teksty

W ostatnim przypadku popełniliśmy typowy błąd nowicjusza i zapomnieliśmy o użyciu spacji, gdy dodajemy dwa stringi.

console.log("Tekst," + "następny," + "jeszcze jeden");
// Tekst,następny,jeszcze jeden

To, co powinniśmy zrobić, to dodać tę spację albo w tekście po lewej, albo po prawej od znaku dodawania.

console.log("Tekst, " + "następny, " + "jeszcze jeden");
// Tekst, następny, jeszcze jeden

console.log("Tekst," + " następny," + " jeszcze jeden");
// Tekst, następny, jeszcze jeden

JavaScript jako kalkulator

console.log(1 * 2 + 3 * 4 + 5 * 6 + 7 * 8 + 9);
// 109
console.log(1 * (2 + 3) * (4 + 5) * (6 + 7) * (8 + 9));
// 9945
// Różni się od poprzedniego, bo nawiasy wymuszają
// dodawanie przed mnożeniem.
console.log(1 * 2 / 3 * 4 / 5 * 6 / 7 * 8 / 9 * 10);
// 4.063492063492064
// musiało być większe od 1, bo ostatnie jest mnożenie.
console.log(2 ** 10); // 1024
// to bardzo znana liczba w programowaniu
// "kilo" w kontekście informatyki oznacza zwykle 1024,
// w czasie gdy w innych kontekstach oznacza 1000.

JavaScript jako kalkulator porównujący

W pierwszym przypadku mnożenie kończy się na 5, a więc możemy spodziewać się, że prawa strona jest większa. Poniższy kod to potwierdza.

console.log(1 * 2 + 3 * 4 + 5 < 1 + 2 * 3 + 4 * 5);
// true

Poniżej sprawdzamy dla innych przypadków, czy lewa strona jest większa od prawej.

console.log(2 ** 20 > 3 ** 15);
// false, więc prawa strona jest większa
console.log(2 ** 15 > 10 ** 4);
// true, więc lewa strona jest większa

Możemy też sprawdzić na odwrót.

console.log(2 ** 20 < 3 ** 15); // true
console.log(2 ** 15 < 3 ** 4); // false

Definiowanie zmiennych

const constantValue = "1234";
console.log(constantValue); // 1234

let name = "Michał";

//***
console.log(name); // Michał
name = "Marcin";
console.log(name); // Marcin

Nazywanie zmiennych

  • publicWorkshops — Poprawna nazwa.
  • 1wayTicket — Nie zaczynamy nazw zmiennych od liczb. Lepsza wersja to oneWayTicket.
  • arbitraryUser — W zasadzie poprawne, aczkolwiek pierwsza część jest niepotrzebnie długa. Gdy chcemy wyrazić, że chodzi o jakąś wartość (przydatne przy testach), używamy a lub an. Lepiej byłoby aUser.
  • noteType — Poprawna nazwa.
  • userMaciek — Formalnie akceptowalna, aczkolwiek użycie polskiego imienia jest dyskusyjne. Także zwykle wydzielamy zmienne, by określić coś uniwersalnego — co charakteryzuje tego użytkownika. W pewnych przypadkach taka nazwa byłaby akceptowalna, ale zazwyczaj preferowalibyśmy na przykład: aUser jak chodzi o "jakiegoś użytkownika", user1 jeśli chodzi o jednego z kilku, czy adminUser jeśli chodzi o użytkownika będącego adminem.
  • postUserRequest — Poprawna nazwa. Tutaj post odnosi się do metody POST w protokole internetowym HTTP.
  • szkolenieWarszawa — Nie używamy polskich słów przy nazywaniu zmiennych. Lepsza wersja to workshopWarsaw.

Dodawanie i odejmowanie od wartości zmiennych

let fruits = "Figa ";
let price = 0;

fruits += "Gruszka ";
price += 3.2;

console.log(fruits); // Figa Gruszka
console.log(price); // 3.2

fruits += "Banan ";
price += 2.8;

console.log(fruits); // Figa Gruszka Banan
console.log(price); // 6

fruits += "(kupon zniżkowy)";
price -= 1.4;

console.log(price); // 4.6

undefinednull

let name = null;
let surname;

console.log(name); // null
console.log(surname); // undefined

name = surname;
surname = "Michalski";

console.log(name); // undefined
console.log(surname); // Michalski

Warunki

Fragment pierwszy, wariant pierwszy

Cześć Michał
Może coś do picia?

Fragment pierwszy, wariant drugi

Witamy Pana Prezesa
Cześć Prezes Marek
Może coś do picia?

Fragment pierwszy, wariant trzeci

Cześć Paweł
Może czekoladkę?

Fragment pierwszy, wariant czwarty

Może czekoladkę?

Fragment drugi, wariant pierwszy

Cześć Michał
Może coś do picia?

Fragment drugi, wariant drugi

Witamy Pana Prezesa
Może coś do picia?

Fragment drugi, wariant trzeci

Cześć Paweł
Może czekoladkę?

Fragment drugi, wariant czwarty

Cześć
Może czekoladkę?

Fragment trzeci, wariant pierwszy

Cześć Michał
Kategoria: Dorośli

Fragment trzeci, wariant drugi

Witamy Pana Prezesa
Kategoria: Starsi

Fragment trzeci, wariant trzeci

Cześć Paweł
Kategoria: Dzieci

Fragment trzeci, wariant czwarty

Kategoria: Młodzież

Złożone wyrażenia logiczne

const hasComputer = true;
const passedTest = false;
let isGrounded = false;

console.log(hasComputer && passedTest);
// false, ponieważ true && false
console.log(passedTest || isGrounded);
// false, ponieważ false || false
console.log(hasComputer && !passedTest);
// true, ponieważ true && !false

let canPlayGames = hasComputer && !isGrounded;
// true, ponieważ true && !false,
// a więc true && true
console.log(canPlayGames); // true

let playGames = hasComputer && canPlayGames;
// true, ponieważ true && true

if (!passedTest) {
  isGrounded = true;
}

console.log(playGames); // true,
// ponieważ gdy wartość ta była obliczana,
// isGrounded był równy false
console.log(passedTest || !isGrounded);
// false, ponieważ false || !true,
// ponieważ aktualna wartość isGrounded to true
console.log(!(!hasComputer || !passedTest));
// false, ponieważ true && false,
// bo !(!a || !b) jest równoznaczne z a && b

Operator warunkowy

  1. "Ma na imię Jasio" zostanie wypisane dla name równego "Jasio". Pusty string dla name będącego falsy (czyli false, null, undefined, "", 0, 0n, NaN).
  2. "Ma 10 lat" zostanie wypisane dla age równego 10, zaś null dla age będącego falsy.
  3. "Ma na imię Janek" zostanie wypisane dla name równego "Janek". Dla name będącego falsy, wypisana będzie wartość name.
  4. Wartość age będzie wypisana, gdy jest ona liczbą większą od 0, a w przeciwnym wypadku będzie to 0.

Ciągi matematyczne

Wypisz kolejne liczby parzyste (wielokrotności 2), zaczynając od 0, a mniejsze od 100.

let i = 0;
while (i < 100) {
  console.log(i);
  i += 2; // albo i = i + 2;
}

Wypisz kolejne wielokrotności 7, zaczynając od 0, a mniejsze od 100.

let i = 0;
while (i < 100) {
  console.log(i);
  i += 7; // albo i = i + 7;
}

Wyświetl kolejne wartości powstałe w wyniku wielokrotnego potrajania liczby, zaczynając od 13, tak długo, jak długo wynik jest mniejszy niż 1000.

let i = 13;
while (i < 1000) {
  console.log(i);
  i *= 3; // albo i = i * 3;
}

Wypisz kwadraty kolejnych liczb całkowitych, mniejszych od 1000.

let i = 0;
while (i * i < 1000) {
  console.log(i * i);
  i += 1; // albo i = i + 1;
}

Przypadki użycia pętli for dla liczb

Wypisz kolejne liczby od 10 do 5.

for (let i = 10; i >= 5; i--) {
  console.log(i);
}

Wypisz kolejne liczby od 5 do 30, z pominięciem ostatniej.

for (let i = 5; i < 30; i++) {
  console.log(i);
}

Wypisz co drugą liczbę od 20 do 0.

for (let i = 20; i >= 0; i -= 2) {
  console.log(i);
}

Funkcje

function printSum(a, b) {
  console.log(a + b);
} 

function printNumbers(a, b) {
  for (let i = a; i <= b; i++) {
    console.log(i);
  }
}

function printStars(num) {
  let stars = "";
  for (let i = 0; i < num; i++) {
    stars += "*";
  }
  console.log(stars);
}

function printSquare(size) {
  for (let i = 0; i < size; i++) {
    printStars(size);
  }
}

function printTriangle(size) {
  for (let i = 1; i <= size; i++) {
    printStars(i);
  }
}

Funkcje zwracające wartości

function daysToMillis(days) {
  return days * 24 * 60 * 60 * 1000;
}

function triangleArea(a, b) {
  return a * b / 2;
} 

function biggestOf(a, b, c) {
  if (a >= b && a >= c) {
    return a;
  } else if (b >= c) {
    return b;
  } else {
    return c;
  }
}

JavaScript jako kalkulator matematyczny

console.log(4 ** 2 * Math.PI); // 50.26...

console.log(Math.log10(20 * 30 + 40)); // 2.80...

console.log(Math.log2(Math.pow(10, -5))); // -16.60...
// albo
console.log(Math.log2(10 ** -5)); // -16.60...

console.log(Math.abs(Math.pow(-3, 7))); // 2187
// albo
console.log(Math.abs((-3) ** 7)); // 2187

Funkcje jako wartości

  • speak() — Tylko Mowa...
  • speak(cheerKids)Hej, dzieci oraz Mowa...
  • speak(cheerAll)Hej, dzieci, Witam rodziców a potem Mowa...
  • speak(cheerAll, bless)Hej, dzieci, Witam rodziców, Mowa... i wreszcie Zdrowia!
  • speak(bless)Zdrowia! a potem Mowa...
  • speak(undefined, bless)Mowa... a potem Zdrowia!

Obiekty

const user = {
  name: "Kuba",
  surname: "Wędrowycz",
  address: {
    country: "Polska",
    city: "Stary Majdan",
    postal: "22-120",
    street: null
  }
};

const book = {
  title: "Karpie Bijem",
  releaseYear: 2019
};

// Użycie
console.log(user.surname); // Wędrowycz
user.name = "Jakub";
console.log(user.address.city); // Stary Majdan
user.address.street = "Bagnowska";

console.log(book.releaseYear); // 2019

releaseYear nie powinien być oddzielony na osobny obiekt release, ponieważ nie ma to sensu, gdy nie interesuje nas nic więcej na temat tego wydania. Moglibyśmy tak zrobić, gdyby było (albo gdybyśmy się spodziewali, że będzie) więcej danych na temat wypuszczenia tej książki, na przykład releaseStatus czy releaseType.

Zabawa z obiektami

let obj = {};
const obj2 = obj;

obj.name = "Alek";
console.log(obj.name); // Alek
console.log(obj2.name); // Alek

const obj3 = { name: obj.name };
console.log(obj3); // {name: "Alek"}

obj = { size: "S" };
console.log(obj.name); // undefined
console.log(obj.size); // S
console.log(obj2.name); // Alek

JSON

Przykładowa odpowiedź mogłaby wyglądać tak:

{
  "name": "Air Max 720",
  "brand": "Nike",
  "description": "Mają największą...",
  "size": 45,
  "imageUrl": "https://example.pl/path/to/airmax.png",
  "price": {
    "value": "649.99",
    "currency": "PLN"
  }
}

Konto bankowe

function makeBankAccount() {
  return {
    balance: 0,
    deposit: function(amount) {
      this.balance += amount;
      return this.balance;
    },
    withdraw: function(amount) {
      if(amount > this.balance) {
        amount = this.balance;
      }
      this.balance -= amount;
      return amount;
    },
    currentBalance: function() {
      return this.balance;
    } 
  }
}

Krótsza alternatywa:

function makeBankAccount() {
  return {
    balance: 0,
    deposit: function(amount) {
      this.balance += amount;
      return this.balance;
    },
    withdraw: function(amount) {
      amount = Math.min(amount, this.balance);
      this.balance -= amount;
      return amount;
    },
    currentBalance: function() {
      return this.balance;
    } 
  }
}

Konto bankowe z operatorem new

function BankAccount() {
  this.balance = 0;
  this.deposit = function(amount) {
    this.balance += amount;
    return this.balance;
  };
  this.withdraw = function(amount) {
    amount = Math.min(amount, this.balance);
    this.balance -= amount;
    return amount;
  };
  this.currentBalance = function() {
    return this.balance;
  };
}

Konto bankowe przez definicję klasy

class BankAccount {
  constructor() {
    this.balance = 0;
  }

  deposit(amount) {
    this.balance += amount;
    return this.balance;
  }

  withdraw(amount) {
    amount = Math.min(amount, this.balance);
    this.balance -= amount;
    return amount;
  }

  currentBalance() {
    return this.balance;
  }
}

Tworzenie i modyfikacja tablic

const values = [true, 42, "AAA"];
console.log(values.pop()); // "AAA"
console.log(values.pop()); // 42
values.push(88);
console.log(values); // [true, 88]

const pets = ["dog", "cat"];
pets.pop();
pets.push("pig");
console.log(pets); // ["dog", "pig"]
const pet = pets.pop();
console.log(pets); // ["dog"]
console.log(pet); // "pig"

Wielkość i elementy tablicy

const letters = ['A', 'B', 'C', 'D', 'E', 'F'];
console.log(letters.length); // 6
console.log(letters[4]); // E

const numbers = [5, 6, 7, 8, 9, 10, 11, 12];
console.log(numbers.length); // 8
console.log(numbers[3]); // 8

forEach

function printAllValues(values) {
  values.forEach(function (value, index) {
    console.log("Na pozycji " + index +
      ' znajduje się "' + value + '"');
  });
}

function sumAll(numbers) {
  let sum = 0;
  numbers.forEach(function (number) {
    sum += number;
  });
  return sum;
}

map

Przykładowa odpowiedź:

function toFullNames(users) {
  return users.map(function (user) {
    if(user.secondName) {
      return user.firstName + " " + user.secondName +
        " " + user.lastName;
    } else {
      return user.firstName + " " + user.lastName;
    }
  });
}

Funkcje strzałkowe przy przetwarzaniu kolekcji

const list = students
  .filter(s => s.score >= 3.5)
  .filter(s => s.points >= 20)
  .map(s => s.name + ", " + s.score)
  .forEach(str => console.log(str));

Podwójny filtr można zamienić na pojedynczy, a mapowanie przed forEach jest niepotrzebnym krokiem.

const list = students
 .filter(s => s.score >= 3.5 && s.points >= 20)
 .forEach(s => console.log(s.name + ", " + s.score));

Funkcje strzałkowe zamiast metod

const times = (a, b) => a * b;

const compareScoresDescending = 
  (s1, s2) => s2.score — s1.score;

const compareNames = (s1, s2) => {
  if(s1.name< s2.name) return -1;
  if(s1.name >s2.name) return 1;
  return 0;
};

Z funkcji strzałkowych do anonimowych i nazwanych

// 1. Funkcje anonimowe

const triple = function(i) {
  return i * 3;
};

const first = function(arr) {
  return arr[0];
};

const bigger = function(a, b) {
  return a > b ? a : b;
};
// 2. Funkcje nazwane

function triple(i) {
  return i * 3;
}

function first(arr) {
  return arr[0];
}

function bigger(a, b) {
  return a > b ? a : b;
}