Cyfroteka.pl

klikaj i czytaj online

Cyfro
Czytomierz
00085 010871 7463736 na godz. na dobę w sumie
Tajniki języka JavaScript. ECMAScript 6 i dalej - ebook/pdf
Tajniki języka JavaScript. ECMAScript 6 i dalej - ebook/pdf
Autor: Liczba stron: 240
Wydawca: Helion Język publikacji: polski
ISBN: 978-83-283-2312-4 Data wydania:
Lektor:
Kategoria: ebooki >> komputery i informatyka >> webmasterstwo >> javascript - programowanie
Porównaj ceny (książka, ebook (-20%), audiobook).
Na pozór JavaScript jest prostym językiem o atrakcyjnych możliwościach. Jego złożone wewnętrzne mechanizmy muszą jednak zostać dokładnie przestudiowane, aby poczucie prawdziwego zrozumienia języka nie okazało się złudne. Zrozumienie subtelności JS jest o tyle ważne, że język ten ewoluuje. Najnowszy standard ECMAScript 6 to gwałtowny skok w przód i ogromna zmiana jakościowa, którą programista JS musi bardzo dobrze poznać!

Niniejsza książka jest częścią serii w całości poświęconej temu językowi. Przed lekturą warto poznać koncepcje opisane w poprzednich książkach tej serii, gdyż w tym tomie autor koncentruje się na nowych możliwościach standardu ES6, m.in. na nowych formach składniowych, różnorodnych formach organizacji kodu czy wspomagających interfejsach API. Szczególny nacisk położono na trudniejsze aspekty języka JavaScript, których wielu programistów unika lub w ogóle nie zna.

W tej książce przedstawiono:

Dowiedz się, jaki będzie JavaScript przyszłości!


Kyle Simpson — programista, propagator Open Web, wielki pasjonat języka JavaScript. Pisze książki, prowadzi warsztaty, występuje na konferencjach o tematyce technicznej oraz pozostaje aktywnym członkiem społeczności OSS.
Znajdź podobne książki

Darmowy fragment publikacji:

