Skip to content

3. Podstawy JavaScriptu

Uwaga: w dalszej części termin „[Javascript]” będzie zawsze odnosił się do standardu ECMAScript 6.

W ramach poprzedniego projektu JavaScript utwórz folder o nazwie [bases]. Umieścimy w nim przykłady z tej sekcji:

Image

3.1. skrypt [bases-01]

Aby przedstawić podstawy PHP7, wykorzystaliśmy następujący kod (patrz akapit z linkiem):


<?php

// to jest komentarz
// zmienna użyta bez zadeklarowania
$nom = "dupont";
// wyświetlanie na ekranie
print "nom=$nom\n";
// tablica zawierająca elementy różnych typów
$tableau = array("un", "deux", 3, 4);
// liczba elementów
$n = count($tableau);
// pętla
for ($i = 0; $i < $n; $i++) {
  print "tableau[$i]=$tableau[$i]\n";
}
// inicjalizacja dwóch zmiennych zawartością tablicy
list($chaine1, $chaine2) = array("chaine1", "chaine2");
// konkatenacja dwóch ciągów znaków
$chaine3 = $chaine1 . $chaine2;
// wyświetlenie wyniku
print "[$chaine1,$chaine2,$chaine3]\n";
// użycie funkcji
affiche($chaine1);
// typ zmiennej może być znany
afficheType("n", $n);
afficheType("chaine1", $chaine1);
afficheType("tableau", $tableau);
// typ zmiennej może ulec zmianie w trakcie wykonywania
$n = "a changé";
afficheType("n", $n);
// funkcja może zwracać wynik
$res1 = f1(4);
print "res1=$res1\n";
// funkcja może zwracać tablicę wartości
list($res1, $res2, $res3) = f2();
print "(res1,res2,res3)=[$res1,$res2,$res3]\n";
// wartości te można by było pobrać do tablicy
$t = f2();
for ($i = 0; $i < count($t); $i++) {
  print "t[$i]=$t[$i]\n";
}
// testy
for ($i = 0; $i < count($t); $i++) {
  // wyświetla tylko ciągi znaków
  if (getType($t[$i]) === "string") {
    print "t[$i]=$t[$i]\n";
  }
}
// operatory porównania == i ===
if("2"==2){
  print "avec l'opérateur ==, la chaîne 2 est égale à l'entier 2\n";
}else{
  print "avec l'opérateur ==, la chaîne 2 n'est pas égale à l'entier 2\n";
}
if("2"===2){
  print "avec l'opérateur ===, la chaîne 2 est égale à l'entier 2\n";
}
else{
  print "avec l'opérateur ===, la chaîne 2 n'est pas égale à l'entier 2\n";
}
// inne testy
for ($i = 0; $i < count($t); $i++) {
  // wyświetla tylko liczby całkowite >10
  if (getType($t[$i]) === "integer" and $t[$i] > 10) {
    print "t[$i]=$t[$i]\n";
  }
}
// pętla while
$t = [8, 5, 0, -2, 3, 4];
$i = 0;
$somme = 0;
while ($i < count($t) and $t[$i] > 0) {
  print "t[$i]=$t[$i]\n";
  $somme += $t[$i];   //$somme=$somme+$t[$i]
  $i++;               //$i=$i+1
}//while
print "somme=$somme\n";

// koniec programu
exit;

//----------------------------------
function affiche($chaine) {
  // wyświetla $chaine
  print "chaine=$chaine\n";
}

//wyświetla
//----------------------------------
function afficheType($name, $variable) {
  // wyświetla typ $variable
  print "type[variable $" . $name . "]=" . getType($variable) . "\n";
}

//afficheType
//----------------------------------
function f1($param) {
  // dodaje 10 do $param
  return $param + 10;
}

//----------------------------------
function f2() {
  // zwraca 3 wartości
  return array("un", 0, 100);
}
?>

Po przetłumaczeniu na JavaScript daje to następujący kod:


'use strict';
// to jest komentarz
// stała
const nom = "dupont";
// wyświetlanie na ekranie
console.log("nom : ", nom);
// tablica zawierająca elementy różnych typów
const tableau = ["un", "deux", 3, 4];
// liczba elementów
let n = tableau.length;
// pętla
for (let i = 0; i < n; i++) {
  console.log("tableau[", i, "] = ", tableau[i]);
}
// inicjalizacja dwóch zmiennych zawartością tablicy
let [chaine1, chaine2] = ["chaine1", "chaine2"];
// konkatenacja dwóch ciągów znaków
const chaine3 = chaine1 + chaine2;
// wyświetlenie wyniku
console.log([chaine1, chaine2, chaine3]);
// użycie funkcji
affiche(chaine1);
// typ zmiennej może być znany
afficheType("n", n);
afficheType("chaine1", chaine1);
afficheType("tableau", tableau);
// typ zmiennej może ulec zmianie w trakcie wykonywania
n = "a changé";
afficheType("n", n);
// funkcja może zwracać wynik
let res1 = f1(4);
console.log("res1=", res1);
// funkcja może zwracać tablicę wartości
let res2, res3;
[res1, res2, res3] = f2();
console.log("(res1,res2,res3)=", [res1, res2, res3]);
// wartości te można by było pobrać do tablicy
let t = f2();
for (let i = 0; i < t.length; i++) {
  console.log("t[i]=", t[i]);
}
// testy
for (let i = 0; i < t.length; i++) {
  // wyświetla tylko ciągi znaków
  if (typeof (t[i]) === "string") {
    console.log("t[i]=", t[i]);
  }
}
// operatory porównania == i ===
if ("2" == 2) {
  console.log("avec l'opérateur ==, la chaîne 2 est égale à l'entier 2");
} else {
  console.log("avec l'opérateur ==, la chaîne 2 n'est pas égale à l'entier 2");
}
if ("2" === 2) {
  console.log("avec l'opérateur ===, la chaîne 2 est égale à l'entier 2");
} else {
  console.log("avec l'opérateur ===, la chaîne 2 n'est pas égale à l'entier 2");
}
// inne testy
for (let i = 0; i < t.length; i++) {
  // wyświetla tylko liczby całkowite większe niż 10
  if (typeof (t[i]) === "number" && Math.floor(t[i]) === t[i] && t[i] > 10) {
    console.log("t[i]=", t[i]);
  }
}
// pętla while
t = [8, 5, 0, -2, 3, 4];
let i = 0;
let somme = 0;
while (i < t.length && t[i] > 0) {
  console.log("t[i]=", t[i]);
  somme += t[i];
  i++;
}
console.log("somme=", somme);

// zatrzymanie programu, ponieważ nie ma już kodu do wykonania

//wyświetla
//----------------------------------
function affiche(chaine) {
  // wyświetla ciąg znaków
  console.log("chaine=", chaine);
}

//afficheType
//----------------------------------
function afficheType(name, variable) {
  // wyświetla typ zmiennej
  console.log("type[variable ", name, "]=", typeof (variable));
}

//----------------------------------
function f1(param) {
  // dodaje 10 do parametru
  return param + 10;
}

//----------------------------------
function f2() {
  // zwraca 3 wartości
  return ["un", 0, 100];
}

Omówmy różnice między kodami PHP i ECMAScript 6 a deklaracją [use strict] (wiersz 1):

  • pierwszą różnicą jest to, że w ECMAScript zmienne deklaruje się za pomocą następujących słów kluczowych:
    • [let] – do zadeklarowania zmiennej, której wartość może ulec zmianie w trakcie wykonywania kodu;
    • [const] do zadeklarowania zmiennej, której wartość nie ulegnie zmianie (czyli stałej) w trakcie wykonywania kodu;
    • można również użyć słowa kluczowego [var] zamiast [let]. Było to słowo kluczowe używane wraz z ECMAScript 5. Nie będziemy go używać w tym kursie;
  • wiersz 6: metoda wyświetlania [console.log] może wyświetlać wszelkiego rodzaju dane: ciągi znaków, liczby, wartości logiczne, tablice, obiekty. Metoda PHP [print] nie potrafi natywnie wyświetlać tablic i obiektów. W wyrażeniu [console.log], [console] jest obiektem, a [log] metodą tego obiektu;
  • wiersz 8: tablice JavaScript są obiektami, do których odwołuje się wskaźnik. Gdy piszemy:

const tableau = ["un", "deux", 3, 4];

zmienna [tableau] jest wskaźnikiem na tablicę literałową ["un", "deux", 3, 4]. Zmiana zawartości tablicy nie powoduje zmiany jej wskaźnika. Dlatego też tablica jest najczęściej deklarowana za pomocą słowa kluczowego [const]. W PHP tablica nie jest odwołana przez wskaźnik. Jest to literał;

  • wiersz 12: zmienna pętli [i] jest zadeklarowana (let) w pętli. Słowo kluczowe [let] ma zasięg blokowy (kod między nawiasami klamrowymi). W związku z tym zmienna [i] z wiersza 12 jest znana tylko w pętli;
  • wiersz 18: operatorem konkatenacji ciągów znaków jest operator „+” w JavaScript, a w PHP – operator „.” Cechą charakterystyczną tego operatora jest to, że ma on pierwszeństwo przed operatorem dodawania „+”. Zatem:
    • w PHP „1” + 2 daje liczbę 3;
    • w JavaScript „1” + 2 daje ciąg znaków „12”;
  • wiersz 20: [console.log] potrafi wyświetlać tabele;
  • wiersz 82: w JavaScript nie ma możliwości określenia typu parametrów funkcji;
  • wiersz 91: operator [typeof] pozwala określić typ danych. Są cztery typy: liczba, ciąg znaków, wartość logiczna i obiekt. Należy zauważyć, że w JavaScript nie ma typu [integer] ani typu [tableau]. Jak już wspomniano, tabele są obsługiwane za pomocą wskaźników i należą do kategorii obiektów;
  • wiersze 50–59: podobnie jak w PHP, JavaScript posiada dwa operatory porównania, == i „===”, o takim samym znaczeniu jak w PHP. ESLint najczęściej sygnalizuje operator == jako potencjalny błąd. Należy konsekwentnie stosować operator ‘===’;
  • wiersz 79: można było wpisać instrukcję [return], ale ESLint generuje ostrzeżenie, że [return] należy stosować wyłącznie w funkcji;

Uruchommy ten kod:

Image

Wyniki wykonania:


[Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe "c:\Temp\19-09-01\javascript\bases\bases-01.js"
nom : dupont
tableau[ 0 ] = un
tableau[ 1 ] = deux
tableau[ 2 ] = 3
tableau[ 3 ] = 4
[ 'chaine1', 'chaine2', 'chaine1chaine2' ]
chaine= chaine1
type[variable n ]= number
type[variable chaine1 ]= string
type[variable tableau ]= object
type[variable n ]= string
res1= 14
(res1,res2,res3)= [ 'un', 0, 100 ]
t[ 0 ]= un
t[ 1 ]= 0
t[ 2 ]= 100
t[ 0 ]= un
avec l'opérateur ==, la chaîne 2 est égale à l'entier 2
avec l'opérateur ===, la chaîne 2 n'est pas égale à l'entier 2
t[ 2 ]= 100
t[ 0 ]= 8
t[ 1 ]= 5
somme= 13

[Done] exited with code=0 in 0.316 seconds

W napisanym kodzie ESLINT zgłasza dwa błędy:

Image

  • po najechaniu kursorem na czerwoną linię ostrzeżenia pojawia się komunikat o błędzie [3]. W tym przypadku ESLint nie rozpoznaje, że porównujemy dwie stałe. Jeden z dwóch operandów powinien być zmienną;
  • w [4] opcja [Quick Fix] pozwala zignorować ostrzeżenie, jeśli zdecydujemy się nie poprawiać błędu;

Image

  • w [5] istnieje możliwość wyłączenia ostrzeżenia dla bieżącego wiersza lub dla całego pliku. W tym przypadku wybieramy tę drugą opcję. Na początku pliku generowany jest wówczas wiersz [6];

3.2. skrypt [bases-02]

Skrypt [bases-02] pokazuje zastosowanie słów kluczowych [let] i [const]:


'use strict';
// aby zainicjować zmienną, używa się let lub const
// let dla zmiennych
let x = 4;
x++;
console.log(x);
// `const` dla stałych
const y = 10;
x += y;
// zabronione
y++;
  • wiersz 11 powoduje błąd podczas wykonywania skryptu [1-2]. Jest on sygnalizowany przez skrypt ESLint przed wykonaniem skryptu [3]:

Image

3.3. skrypt [bases-03]

Skrypt [bases-03] sprawdza zakres zmiennych w JavaScript:


'use strict';
// zakres zmiennych
let count = 1;
function doSomething() {
  // count jest tutaj znany
  console.log("count=",count);
}
// wywołanie
doSomething();
  • zmienna [count] zadeklarowana poza funkcją [doSomething] jest jednak rozpoznawalna w tej funkcji. Stanowi to zasadniczą różnicę w stosunku do PHP;

Wykonanie


[Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe "c:\Temp\19-09-01\javascript\bases\bases-03.js"
count= 1

[Done] exited with code=0 in 0.3 seconds

3.4. skrypt [bases-04]

Zmienna lokalna przesłania zmienną globalną o tej samej nazwie:


'use strict';
// zakres zmiennych
const count = 1;
function doSomething() {
  // zmienna lokalna zasłania zmienną globalną
  const count = 2;
  console.log("count inside function=",count);
}
// zmienna globalna
console.log("count outside function=",count);
// zmienna lokalna
doSomething();

Wykonanie


[Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe "c:\Temp\19-09-01\javascript\bases\bases-04.js"
count outside function= 1
count inside function= 2

[Done] exited with code=0 in 0.246 seconds

3.5. skrypt [bases-05]

Zmienna zdefiniowana w funkcji nie jest rozpoznawana poza nią:


'use strict';
// zakres zmiennych
function doSomething() {
  // zmienna lokalna w funkcji
  const count = 2;
  console.log("count inside function=", count);
}
// tutaj count nie jest znany
console.log("count outside function=", count);
doSomething();

ESLint zgłasza błąd w wierszu 9:

Image

3.6. skrypt [bases-06]

Słowa kluczowe [let] i [const] definiują zmienne o zasięgu [bloc] (kod w nawiasach klamrowych), ale nie słowo kluczowe [var]:


'use strict';
// słowo kluczowe [let] pozwala zdefiniować zmienną o zasięgu bloku
{
  // zmienna [count] jest znana tylko w tym bloku
  let count = 1;
  console.log("count=", count);
}
// tutaj zmienna [count] nie jest znana
count++;

// słowo kluczowe [const] pozwala zdefiniować zmienną o zasięgu bloku
{
  // zmienna [count2] jest znana tylko w tym bloku
  const count2 = 1;
  console.log("count=", count2);
}
// tutaj zmienna [count2] nie jest znana
count2++;

// słowo kluczowe [var] nie pozwala na zdefiniowanie zmiennej o zasięgu bloku
{
  // zmienna [count3] będzie znana globalnie
  var count3 = 1;
  console.log("count=", count3);
}
// tutaj zmienna [count3] jest znana
count3++;

Komentarze

  • wiersz 5: zmienna [count] jest znana tylko w bloku kodu, w którym została zadeklarowana (wiersze 3–7);
  • wiersz 14: stała [count2] jest znana tylko w bloku kodu, w którym została zadeklarowana (wiersze 12–16);
  • wiersz 23: zmienna [count3] jest znana poza blokiem kodu, w którym została zadeklarowana (wiersze 21–25);

ESLint zgłasza następujące błędy:

Image

Z uwagi na zasięg blokowy będziemy dalej używać wyłącznie słów kluczowych [let] i [const].

3.7. skrypt [bases-07]

Typy danych w JavaScript:


'use strict';

// typ danych jS
const var1 = 10;
const var2 = "abc";
const var3 = true;
const var4 = [1, 2, 3];
const var5 = {
  nom: 'axèle'
};
const var6 = function () {
  return +3;
}
// wyświetlanie typów
console.log("typeof(var1)=", typeof (var1));
console.log("typeof(var2)=", typeof (var2));
console.log("typeof(var3)=", typeof (var3));
console.log("typeof(var4)=", typeof (var4));
console.log("typeof(var5)=", typeof (var5));
console.log("typeof(var6)=", typeof (var6));

Wykonanie


[Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe "c:\Temp\19-09-01\javascript\bases\bases-07.js"
typeof(var1)= number
typeof(var2)= string
typeof(var3)= boolean
typeof(var4)= object
typeof(var5)= object
typeof(var6)= function

[Done] exited with code=0 in 0.26 seconds

Komentarze

  • wiersz 7 (kod): tablica jest obiektem. W związku z tym [var4] jest wskaźnikiem do tablicy, a nie samą tablicą;
  • wiersz 8 (kod): [var5] jest wskaźnikiem do obiektu literałowego. Zobaczymy, że obiekty literałowe w JavaScript bardzo przypominają instancje klasy PHP. One również są odwołane za pomocą wskaźników;
  • wiersz 11 (kod): zmienna może być typu [fonction] (wiersz 7 wyników);

3.8. skrypt [bases-08]

Ten skrypt pokazuje możliwe zmiany typów w JavaScript.


'use strict';

// domyślne zmiany typów
// typ -->bool
console.log("---------------[Conversion implicite vers un booléen]------------------------------");
showBool("abcd");
showBool("");
showBool([1, 2, 3]);
showBool([]);
showBool(null);
showBool(0.0);
showBool(0);
showBool(4.6);
showBool({});
showBool(undefined);

function showBool(data) {
  // konwersja danych na wartość logiczną odbywa się automatycznie w poniższym teście
  console.log("[data=", data, "], [type(data)]=", typeof (data), "[valeur booléenne(data)]=", data ? true : false);
}

// domyślne zmiany typu na typ li czbowy
console.log("---------------[Conversion implicite vers un nombre]------------------------------");
showNumber("12");
showNumber("45.67");
showNumber("abcd");

function showNumber(data) {
  // dane + 1 nie działa, ponieważ wówczas jS wykonuje konkatenację ciągów znaków zamiast dodawania
  const nombre = data * 1;
  console.log("[data=", data, "], [type(data)]=", typeof (data), "[nombre]=", nombre, "[type(nombre)]=", typeof (nombre));
}

// jawne zmiany typu na wartość logiczną
console.log("---------------[Conversion explicite vers un booléen]------------------------------");
showBool2("abcd");
showBool2("");
showBool2([1, 2, 3]);
showBool2([]);
showBool2(null);
showBool2(0.0);
showBool2(0);
showBool2(4.6);
showBool2({});
showBool2(undefined);

function showBool2(data) {
  // konwersja danych na wartość logiczną odbywa się jawnie w następującym po niej teście
  console.log("[", data, "], [type(data)]=", typeof (data), "[valeur booléenne(data)]=", Boolean(data));
}
// jawną zmianę typu na liczbę
console.log("---------------[Conversion explicite vers un nombre]------------------------------");
showNumber2("12.45");
showNumber2(67.8);
showNumber2(true);
showNumber2(null);

function showNumber2(data) {
  const nombre = Number(data);
  console.log("[data=", data, "], [type(data)]=", typeof (data), "[nombre]=", nombre, "[type(nombre)]=", typeof (nombre));
}

// na typ String
console.log("---------------[Conversion explicite vers un string]------------------------------");
showString(5);
showString(6.7);
showString(false);
showString(null);

function showString(data) {
  const chaîne = String(data);
  console.log("[data=", data, "], [type(data)]=", typeof (data), "[chaîne]=", chaîne, "[type(chaîne)]=", typeof (chaîne));
}

// kilka nieoczekiwanych konwersji domyślnych
console.log("---------------[Autres cas]------------------------------");
const string1 = '1000.78';
// domyślne łączenie ciągów znaków
const data1 = string1 + 1.034;
console.log("data1=", data1, "type=", typeof (data1));
const data2 = 1.034 + string1;
console.log("data2=", data2, "type=", typeof (data2));
// jawna konwersja na liczbę
const data3 = Number(string1) + 1.034;
console.log("data3=", data3, "type=", typeof (data3));
// wartość true jest konwertowana na liczbę 1
const data4 = true * 1.18;
console.log("data4=", data4, "type=", typeof (data4));
// wartość „false” jest konwertowana na liczbę 0
const data5 = false * 1.18;
console.log("data5=", data5, "type=", typeof (data5));

Wykonanie


[Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe "c:\Data\st-2019\dev\es6\javascript\bases\bases-08.js"
---------------[Conversion implicite vers un booléen]------------------------------
[data= abcd ], [type(data)]= string [valeur booléenne(data)]= true
[data= ], [type(data)]= string [valeur booléenne(data)]= false
[data= [ 1, 2, 3 ] ], [type(data)]= object [valeur booléenne(data)]= true
[data= [] ], [type(data)]= object [valeur booléenne(data)]= true
[data= null ], [type(data)]= object [valeur booléenne(data)]= false
[data= 0 ], [type(data)]= number [valeur booléenne(data)]= false
[data= 0 ], [type(data)]= number [valeur booléenne(data)]= false
[data= 4.6 ], [type(data)]= number [valeur booléenne(data)]= true
[data= {} ], [type(data)]= object [valeur booléenne(data)]= true
[data= undefined ], [type(data)]= undefined [valeur booléenne(data)]= false
---------------[Conversion implicite vers un nombre]------------------------------
[data= 12 ], [type(data)]= string [nombre]= 12 [type(nombre)]= number
[data= 45.67 ], [type(data)]= string [nombre]= 45.67 [type(nombre)]= number
[data= abcd ], [type(data)]= string [nombre]= NaN [type(nombre)]= number
---------------[Conversion explicite vers un booléen]------------------------------
[ abcd ], [type(data)]= string [valeur booléenne(data)]= true
[ ], [type(data)]= string [valeur booléenne(data)]= false
[ [ 1, 2, 3 ] ], [type(data)]= object [valeur booléenne(data)]= true
[ [] ], [type(data)]= object [valeur booléenne(data)]= true
[ null ], [type(data)]= object [valeur booléenne(data)]= false
[ 0 ], [type(data)]= number [valeur booléenne(data)]= false
[ 0 ], [type(data)]= number [valeur booléenne(data)]= false
[ 4.6 ], [type(data)]= number [valeur booléenne(data)]= true
[ {} ], [type(data)]= object [valeur booléenne(data)]= true
[ undefined ], [type(data)]= undefined [valeur booléenne(data)]= false
---------------[Conversion explicite vers un nombre]------------------------------
[data= 12.45 ], [type(data)]= string [nombre]= 12.45 [type(nombre)]= number
[data= 67.8 ], [type(data)]= number [nombre]= 67.8 [type(nombre)]= number
[data= true ], [type(data)]= boolean [nombre]= 1 [type(nombre)]= number
[data= null ], [type(data)]= object [nombre]= 0 [type(nombre)]= number
---------------[Conversion explicite vers un string]------------------------------
[data= 5 ], [type(data)]= number [chaîne]= 5 [type(chaîne)]= string
[data= 6.7 ], [type(data)]= number [chaîne]= 6.7 [type(chaîne)]= string
[data= false ], [type(data)]= boolean [chaîne]= false [type(chaîne)]= string
[data= null ], [type(data)]= object [chaîne]= null [type(chaîne)]= string
---------------[Autres cas]------------------------------
data1= 1000.781.034 type= string
data2= 1.0341000.78 type= string
data3= 1001.814 type= number
data4= 1.18 type= number
data5= 0 type= number