ECMAScript

ECMAScript
Pojawienie się

1997

Paradygmat

Język skryptowy wieloparadygmatowy (obiektowy, funkcyjny, imperatywny)

Typowanie

dynamiczne, Duck typing

Implementacje

JavaScript, SpiderMonkey, V8, JavaScriptCore, Chakra, Rhino, Carakan, KJS

Aktualna wersja stabilna

ECMAScript 2021[1] (czerwiec 2021)

Twórca

Brendan Eich, Ecma International

Platforma sprzętowa

wieloplatformowy

Strona internetowa

ECMAScript – stworzona przez ECMA ustandaryzowana specyfikacja obiektowego[2][3] języka programowania, której najbardziej znane implementacje to JavaScript, JScript i ActionScript. Specyfikacja ta oznaczona jest jako ECMA-262 i ISO/IEC 16262. Język początkowo był projektowany z myślą o pisaniu skryptów i przeniesieniu części zadań obliczeniowych na stronę klienta (przeglądarki internetowej) w architekturze klient-serwer[4], od tego czasu znacznie ewoluował i błędem byłoby nazywanie go językiem skryptowym, sami autorzy w specyfikacji nazywają go językiem programowania, który wyszedł poza skryptowanie i jest pełnoprawnym językiem programowania ogólnego zastosowania[5], wskazując na jego właściwości porównywalne do tych obecnych w Javie, Selfie, czy Scheme[6].

ECMA-262 definiuje samą semantykę języka oraz niektóre podstawowe typy danych (String, Boolean, Number, Object itp.) i obiekty (np. Math, Array). Elementy, takie jak obiektowy model dokumentu, specjalne funkcje wejścia-wyjścia, obsługa graficznego interfejsu użytkownika nie wchodzą w skład specyfikacji ECMAScript – definiowane są przez inne standardy (W3C DOM) lub samych autorów konkretnych implementacji.

Standard jest cały czas uaktualniany. Od 2015, co roku powstaje nowa wersja, którą zajmuje się komisja TC39, w skład której wchodzą przedstawiciele wszystkich głównych przeglądarek internetowych i kilkadziesiąt innych organizacji[7][8].

Wersje ECMAScript

Zaraz po powstaniu ES6 (6 lat od wersji ES5), TC39 zdecydowała się na zmianę nazewnictwa kolejnych wersji standardu. I tak wersja ES6, została nazwana ES2015. Kolejne to ES2017, ES2018 itd. Są to oficjalne nazwy wersji[9].

Więcej o różnych implementacjach i ich wersjach w artykule JavaScript.

Proces dodawania nowych funkcji do standardu opisującego JavaScript jest opisany w artykule TC39.

Wersje standardu ECMAScript
WersjaWydanaZmiany[10]
1czerwiec 1997
2czerwiec 1998Głównie korekta redaktorska[a]
3grudzień 1999Wyrażenia regularne, obsługa wyjątków, nowe rodzaje pętli, literały tablic i obiektów
4porzuconaKlasy, jawna kontrola dostępu, alternatywne dla DOM wbudowane wsparcie dla XML[b]
5 – ES5grudzień 2009Tryb „use strict”, obsługa JSON
6 – ES2015[11]czerwiec 2015Klasy, moduły, iterator for/of, kolekcje, tablice typowane, proxies (meta obiekty), wyjaśnia wiele niejednoznaczności z wersji 3
7 – ES2016[12]czerwiec 2016Operator potęgowy – **, Array.prototype.includes
8 – ES2017[13]czerwiec 2017funkcje async/await, String.prototype.padStart/padEnd, Object.values/entries
9 – ES2018[14]czerwiec 2018Asynchroniczne iteratory, Rest/Spread dla właściwości obiektów, nowe funkcje wyrażeń regularnych, funkcja finally dla obietnic
10 – ES2019[15]czerwiec 2019Dodanie metody spłaszczania tablic, zmiany w metodzie sortowania tablic i metodzie Object.fromEntries
11 – ES2020[16]czerwiec 2020Dodanie operatora nullowego scalania, operatora opcjonalnego dostępu, wprowadzono primityw BigInt
12 – ES2021[1]czerwiec 2021Możliwość używania separatorów liczbowych dla literałów liczbowych, możliwość sprawdzenia pustości lub braku określenia wartości zmiennej, funkcja any dla obietnic

Podstawowe elementy składni wersji 3

Komentarze

Komentarze w skryptach ECMAScriptu umieszcza się w sposób identyczny jak w C++. Komentarz blokowy umieszcza się między sekwencją znaków /* a */, komentarz liniowy rozpoczyna się sekwencją // a kończy znakiem końca linii:

/* To jest komentarz
  blokowy. Zajmuje on
  kilka linii */

// to jest komentarz liniowy

Zmienne

Zmienne są typowane dynamicznie. Definiowanie zmiennej polega na zwykłym przypisaniu jej wartości lub skorzystaniu z instrukcji var. Zmienne zdefiniowane poza funkcjami są dostępne w zasięgu globalnym (widoczne dla całego skryptu).

Tryb ścisły (strict mode) w ECMAScript 5 uniemożliwia przypisanie wartości do niezdefiniowanej zmiennej.

Instrukcje sterujące

Podstawowe instrukcje są identyczne z instrukcjami znanymi z języków Java i C++.

Instrukcja if

if (warunki) {
    instrukcje;
} else {
    instrukcje;
}

Pętla while

while (warunki) {
    instrukcje;
}

Pętla do...while

do {
    instrukcje
} while (warunki);

Pętla for

for ([instrukcje - początkowe];[warunki];[instrukcje - krokowe]) {
    instrukcje - środkowe;
}

Pętla for...in oraz for...of

Pętla ta przechodzi przez wszystkie pola danego obiektu (w tym elementy tablicy):

for (let własność in obiekt) {
    instrukcje;
}

Kolejność przechodzenia nie jest zdefiniowana w standardzie języka[17].

Pętla ta przechodzi przez wszystkie indeksy tablicy, pomijając właściwości nonenumerable:

for (let własność of obiekt) {
    instrukcje;
}

Jest to instrukcja, która nie występuje w Javie (stosowny odpowiednik pojawił się dopiero w wersji JDK 1.5) natomiast jest dostępna w Borland Delphi 2005. W PHP5 jej odpowiednikiem jest instrukcja foreach. W C++ można ją spotkać w trzech odmianach:

  • jako funkcję for_each wykorzystując funktor. Warto również zwrócić uwagę na możliwość wykorzystania for_each z bibliotekami boost::bind oraz boost::lambda
  • jako konstrukcję BOOST_FOREACH z biblioteki boost
  • jako pętla for bazująca na zasięgu (range-based for) wprowadzona w standardzie C++11.

Instrukcja switch

Analogicznie jak w C, C++, PHP i Javie.

switch (wyrażenie) {
    case wartość1:
        instrukcje;
        break;
    case wartość2:
        instrukcje;
        break;
    default:
        instrukcje;
        break;
}

Obiekty

W ECMAScripcie istnieją obiekty i typy prymitywne. Możliwe jest jednak użycie metod z odpowiedniej otoczki – w razie potrzeby dynamicznie jest tworzona otoczka odpowiedniego typu (np. String), która od razu po wykorzystaniu jest usuwana. Podstawowym obiektem jest Object. Standard ECMA opisuje także obiekty Array (tablica), String (ciąg znaków), Number (liczba całkowita lub rzeczywista), Boolean (wartość logiczna), Function (funkcja ECMAScriptu), Date (data) i Math (operacje matematyczne).

Dostęp do pól i metod

Obiekty ECMAScriptu są tablicami asocjacyjnymi. Dostęp do pól obiektów jest możliwy przy użyciu dwóch równoważnych notacji: obiekt.pole i obiekt["pole"].

Ponieważ metody obiektu (funkcje) są jego polami, także do nich dostęp jest możliwy przy użyciu zarówno notacji z kropką, jak i notacji z nawiasami kwadratowymi. Poniższe dwie linie kodu są zatem równoważne:

m.metoda1();
m["metoda1"]();

Obie notacje z nawiasami kwadratowymi zwyczajowo stosuje się jednak przy korzystaniu z tablic powstałych jako obiekt Array.

Dostęp do pól i metod obiektu ułatwiała instrukcja wiążąca "with". Nie jest ona jednak dozwolona w trybie ścisłym ECMAScript 5[18]. Poniższe konstrukcje są równoważne:

obiekt.pole1 = wartość;
obiekt.pole2 = wartość;

i

with (obiekt) {
    pole1 = wartość;
    pole2 = wartość;
}

Definiowanie własnego obiektu

Aby zdefiniować własny obiekt wystarczy utworzyć funkcję konstruktora:

// funkcja konstruktora
function MojObiekt(poleA, poleB) {
    this.poleA = poleA;
    this.poleB = poleB;

    this.metoda1 = function() {
        alert("mojObiekt::metoda1()");
    };

    this.metoda2 = function() {
        alert("mojObiekt::metoda2()");
    };
}

Dalsza część opisu mówi o "klasach", mimo że w przypadku ECMAScriptu pojęcie "klasy" jest nieformalne. "Klasa" oznacza tutaj zbiór obiektów utworzonych przy użyciu tego samego konstruktora.

Aby utworzyć instancję klasy MojObiekt, należy skorzystać z operatora new:

let m = new MojObiekt(2, 3);

Podobnie jak w Javie, nowe obiekty w ECMAScripcie tworzone są na stercie. W przypadku kiedy do danego obiektu nie istnieje już żadna referencja, mechanizm odśmiecania pamięci usuwa dany obiekt.

Funkcje

Funkcje w ECMAScripcie definiujemy przy użyciu słowa kluczowego function, a jej argumenty podajemy w nawiasach:

function dodajDwieLiczby(a, b) {
    return a + b;
}

Taki sam rezultat otrzymamy za pomocą:

var dodajDwieLiczby = function(a, b) {
    return a + b;
}

Funkcje w ECMAScript są obiektami, dzięki czemu istnieje możliwość zapisywania ich do zmiennych oraz stworzenia ich za pomocą konstruktora przyjmującego N argumentów: gdzie N-1 argumentów to argumenty dla funkcji a N to ciało funkcji. Należy pamiętać, że nie jest to zalecany sposób tworzenia funkcji i powinno się to traktować tylko jako ciekawostkę.

let dodajDwieLiczby = new Function("a", "b", "return a + b;");

Pamiętając ze funkcja jest obiektem, możemy manipulować funkcjami w trakcie trwania skryptu.

function dodaj(a, b) {
    alert(a + b);
}

function odejmij(a, b) {
    alert(a - b);
}

console.log(dodaj.name); // wyświetla "dodaj";
dodaj.name = odejmij.name;
console.log(dodaj.name); // wyświetla "odejmij";

Dziedziczenie

W implementacjach ECMAScript dziedziczenie realizowane jest przez prototypy. Jeśli chcemy utworzyć klasę Pochodna dziedziczącą po klasie Bazowa, ustawiamy pole Pochodna.prototype na nową instancję klasy Bazowa:

function Bazowa() {
    this.metodaA = function() {
        alert("Bazowa::A()");
    };

    this.metodaB = function() {
        alert("Bazowa::B()");
    };
}

function Pochodna() {
    // metodaB nadpisuje odpowiednią metodę z klasy Bazowa:
    this.metodaB = function() {
        alert("Pochodna::B()");
    };
}
Pochodna.prototype = new Bazowa();

x = new Bazowa();
y = new Pochodna();

x.metodaA(); // wyświetla: "Bazowa::A()"
y.metodaA(); // wyświetla: "Bazowa::A()"
x.metodaB(); // wyświetla: "Bazowa::B()"
y.metodaB(); // wyświetla: "Pochodna::B()"

Istnieją inne sposoby na uzyskanie dziedziczenia w ECMAScripcie. Osoby przyzwyczajone do Javy lub C++ mogą zmodyfikować prototyp obiektu Object w następujący sposób:

Object.prototype.extends = function(supClass) {
    tempObj = new supClass();
    for (let property in tempObj) {
        this[property] = tempObj[property];
    }
};

by później w funkcjach konstruktora korzystać z metody extends(), rozwiązującej szybko problem dziedziczenia. Oto przykład:

function Bazowa() {
    this.value = 5;

    this.metoda1 = function() {
        alert("Bazowa::metoda1() " + this.value);
    };

    this.metoda2 = function() {
        alert("Bazowa::metoda2()");
    };
}

function Pochodna() {
    this.extends(Bazowa);

    this.metoda1 = function() {
        alert("Pochodna::metoda1() " + this.value);
    };
}

Ważne tylko, żeby extends() wywoływać jako pierwszą instrukcję w konstruktorze.

Obsługa wyjątków

Do obsługi wyjątków w ECMAScripcie wykorzystywana jest "klasa" Error, odpowiadająca znanej z Javy klasie Exception.

Aby wyrzucić nowy wyjątek, należy skorzystać z instrukcji throw:

function zjedz() {
    throw new Error("Komunikat wyjątku");
}

Aby obsłużyć wyjątek, należy zastosować konstrukcję try...catch...finally (przy czym finally jest opcjonalne):

try {
    zjedz();
} catch (wyjatek) {
    alert("Nastąpił wyjątek: " + wyjatek.toString());
} finally {
    posprzataj();
}

// ...

throw("nastapil blad");

Uwagi

  1. Przyczyną były prace nad ustanowieniem ECMAScript 2 normą ISO 16262.
  2. Dotychczas opracowywane jako osobny standard ECMA-357.

Przypisy

  1. a b ECMAScript® 2021 Language Specification. 262.ecma-international.org. [dostęp 2021-08-07]. (ang.).
  2. ECMAScript® 2020 Language Specification
  3. ECMAScript® 2020 Language Specification
  4. From ECMAScript® 2020 Language Specification "ECMAScript was originally designed to be a Web scripting language, providing a mechanism to enliven Web pages in browsers and to perform server computation as part of a Web-based client-server architecture." https://262.ecma-international.org/11.0/#sec-overview
  5. From ECMAScript® 2020 Language Specification "ECMAScript usage has moved beyond simple scripting and it is now used for the full spectrum of programming tasks in many different environments and scales. As the usage of ECMAScript has expanded, so have the features and facilities it provides. ECMAScript is now a fully featured general-purpose programming language." https://262.ecma-international.org/11.0/#sec-overview
  6. From ECMAScript® 2020 Language Specification. "Some of the facilities of ECMAScript are similar to those used in other programming languages; in particular C, Java™, Self, and Scheme as described in: ISO/IEC 9899:1996, Programming Languages — C. Gosling, James, Bill Joy and Guy Steele. The Java Language Specification. Addison Wesley Publishing Co., 1996. Ungar, David, and Smith, Randall B. Self: The Power of Simplicity. OOPSLA '87 Conference Proceedings, pp. 227-241, Orlando, FL, October 1987. IEEE Standard for the Scheme Programming Language. IEEE Std 1178-1990." https://262.ecma-international.org/11.0/#sec-overview
  7. The TC39 process for ECMAScript features. 2ality.com.
  8. TC39-Royalty Free Technical Committee members, Ecma International [dostęp 2021-11-11] (ang.).
  9. ES5, ES6, ES2016, ES.Next: What's going on with JavaScript versioning?. benmccormick.org.
  10. JavaScript 2.0: Evolving a Language for Evolving Systems.
  11. ECMAScript 2015 Language Specification – ECMA-262 6th Edition, www.ecma-international.org [dostęp 2018-04-15] (ang.).
  12. ECMAScript® 2016 Language Specification, www.ecma-international.org [dostęp 2018-04-15].
  13. ECMAScript® 2017 Language Specification, www.ecma-international.org [dostęp 2018-04-15].
  14. ECMAScript® 2018 Language Specification, www.ecma-international.org [dostęp 2019-06-19].
  15. Standard ECMA-262. www.ecma-international.org. [dostęp 2016-12-29].
  16. ECMAScript® 2020 Language Specification.
  17. ECMAScript Language Specification
  18. Strict Mode – JavaScript MDN

Linki zewnętrzne