Tytuł oryginału: You Don t Know JS: ES6 Beyond Tłumaczenie: Piotr Rajca ISBN: 978-83-283-2311-7 © 2016 Helion SA Authorized Polish translation of the English edition You Don t Know JS: ES6 Beyond ISBN 9781491904244 © 2016 Getify Solutions, Inc. This translation is published and sold by permission of O’Reilly Media, Inc., which owns or controls all rights to publish and sell the same. All rights reserved. No part of this book may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording or by any information storage retrieval system, without permission from the Publisher. Wszelkie prawa zastrzeżone. Nieautoryzowane rozpowszechnianie całości lub fragmentu niniejszej publikacji w jakiejkolwiek postaci jest zabronione. Wykonywanie kopii metodą kserograficzną, fotograficzną, a także kopiowanie książki na nośniku filmowym, magnetycznym lub innym powoduje naruszenie praw autorskich niniejszej publikacji. Wszystkie znaki występujące w tekście są zastrzeżonymi znakami firmowymi bądź towarowymi ich właścicieli. Autor oraz Wydawnictwo HELION dołożyli wszelkich starań, by zawarte w tej książce informacje były kompletne i rzetelne. Nie biorą jednak żadnej odpowiedzialności ani za ich wykorzystanie, ani za związane z tym ewentualne naruszenie praw patentowych lub autorskich. Autor oraz Wydawnictwo HELION nie ponoszą również żadnej odpowiedzialności za ewentualne szkody wynikłe z wykorzystania informacji zawartych w książce. Wydawnictwo HELION ul. Kościuszki 1c, 44-100 GLIWICE tel. 32 231 22 19, 32 230 98 63 e-mail: helion@helion.pl WWW: http://helion.pl (księgarnia internetowa, katalog książek) Drogi Czytelniku! Jeżeli chcesz ocenić tę książkę, zajrzyj pod adres http://helion.pl/user/opinie/tajnjs Możesz tam wpisać swoje uwagi, spostrzeżenia, recenzję. Printed in Poland. • Kup książkę • Poleć książkę • Oceń książkę • Księgarnia internetowa • Lubię to! » Nasza społeczność Spis treści Przedmowa .......................................................................................................................5 Wstęp ...............................................................................................................................7 1. ES? Teraźniejszość i przyszłość ..........................................................................................11 12 13 14 15 Wersje języka Transpilacja — transformacja kodu Biblioteki typu shim i polyfill Podsumowanie 2. Składnia ..........................................................................................................................17 17 24 26 30 44 51 57 62 65 73 74 79 84 Deklaracje zakresu bloku Rozproszenie (reszta) Domyślne wartości parametrów Destrukturyzacja Rozszerzenia literałów obiektowych Literały szablonów Funkcje typu arrow function Pętle for .. of Wyrażenia regularne Rozszerzenia literałów liczbowych Unicode Symbole Podsumowanie 3. Organizacja .....................................................................................................................85 85 95 110 126 136 Iteratory Generatory Moduły Klasy Podsumowanie 3 Poleć książkęKup książkę 4. Asynchroniczne sterowanie przepływem ........................................................................137 137 143 146 Obietnice Generatory i obietnice Podsumowanie 5. Kolekcje .........................................................................................................................147 147 152 156 157 158 159 Tablice określonego typu Mapy Mapy typu WeakMap Zbiory Zbiory typu WeakSet Podsumowanie 6. Modyfikacje API .............................................................................................................161 161 170 173 175 178 179 Array Object Math Number String Podsumowanie 7. Metaprogramowanie .....................................................................................................181 181 185 190 202 206 208 215 Nazwy funkcji Dobrze znane symbole Obiekty pośredniczące — Proxy Interfejs API obiektu Reflect Testowanie możliwości Optymalizacja TCO Podsumowanie 8. Dalszy rozwój języka po ES6 ...........................................................................................217 218 221 224 224 225 226 227 229 Funkcje asynchroniczne Object.observe(..) Operator potęgi Właściwości obiektów i operator ... Array#includes(..) SIMD WebAssembly (WASM) Podsumowanie A Podziękowania ..............................................................................................................231 Skorowidz .....................................................................................................................235 4 (cid:95) Spis treści Poleć książkęKup książkę ROZDZIAŁ 5. Kolekcje Kolekcje oraz dostęp do danych o określonej strukturze to kluczowe elementy niemal wszystkich pro- gramów pisanych w języku JavaScript. Od samego początku istnienia języka aż do chwili obecnej podstawowymi mechanizmami używanymi do tworzenia struktur danych są tablice i obiekty. Oczywi- ście, powstawały także biblioteki, które korzystając z tablic i obiektów, implementowały struktury danych wyższego poziomu. Jednak w ES6 niektóre najbardziej przydatne (i zoptymalizowane pod względem wydajności działa- nia!) spośród tych abstrakcji danych zostały dodane jako macierzyste elementy języka. Zaczniemy od przedstawienia tablic określonego typu (ang. TypedArrays), rozwiązania wprowadzo- nego kilka lat temu wraz z udostępnieniem wersji ES5, lecz zestandaryzowanego wyłącznie jako dodatek do WebGL, a nie do samego języka JavaScript. W przypadku ES6 tablice określonego typu zo- stały dodane bezpośrednio do specyfikacji języka, co nadało im najwyższy status. Mapy przypominają nieco obiekty (pary klucz-wartość), jednak w ich przypadku kluczem może być nie tylko łańcuch znaków, lecz dowolna wartość — nawet inny obiekt lub mapa! Zbiory są podobne do tablic (list wartości), ale umieszczane w nich wartości są unikalne; w przypadku próby dodania duplikatu zostanie on pominięty. Istnieją także „słabe” (pod względem odzyskiwania i oczyszczania pamięci) odpowiedniki tych dwóch struktur danych: WeakMap oraz WeakSet. Tablice określonego typu Zgodnie z informacjami podanymi w książce Tajniki języka JavaScript. Typy i gramatyka, należącej do tej serii wydawniczej, język JavaScript posiada zestaw wbudowanych typów danych, takich jak number oraz string. Kuszące byłoby przypuszczenie, że termin „tablice określonego typu” oznacza tablicę wartości pewnego konkretnego typu, na przykład: tablicę zawierającą wyłącznie łańcuchy znaków. Jednak w przypadku tablic określonego typu chodzi raczej o zapewnienie strukturalnego dostępu do danych binarnych przy wykorzystaniu składni charakterystycznej dla tablic (dostępu z użyciem indeksów itd.). Słowo „typ” w nazwie tego mechanizmu odnosi się raczej do „widoku” prezentującego zbiory bitów, stanowiącego w zasadzie odwzorowanie określające, czy bity powinny być traktowane jako tablica 8-bitowych liczb całkowitych ze znakiem, czy też jako 16-bitowe liczby całkowite ze znakiem itd. 147 Poleć książkęKup książkę W jaki sposób tworzymy taki „zbiór bitów”. Jest on nazywany „buforem”, a najbardziej bezpośredni sposób jego tworzenia polega na użyciu konstruktora ArrayBuffer(..): var buf = new ArrayBuffer( 32 ); buf.byteLength; // 32 Po wykonaniu powyższych instrukcji zmienna buf będzie zawierać bufor binarny o długości 32 bajtów (czyli 256 bitów), który początkowo został wypełniony zerami. Sam bufor nie daje nam żadnych możliwości interakcji z jego zawartością, a jedyną operacją, jaką możemy na nim wykonać, jest sprawdzenie jego wielkości przy użyciu właściwości byteLength. Kilka mechanizmów różnych platform internetowych korzysta z takich buforów lub je zwraca; należą do nich: FileReader#readAsArrayBuffer(..), XMLHttpRequest# (cid:180)send(..) oraz ImageData (dane płócien). Jednak na taki bufor możemy następnie nałożyć „widok”, którym jest właśnie tablica określonego typu. Przyjrzyjmy się poniższemu przykładowi: var arr = new Uint16Array( buf ); arr.length; // 16 Zmienna arr jest teraz tablicą określonego typu, prezentującą zawartość 256-bitowego bufora buff jako 16-bitowe liczby całkowite bez znaku, co oznacza, że będzie ona miała 16 elementów. Kolejność zapisu bajtów Koniecznie należy zrozumieć i zapamiętać, że tablica arr jest odwzorowywana z wykorzystaniem określonej kolejności zapisu bajtów (big-endian lub little-endian), odpowiadających ustawieniom platformy, na której działa środowisko JavaScript. Ten fakt może być problemem, gdy dane binarne zostaną przygotowane z użyciem innej kolejności zapisu bajtów, lecz muszą być interpretowane na platformie, w której kolejność zapisu bajtów jest przeciwna. Oba terminy (big-endian oraz little-endian) określają, czy najmniej znaczący bajt (kolekcja 8 bitów) wielobajtowej liczby — takiej jak 16-bitowe liczby bez znaku, których użyliśmy w ostatnim przykła- dzie — zostanie zapisany z prawej, czy z lewej strony sekwencji bajtów reprezentujących liczbę. Wyobraźmy sobie liczbę dziesiętną 3085, która musi być reprezentowana przy użyciu 16 bitów. Gdyby taka liczba miała być reprezentowana przy użyciu jednego, 16-bitowego pojemnika, to nie- zależnie od kolejności zapisu miałaby dwójkową postać 0000110000001101 (lub 0c0d szesnastkowo). Gdybyśmy jednak chcieli zapisać ją jako dwie liczby 8-bitowe, to zastosowana kolejność zapisu baj- tów miałaby ogromne znaczenie dla postaci tej liczby umieszczonej w pamięci: (cid:120) 0000110000001101 / 0c0d (w przypadku stosowania zapisu big-endian), (cid:120) 0000110100001100 / 0d0c (w przypadku stosowania zapisu little-endian). Jeśli pobraliśmy bity 0000110100001100 reprezentujące liczbę 3085 w systemie korzystającym z zapisu little-endian, lecz spróbujemy ich użyć w systemie korzystającym z zapisu big-endian, to okaże się, że uzyskamy liczbę 3340 (w systemie dziesiętnym) lub 0d0c (w systemie szesnastkowym). 148 (cid:95) Rozdział 5. Kolekcje Poleć książkęKup książkę Zapis little-endian jest obecnie najczęściej używaną w internecie reprezentacją danych, choć oczywi- ście istnieją przeglądarki, w których nie jest on stosowany. Bardzo ważne jest, by zrozumieć, że zna- czenie ma sposób zapisu danych binarnych używany zarówno przez ich producenta, jak i przez konsumenta. Oto prosta metoda sprawdzania używanej kolejności zapisu danych, przedstawiona na witrynie MDN: var littleEndian = (function() { var buffer = new ArrayBuffer( 2 ); new DataView( buffer ).setInt16( 0, 256, true ); return new Int16Array( buffer )[0] === 256; })(); Zmienna littleEndian będzie przyjmować wartości true lub false, w większości przeglądarek powinna mieć wartość true. Powyższy kod korzysta z konstruktora DataView(..) zapewniającego najwyższy i najdokładniejszy poziom kontroli nad dostępnością (ustawianiem i pobieraniem) bitów z widoku utworzonego na bazie bufora. Trzeci parametr metody setInt16(..) zastosowa- nej w powyższym kodzie informuje DataView, jaka kolejność zapisu danych ma być zastosowana w wykonywanej operacji. Nie należy mylić kolejności zapisu danych binarnych przechowywanych w buforze tablicy ze sposobem reprezentacji danej liczby po udostępnieniu jej w programie JavaScript. Na przykład wywołanie (3085).toString(2) zwróci łańcuch znaków 110000001101 , który przy pominięciu czterech początkowych 0 wydaje się repre- zentacją używającą zapisu little-endian. W rzeczywistości reprezentacja ta bazuje na pojedynczym widoku 16-bitowym, a nie na widoku dwóch bajtów (po 8 bitów każdy). Powyższe rozwiązanie stanowi najlepszy sposób sprawdzenia kolejności zapisu bajtów stosowanej przez używane środowisko. Wiele widoków Do jednego bufora można dołączyć wiele różnych widoków. Oto przykład: var buf = new ArrayBuffer( 2 ); var view8 = new Uint8Array( buf ); var view16 = new Uint16Array( buf ); view16[0] = 3085; view8[0]; // 13 view8[1]; // 12 view8[0].toString( 16 ); // d view8[1].toString( 16 ); // c // zamiana (jakby zmiana kolejno(cid:286)ci zapisu!) var tmp = view8[0]; view8[0] = view8[1]; view8[1] = tmp; view16[0]; // 3340 Tablice określonego typu (cid:95) 149 Poleć książkęKup książkę Konstruktory tablic określonego typu mają wiele różnych sygnatur. Na razie przedstawione zostały tylko te, do których przekazywany jest sam bufor. Niemniej jednak ta forma konstruktorów umożliwia także podanie dwóch opcjonalnych parametrów: byteOffset oraz length. Oznacza to, że tworzony widok tablicy określonego typu może się zaczynać w innym miejscu niż wskazywane przez indeks 0 i może obejmować mniejszy zakres danych niż pełna długość bufora. Technika ta może się okazać całkiem przydatna, jeśli dane binarne umieszczone w buforze nie mają jednolitej wielkości bądź rozmieszczenia. Załóżmy, że dysponujemy binarnym buforem, w którym najpierw jest zapisana liczba składająca się z dwóch bajtów (tak zwane „słowo”), następnie dwie liczby jednobajtowe, a następnie liczba zmienno- przecinkowa o długości czterech bajtów. Poniższy przykład pokazuje, w jaki sposób można pobrać te wszystkie dane, korzystając z różnych widoków bazujących na tym samym buforze, lecz używają- cych różnych przesunięć oraz długości: var first = new Uint16Array( buf, 0, 2 )[0], second = new Uint8Array( buf, 2, 1 )[0], third = new Uint8Array( buf, 3, 1 )[0], fourth = new Float32Array( buf, 4, 4 )[0]; Konstruktory tablic określonego typu Oprócz formy z parametrami (buffer, [offset, [length]]), przedstawionej w poprzednich przy- kładach, dostępne są także inne postaci konstruktorów tablic określonego typu: (cid:120) [konstruktor](length) — tworzy nowy widok na bazie nowego bufora o długości length. (cid:120) [konstruktor](typedArr) — tworzy nowy widok oraz nowy bufor, kopiując do niego zawartość przekazanego widoku typedArr. (cid:120) [konstruktor](obj) — tworzy nowy widok oraz nowy bufor, do którego zostają skopiowane dane pobrane podczas przeglądania obiektu lub obiektu przypominającego tablicę. Na poniższej liście przedstawiłem wszystkie konstruktory tablic określonego typu dostępne w ję- zyku ES6: (cid:120) Int8Array (8-bitowe liczby całkowite ze znakiem), Uint8Array (8-bitowe liczby całkowite bez znaku), (cid:120) Uint8ClampedArray (8-bitowe liczby całkowite bez znaku, przy czym podczas inicjalizacji każda wartość jest ograniczana do zakresu od 0 do 255), (cid:120) Int16Array (16-bitowe liczby całkowite ze znakiem), Uint16Array (16-bitowe liczby całkowite bez znaku), (cid:120) Int32Array (32-bitowe liczby całkowite ze znakiem), Uint32Array (32-bitowe liczby całkowite bez znaku), (cid:120) Float32Array (32-bitowe liczby zmiennoprzecinkowe, IEEE-754), (cid:120) Float64Array (64-bitowe liczby zmiennoprzecinkowe, IEEE-754). Tablice określonego typu tworzone przy użyciu tych konstruktorów są niemal takie same jak zwyczaj- ne, macierzyste tablice języka JavaScript. Różnica pomiędzy nimi polega na tym, że tablice określonego typu mają stałą, niezmienną długość, a ich zawartość stanowią wartości tego samego „typu”. 150 (cid:95) Rozdział 5. Kolekcje Poleć książkęKup książkę Poza tymi rozbieżnościami oba rodzaje tablic dysponują tymi samymi metodami udostępnianymi przez ich konstruktory. Z tego względu zapewne będziemy mogli używać ich jako zwyczajnych tablic bez konieczności przeprowadzania jakichkolwiek konwersji. Oto przykład: var a = new Int32Array( 3 ); a[0] = 10; a[1] = 20; a[2] = 30; a.map( function(v){ console.log( v ); } ); // 10 20 30 a.join( - ); // 10-20-30 Podczas korzystania z tablic określonego typu nie można używać niektórych metod Array.prototype; chodzi o metody, których zastosowanie nie ma w danym przypadku sensu, takie jak mutatory (splice(..), push(..) itd.) oraz metoda concat(..). Trzeba pamiętać, że wielkość elementów tablic określonego typu jest ograniczona przez zadeklaro- wany typ tablicy. Jeśli używamy tablicy Uint8Array i spróbujemy zapisać w jednym z jej elemen- tów daną, której wielkość przekracza 8 bitów, to zostanie ona przycięta tak, by liczba bajtów zapi- sywanej wartości odpowiadała zadeklarowanej wielkości elementów tablicy. W niektórych przypadkach, na przykład w razie próby podniesienia do kwadratu liczb zapisanych w tablicy określonego typu, może to być przyczyną problemów. Przeanalizujmy następujący przykład: var a = new Uint8Array( 3 ); a[0] = 10; a[1] = 20; a[2] = 30; var b = a.map( function(v){ return v * v; } ); b; // [100, 144, 132] Podniesienie do kwadratu wartości 20 oraz 30 powoduje wystąpienie przepełnienia bitowego. Ogra- niczenie to można ominąć, korzystając z funkcji TypedArray#from(..): var a = new Uint8Array( 3 ); a[0] = 10; a[1] = 20; a[2] = 30; var b = Uint16Array.from( a, function(v){ return v * v; } ); b; // [100, 400, 900] Tablice określonego typu (cid:95) 151 Poleć książkęKup książkę Więcej informacji na temat funkcji Array.from(..), dostępnej także w tablicach określonego typu, można znaleźć w rozdziale 6., w punkcie „Funkcja statyczna Array.from(..)”. W podpunkcie „Od- wzorowania” opisana została funkcja odwzorowująca, którą można przekazać jako drugi argument funkcji Array.from(..). Jedną z interesujących cech tablic określonego typu jest udostępnianie metody sort(..), analogicz- nej do metody dostępnej w normalnych tablicach języka JavaScript. Jednak w przypadku tablic określonego typu metoda ta domyślnie porównuje liczby, a nie konwertuje wartości do postaci łańcu- chowej w celu wykonania porównania alfabetycznego. Oto przykład zastosowania obu tych metod: var a = [ 10, 1, 2, ]; a.sort(); // [1,10,2] var b = new Uint8Array( [ 10, 1, 2 ] ); b.sort(); // [1,2,10] Metoda TypedArray#sort(..) dysponuje także drugim, opcjonalnym parametrem — można go użyć do przekazania funkcji porównującej, która działa dokładnie tak samo jak funkcje porównujące sto- sowane w metodzie Array#sort(..). Mapy Jeśli masz duże doświadczenie w programowaniu w języku JavaScript, to zapewne wiesz, że obiekty są podstawowym mechanizmem do tworzenia struktur danych zawierających nieuporządkowane pary klucz-wartość, nazywanych także mapami. Niemniej jednak podstawową wadą związaną z tworzeniem map przy wykorzystaniu obiektów jest to, że kluczami obiektów mogą być wyłącznie łańcuchy znaków. Przeanalizujmy następujący przykład: var m = {}; var x = { id: 1 }, y = { id: 2 }; m[x] = foo ; m[y] = bar ; m[x]; // bar m[y]; // bar Co się tu dzieje? Otóż oba obiekty, x i y, zostają przekształcone do postaci łańcucha [object Object] , dlatego w obiekcie m zostanie utworzony tylko jeden klucz. Niektórzy implementowali fałszywe mapy, tworząc dwie tablice, z których jedna zawierała klucze niebędące łańcuchami znaków, a druga — wartości. Oto przykład takiego rozwiązania: var keys = [], vals = []; var x = { id: 1 }, y = { id: 2 }; keys.push( x ); vals.push( foo ); 152 (cid:95) Rozdział 5. Kolekcje Poleć książkęKup książkę keys.push( y ); vals.push( bar ); keys[0] === x; // true vals[0]; // foo keys[1] === y; // true vals[1]; // bar Oczywiście nikt nie chciałby własnoręcznie zarządzać takimi równoległymi tablicami, dlatego za- pewne warto by zdefiniować strukturę danych, której metody ukrywałyby szczegóły implementa- cyjne związane z zarządzaniem tymi tablicami. Oprócz konieczności zaimplementowania takiego rozwiązania jego kolejną podstawową wadą jest to, że złożoność działania takiej struktury danych nie jest już liniowa — O(1) — lecz wynosi O(n). Na szczęście w języku ES6 nie ma już potrzeby stosowania takich rozwiązań! Wystarczy utworzyć mapę — Map(..): var m = new Map(); var x = { id: 1 }, y = { id: 2 }; m.set( x, foo ); m.set( y, bar ); m.get( x ); // foo m.get( y ); // bar Jedyną wadą jest tu brak możliwości ustawiania i pobierania wartości mapy przy użyciu zapisu z nawiasami kwadratowymi ([ ]), charakterystycznego dla tablic. Zamiast tego operacje te trzeba wykonywać odpowiednio przy użyciu metod set(..) i get(..). Do usuwania elementów mapy nie należy używać operatora delete, lecz metody delete(..): m.set( x, foo ); m.set( y, bar ); m.delete( y ); Całą zawartość mapy można usunąć przy użyciu metody clear(). Z kolei długość mapy (czyli liczbę jej kluczy) można pobrać, używając właściwości size (nie length): m.set( x, foo ); m.set( y, bar ); m.size; // 2 m.clear(); m.size; // 0 W wywołaniu konstruktora Map(..) można także przekazać obiekt iterowalny (więcej informacji na temat takich obiektów podałem w rozdziale 3., w podrozdziale „Iteratory”), który musi zwracać li- stę tablic, przy czym pierwszy element każdej tablicy jest kluczem, a drugi — skojarzoną z nim wartością. Ten format iteracji idealnie odpowiada efektom działania metody entries(), opisanej w następnym punkcie rozdziału. Dzięki temu bardzo łatwo można tworzyć kopie map: Mapy (cid:95) 153 Poleć książkęKup książkę var m2 = new Map( m.entries() ); // ma ten sam efekt co: var m2 = new Map( m ); Ponieważ instancja mapy jest obiektem iterowalnym, a jej domyślny iterator działa tak samo jak metoda entries(), to preferowany jest drugi, krótszy sposób wywoływania konstruktora. Oczywiście istnieje także możliwość ręcznego przekazania do konstruktora Map(..) listy elemen- tów mapy (czyli tablicy dwuelementowych tablic zawierających pary klucz-wartość). Takie roz- wiązanie przedstawia poniższy przykład: var x = { id: 1 }, y = { id: 2 }; var m = new Map( [ [ x, foo ], [ y, bar ] ] ); m.get( x ); // foo m.get( y ); // bar Wartości map W celu pobrania wartości mapy należy wywołać metodę values(..), która zwraca iterator. W roz- działach 2. i 3. poznaliśmy różne metody sekwencyjnego przetwarzania iteratorów (czyli podob- nego do sposobów korzystania z tablic), na przykład z użyciem operatora ... lub pętli for .. of. Informacje na temat metody Array.from(..) można także znaleźć w rozdziale 6., w punkcie „Tworzenie tablic i podtypów”. Przeanalizujmy poniższy przykład: var m = new Map(); var x = { id: 1 }, y = { id: 2 }; m.set( x, foo ); m.set( y, bar ); var vals = [ ...m.values() ]; vals; // [ foo , bar ] Array.from( m.values() ); // [ foo , bar ] Zgodnie z informacjami podanymi w poprzednim rozdziale, elementy mapy można przejrzeć, korzystając z metody entries() (bądź domyślnego iteratora mapy). Oto przykład wykorzystania tej metody: var m = new Map(); var x = { id: 1 }, y = { id: 2 }; m.set( x, foo ); m.set( y, bar ); var vals = [ ...m.entries() ]; 154 (cid:95) Rozdział 5. Kolekcje Poleć książkęKup książkę vals[0][0] === x; // true vals[0][1]; // foo vals[1][0] === y; // true vals[1][1]; // bar Klucze map W celu pobrania wartości mapy należy wywołać metodę keys(..), która zwraca iterator udostęp- niający wszystkie klucze mapy: var m = new Map(); var x = { id: 1 }, y = { id: 2 }; m.set( x, foo ); m.set( y, bar ); var keys = [ ...m.keys() ]; keys[0] === x; // true keys[1] === y; // true Z kolei metoda has(..) pozwala sprawdzić, czy mapa zawiera konkretny klucz: var m = new Map(); var x = { id: 1 }, y = { id: 2 }; m.set( x, foo ); m.has( x ); // true m.has( y ); // false W gruncie rzeczy mapy pozwalają nam skojarzyć obiektem (kluczem) jakąś informację (wartość) bez faktycznego zapisywania tej informacji w danym obiekcie. Choć kluczami map mogą być dowolne wartości, to jednak zazwyczaj będą nimi obiekty, gdyż łań- cuchy znaków oraz wartości innych typów prostych mogą być stosowane jako klucze zwyczajnych obiektów. Innymi słowy, najprawdopodobniej wciąż będziemy używali zwyczajnych obiektów za- miast map, wyjąwszy sytuacje, gdy niektóre lub wszystkie klucze obiektu także muszą być obiektami — zastosowanie mapy będzie wówczas lepszym rozwiązaniem. Jeśli użyjemy obiektu jako klucza mapy, a następnie obiekt ten zostanie porzucony (zostaną usunięte wszystkie odwołania do niego) w ramach próby odzyskania zajmo- wanej przez niego pamięci, to element mapy nie zostanie usunięty. W celu umożli- wienia odzyskania pamięci zajmowanej przez element mapy trzeba go z niej jawnie usunąć. W następnym punkcie, poświęconym mapom typu WeakMap, poznamy lepsze rozwiązanie, jeśli chodzi o stosowanie obiektów jako kluczy i odzyskiwanie pamięci. Mapy (cid:95) 155 Poleć książkęKup książkę Mapy typu WeakMap Tak zwane „słabe mapy”, WeakMap, to specjalny rodzaj map, który dysponuje w zasadzie takim samym zewnętrznym zachowaniem jak normalne mapy, lecz różni się od nich pod względem wewnętrznego sposobu przydzielania pamięci (a zwłaszcza możliwości jej odzyskiwania). W przypadku map typu WeakMap kluczami mogą być wyłącznie obiekty. Obiekty te są przechowywane w słaby sposób, co oznacza, że w przypadku ich usunięcia przez mechanizm odzyskiwania pamięci (GC) usunięty zostanie także cały element mapy. Tego działania nie można jednak zaobserwo- wać, gdyż jedynym sposobem, by mechanizm odzyskiwania pamięci usunął obiekt, jest usunięcie wszystkich referencji do tego obiektu — to z kolei oznacza, że w programie nie będzie żadnych referencji, które pozwoliłyby sprawdzić, czy dany obiekt istnieje w mapie, czy nie. Pod wszelkimi innymi względami interfejs API mapy typu WeakMap w dużym stopniu przypomina interfejs zwyczajnych map, choć jest nieco bardziej ograniczony: var m = new WeakMap(); var x = { id: 1 }, y = { id: 2 }; m.set( x, foo ); m.has( x ); // true m.has( y ); // false Mapy typu WeakMap nie udostępniają właściwości size, metody clear() ani iteratorów pozwalających na przeglądanie ich kluczy, wartości oraz elementów. A zatem nawet jeśli usuniemy referencję x, powodując tym samym usunięcie skojarzonego z nią elementu mapy m podczas następnego cyklu odzyskiwania pamięci, to i tak nie będziemy mogli się o tym przekonać. Pozostanie nam jedynie wierzyć na słowo! Mapy typu WeakMap, podobnie jak zwyczajne mapy, pozwalają na swoiste kojarzenie informacji z obiektami. Są one szczególnie przydatne, kiedy nie dysponujemy całkowitą kontrolą nad używa- nymi obiektami, na przykład gdy są nimi elementy DOM. A zatem, jeśli obiekty używane jako klucze map mogą być usuwane i zajmowana przez nie pamięć powinna zostać zwolniona w ramach procesu odzyskiwania pamięci (GC), to zastosowanie mapy typu WeakMap będzie optymalnym rozwiązaniem. Koniecznie należy zapamiętać, że w przypadku map typu WeakMap jedynie klucze są przechowy- wane w „słaby” sposób — nie dotyczy to wartości mapy. Przeanalizujmy poniższy przykład: var m = new WeakMap(); var x = { id: 1 }, y = { id: 2 }, z = { id: 3 }, w = { id: 4 }; m.set( x, y ); x = null; // { id: 1 } mo(cid:298)e zosta(cid:252) usuni(cid:266)ty przez GC y = null; // { id: 2 } mo(cid:298)e zosta(cid:252) usuni(cid:266)ty przez GC // ale tylko dlatego, (cid:298)e dotyczy to tak(cid:298)e obiektu { id: 1 } 156 (cid:95) Rozdział 5. Kolekcje Poleć książkęKup książkę m.set( z, w ); w = null; // { id: 4 } nie mo(cid:298)e zosta(cid:252) usuni(cid:266)ty przez GC Właśnie z tego powodu uważam, że odpowiedniejszą nazwą dla map tego typu byłoby WeakKeyMap — „mapy o słabych kluczach”. Zbiory Zbiory są kolekcjami unikalnych wartości (wszelkie duplikaty są pomijane). Interfejs API zbiorów przypomina interfejs map. Zamiast metody set(..) zbiory używają metody add(..) (co można by potraktować jako swoisty żart), a poza tym nie udostępniają metody get(..). Przeanalizujmy poniższy przykład: var s = new Set(); var x = { id: 1 }, y = { id: 2 }; s.add( x ); s.add( y ); s.add( x ); s.size; // 2 s.delete( y ); s.size; // 1 s.clear(); s.size; // 0 Konstruktor Set(..) przypomina nieco konstruktor Map(..), gdyż tak jak w przypadku map, można do niego przekazać daną iterowalną, taką jak inny zbiór lub zwyczajna tablica wartości. Jednak w odróżnieniu od konstruktora Map(..), do którego należy przekazać listę elementów (ta- blicę tablic reprezentujących pary klucz-wartość), konstruktor Set(..) oczekuje przekazania listy wartości (tablicy wartości): var x = { id: 1 }, y = { id: 2 }; var s = new Set( [x,y] ); Zbiory nie potrzebują metody get(..), gdyż w ich przypadku nie wykonujemy operacji pobiera- nia wartości ze zbioru, a jedynie sprawdzamy, czy konkretna wartość w nim występuje: var s = new Set(); var x = { id: 1 }, y = { id: 2 }; s.add( x ); s.has( x ); // true s.has( y ); // false Zbiory (cid:95) 157 Poleć książkęKup książkę Algorytm porównywania używany przez metodę has(..) jest niemal identyczny z algorytmem stosowanym przez funkcję Object.is(..) (informacje na jego temat można znaleźć w rozdziale 6.), a jedyna różnica polega na tym, że wartości -0 i 0 są traktowane jako identyczne, a nie różne. Iteratory zbiorów Zbiory udostępniają te same metody zwracające iteratory co mapy. W przypadku zbiorów działanie tych metod jest nieco inne, choć analogiczne do działania iteratorów map. Przeanalizujmy nastę- pujący przykład: var s = new Set(); var x = { id: 1 }, y = { id: 2 }; s.add( x ).add( y ); var keys = [ ...s.keys() ], vals = [ ...s.values() ], entries = [ ...s.entries() ]; keys[0] === x; keys[1] === y; vals[0] === x; vals[1] === y; entries[0][0] === x; entries[0][1] === x; entries[1][0] === y; entries[1][1] === y; Iteratory keys() oraz values() zwracają listę wszystkich unikalnych wartości zbioru. Z kolei iterator entries() zwraca listę tablic reprezentujących elementy zbioru, przy czym oba elementy tych ta- blic są unikalną wartością zbioru. Domyślnym iteratorem zbiorów jest values(). Najużyteczniejszą cechą zbiorów jest charakterystyczna dla nich unikalność. Przeanalizujmy po- niższy przykład: var s = new Set( [1,2,3,4, 1 ,2,4, 5 ] ), uniques = [ ...s ]; uniques; // [1,2,3,4, 1 , 5 ] Mechanizm określania unikalności wartości zapisywanych w zbiorze nie korzysta z konwersji typów, dlatego 1 i 1 są traktowane jako odrębne, unikalne wartości. Zbiory typu WeakSet W odróżnieniu od map typu WeakMap, które w „słaby” sposób przechowują klucze (wartości są nato- miast przechowywane „mocno”), zbiory typu WeakSet w „słaby” sposób przechowują wartości (klucze w zasadzie nie są w nich stosowane). 158 (cid:95) Rozdział 5. Kolekcje Poleć książkęKup książkę var s = new WeakSet(); var x = { id: 1 }, y = { id: 2 }; s.add( x ); s.add( y ); x = null; // obiekt x mo(cid:298)e zosta(cid:252) usuni(cid:266)ty przez GC y = null; // obiekt y mo(cid:298)e zosta(cid:252) usuni(cid:266)ty przez GC Wartościami zbiorów typu WeakSet muszą być obiekty — w odróżnieniu od zwyczaj- nych zbiorów zapisywanie w nich wartości typów prostych nie jest dozwolone. Podsumowanie W języku ES6 dodanych zostało kilka użytecznych kolekcji, dzięki którym operowanie na danych w strukturalny sposób stało się prostsze i wydajniejsze. Tablice określonego typu tworzą „widoki” buforów zawierających dane binarne, dzięki którym dane te są prezentowane jako wartości różnych typów liczbowych, na przykład: 8-bitowe liczby całko- wite bez znaku lub 32-bitowe liczby zmiennoprzecinkowe. Możliwość dostępu do danych binar- nych z użyciem zapisu tablicowego sprawia, że wykonywane operacje można zapisać w znacznie prostszej postaci, ułatwiając tym samym pielęgnację kodu oraz korzystanie ze złożonych danych, takich jak dane audiowizualne, dane płócien itd. Mapy są kolekcjami para klucz-wartość, przy czym pozwalają, by kluczami były także obiekty, a nie tylko wartości typów prostych i łańcuchy znaków. Zbiory są listami unikalnych wartości (dowolnego typu). Mapy typu WeakMap przechowują swoje klucze (obiekty) w słaby sposób, dzięki czemu mechanizm odzyskiwania pamięci może usunąć element mapy, jeśli to ona zawiera ostatnią referencję do danego klucza. Z kolei zbiory typu WeakSet w podobny — słaby — sposób przechowują swoje wartości, dzięki czemu, jeśli w zbiorze jest przechowywana ostatnia referencja do obiektu, to odpowiadający mu element zbioru może zostać usunięty przez mechanizm odzyskiwania pamięci. Podsumowanie (cid:95) 159 Poleć książkęKup książkę 160 (cid:95) Rozdział 5. Kolekcje Poleć książkęKup książkę Skorowidz A E AMD, Asynchronous Module Definition, 111 API, 161 asynchroniczne sterowanie przepływem, 137 eksport domyślny, 115 nazwany, 114 B biblioteka FeatureTests.io, 208 biblioteki typu polyfill, 14 typu shim, 14 błąd tymczasowej martwej strefy, 20 błędy, 106 C D CommonJS, 113 deklaracja const, 22 let, 18, 21 deklaracje zakresu bloku, 17 delegacja zwracania wartości, 99 destrukturyzacja, 30, 41 obiektów, 31 parametrów, 38 tablicy, 31 zagnieżdżona, 38 domyślne wartości parametrów, 26 dopasowywanie globalne, 70 eksportowanie interfejsów API, 113 ES5, 11 ES6, 217 F flaga sticky, 67 Unicode, 66 funkcja Array.from(..), 162, 164 Array.of(..), 161 indexOf(..), 225 Number.isFinite(..), 176 Number.isNaN(..), 175 Object.assign(..), 172 Object.getOwnPropertySymbols(..), 171 Object.is(..), 170 Object.observe(..), 221 Object.setPrototypeOf(..), 171 Object.unobserve(..), 224 repeat(..), 179 String.raw(..), 178 Symbol.species(..), 135 funkcje asynchroniczne, 218 matematyczne, 173 o zakresie bloku, 23 sprawdzania łańcuchów znaków, 179 235 Poleć książkęKup książkę funkcje statyczne, 170, 175 statyczne związane z liczbami całkowitymi, 176 typu arrow function, 57, 63 związane z Unicode, 178 Ł łańcuch, 199 magiczny znaków, 82 znaków nieprzetworzony, 57 G generatory, 95, 143 generowanie sekwencji wartości, 109 I import przestrzeni nazw, 120 importowanie interfejsów API, 118 interfejs, 86 API, 113, 202 API obietnic, 141 iteracje, 87 iteratorResult, 86 iteratory, 85 iteratory niestandardowe, 90 zbiorów, 158 jawność, 21 J K klasy, 126 klucze map, 155 kolejność właściwości, 204 zapisu bajtów, 148 kolekcje, 147 konstruktory klas pochodnych, 131 tablic, 150 kontrola iteratora, 96, 100 L literały liczbowe, 73 szablonów, 51 szablonów ze znacznikiem, 54 236 (cid:95) Skorowidz M mapy, 152 klucze, 155 typu WeakMap, 156 wartości, 154 Math, 173 metaprogramowanie, 181 metawłaściwość, 133, 184 metoda array#includes(..), 225 copyWithin(..), 166 entries(), 169 fill(..), 167 find(..), 167 findIndex(..), 168 keys(), 169 next(), 87 notifier.notify(..), 223 Promise.reject(..), 142 then(..), 139 values(), 169 metody opcjonalne, 88 prototypu, 166, 169 statyczne, 134 typu getter, 48 typu setter, 48 zwięzłe, 44 moduł sposoby wczytywania, 126 wczytywanie, 124 moduły, 110 modyfikacje API, 161 N nazwy funkcji, 181 identyfikatorów, 79 new.target, 133 Number, 175 Poleć książkęKup książkę O R obiekt Math, 173 Number, 175 Object, 170 Reflect, 202 String, 178 obiekty obsługi, 191 pośredniczące, 190 jako ostatnie, 195 jako pierwsze, 195 ograniczenia, 194 z możliwością unieważnienia, 194 zastosowania, 195 thenable, 140 obietnice, 137, 143 obliczane nazwy właściwości, 49 obsługa błędów, 106 odwzorowania, 164 ograniczenia obiektów pośredniczących, 194 OLOO, 11 operator potęgi, 224 optymalizacja, 211 TCO, 208, 213 P parametr, 26 pętla for .. of, 62 iteratora, 89 pozycja ogonowa, 210 pozycjonowanie w trybie sticky, 68 znaków, 77 Proxy, 190 przepisywanie wywołania ogonowego, 210 przetwarzane literały łańcuchowe, 52 przypisania powtarzane, 35 przypisanie destrukturyzacji, 35 wartości domyślnej, 37 właściwości obiektu, 31 pułapki, 190 puste komórki, 164 ramka stosu, 208 rejestr symboli, 81 rekurencja, 212 restrukturyzacja, 41 reszta, 24 rozproszenie, 24 rozszerzanie obiektów, 132 rozszerzenia literałów liczbowych, 73 obiektowych, 44 rozwijanie rekurencji, 212 S SIMD, 226 singleton, 81 składnia, 17 słowo kluczowe class, 126 extends, 128 super, 51, 128 yield, 97 specyfikator modułu, 119 sprytne łańcuchy, 52 stała, 22 standard ES5, 11 sterowanie przepływem, 137 stosowanie const, 23 iteratorów, 94 obiektów pośredniczących, 195 obietnic, 138 String, 178 strukturalne przypisanie, 30 Symbol.hasInstance, 186 Symbol.isConcatSpreadable, 189 Symbol.iterator, 185 Symbol.species, 187 Symbol.toPrimitive, 187 Symbol.toStringTag, 186 Symbol.unscopables, 190 symbole, 79, 83, 185 wbudowane, 83 wyrażeń regularnych, 188 Skorowidz (cid:95) 237 Poleć książkęKup książkę wersje języka, 12 wiązanie this, 60 widok, 149 właściwości obiektów, 224 statyczne, 175 zwięzłe, 44 wnioskowanie, 183 wyrażenia, 28 interpolowane, 53 regularne, 65 wyrażenie yield, 96 yield*, 99 wywołania ogonowe, 209, 210 wzorzec przypisania, 31 Z zakotwiczenie, 71 zakres jawny bloku, 17, 19 wyrażeń, 54 zależności cykliczne, 123 pomiędzy modułami, 123 zapis bajtów, 148 zastosowania generatorów, 109 zbiory, 157 typu WeakSet, 158 zdarzenia niestandardowe zmian, 222 znacznik, 55 znaki diakrytyczne, 76 Unicode, 74 T tablice określonego typu, 147 TDZ, Temporal Dead Zone, 20 testowanie możliwości, 206 trampolina, 211 transformacja kodu, 13 transpilacja, 13 generatora, 108 tryb sticky, 70, 71 tworzenie kolejki zadań, 110 obietnic, 138 podtypów, 165 tablic, 165 tymczasowa martwa strefa, TDZ, 20 U Unicode, 74 ustawienie [[Prototype]], 50 W wartości domyślne destrukturyzacji, 40 domyślne parametrów, 40 domyślne zagnieżdżone, 41 map, 154 parametrów, 26 wartość NaN, 226 WASM, 227 wczesne przerwanie, 105 zakończenie, 103 wczytywanie modułów, 124 WebAssembly, 227 238 (cid:95) Skorowidz Poleć książkęKup książkę
Pobierz darmowy fragment (pdf)

Gdzie kupić całą publikację:

Tajniki języka JavaScript. ECMAScript 6 i dalej
Autor:

Opinie na temat publikacji:


Inne popularne pozycje z tej kategorii:


Czytaj również:


Prowadzisz stronę lub blog? Wstaw link do fragmentu tej książki i współpracuj z Cyfroteką: