24

Tytuł oryginału: You Don't Know JS: ES6 & Beyondpdf.helion.pl/tajnjs/tajnjs.pdfzyku ES6: x Int8Array (8-bitowe liczby całkowite ze znakiem), Uint8Array (8-bitowe liczby całkowite

  • Upload
    others

  • View
    9

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Tytuł oryginału: You Don't Know JS: ES6 & Beyondpdf.helion.pl/tajnjs/tajnjs.pdfzyku ES6: x Int8Array (8-bitowe liczby całkowite ze znakiem), Uint8Array (8-bitowe liczby całkowite
Page 2: Tytuł oryginału: You Don't Know JS: ES6 & Beyondpdf.helion.pl/tajnjs/tajnjs.pdfzyku ES6: x Int8Array (8-bitowe liczby całkowite ze znakiem), Uint8Array (8-bitowe liczby całkowite

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 HELIONul. Kościuszki 1c, 44-100 GLIWICEtel. 32 231 22 19, 32 230 98 63e-mail: [email protected]: 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/tajnjsMoż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ść

Page 3: Tytuł oryginału: You Don't Know JS: ES6 & Beyondpdf.helion.pl/tajnjs/tajnjs.pdfzyku ES6: x Int8Array (8-bitowe liczby całkowite ze znakiem), Uint8Array (8-bitowe liczby całkowite

3

Spis treści

Przedmowa .......................................................................................................................5

Wstęp ...............................................................................................................................7

1. ES? Teraźniejszość i przyszłość .......................................................................................... 11Wersje języka 12Transpilacja — transformacja kodu 13Biblioteki typu shim i polyfill 14Podsumowanie 15

2. Składnia .......................................................................................................................... 17Deklaracje zakresu bloku 17Rozproszenie (reszta) 24Domyślne wartości parametrów 26Destrukturyzacja 30Rozszerzenia literałów obiektowych 44Literały szablonów 51Funkcje typu arrow function 57Pętle for .. of 62Wyrażenia regularne 65Rozszerzenia literałów liczbowych 73Unicode 74Symbole 79Podsumowanie 84

3. Organizacja ..................................................................................................................... 85Iteratory 85Generatory 95Moduły 110Klasy 126Podsumowanie 136

Poleć książkęKup książkę

Page 4: Tytuł oryginału: You Don't Know JS: ES6 & Beyondpdf.helion.pl/tajnjs/tajnjs.pdfzyku ES6: x Int8Array (8-bitowe liczby całkowite ze znakiem), Uint8Array (8-bitowe liczby całkowite

4 Spis treści

4. Asynchroniczne sterowanie przepływem ........................................................................137Obietnice 137Generatory i obietnice 143Podsumowanie 146

5. Kolekcje .........................................................................................................................147Tablice określonego typu 147Mapy 152Mapy typu WeakMap 156Zbiory 157Zbiory typu WeakSet 158Podsumowanie 159

6. Modyfikacje API .............................................................................................................161Array 161Object 170Math 173Number 175String 178Podsumowanie 179

7. Metaprogramowanie .....................................................................................................181Nazwy funkcji 181Dobrze znane symbole 185Obiekty pośredniczące — Proxy 190Interfejs API obiektu Reflect 202Testowanie możliwości 206Optymalizacja TCO 208Podsumowanie 215

8. Dalszy rozwój języka po ES6 ...........................................................................................217Funkcje asynchroniczne 218Object.observe(..) 221Operator potęgi 224Właściwości obiektów i operator ... 224Array#includes(..) 225SIMD 226WebAssembly (WASM) 227Podsumowanie 229

A Podziękowania ..............................................................................................................231

Skorowidz .....................................................................................................................235

Poleć książkęKup książkę

Page 5: Tytuł oryginału: You Don't Know JS: ES6 & Beyondpdf.helion.pl/tajnjs/tajnjs.pdfzyku ES6: x Int8Array (8-bitowe liczby całkowite ze znakiem), Uint8Array (8-bitowe liczby całkowite

147

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 obecnejpodstawowymi 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 strukturydanych 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 jakododatek 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ą podobnedo tablic (list wartości), ale umieszczane w nich wartości są unikalne; w przypadku próby dodaniaduplikatu zostanie on pominięty. Istnieją także „słabe” (pod względem odzyskiwania i oczyszczaniapamięci) odpowiedniki tych dwóch struktur danych: WeakMap oraz WeakSet.

Tablice określonego typuZgodnie z informacjami podanymi w książce Tajniki języka JavaScript. Typy i gramatyka, należącej dotej serii wydawniczej, język JavaScript posiada zestaw wbudowanych typów danych, takich jak numberoraz 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 dodanych binarnych przy wykorzystaniu składni charakterystycznej dla tablic (dostępu z użyciemindeksów itd.). Słowo „typ” w nazwie tego mechanizmu odnosi się raczej do „widoku” prezentującegozbiory bitów, stanowiącego w zasadzie odwzorowanie określające, czy bity powinny być traktowanejako tablica 8-bitowych liczb całkowitych ze znakiem, czy też jako 16-bitowe liczby całkowite zeznakiem itd.

Poleć książkęKup książkę

Page 6: Tytuł oryginału: You Don't Know JS: ES6 & Beyondpdf.helion.pl/tajnjs/tajnjs.pdfzyku ES6: x Int8Array (8-bitowe liczby całkowite ze znakiem), Uint8Array (8-bitowe liczby całkowite

148 Rozdział 5. Kolekcje

W jaki sposób tworzymy taki „zbiór bitów”. Jest on nazywany „buforem”, a najbardziej bezpośrednisposó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 żadnychmożliwości interakcji z jego zawartością, a jedyną operacją, jaką możemy na nim wykonać, jestsprawdzenie jego wielkości przy użyciu właściwości byteLength.

Kilka mechanizmów różnych platform internetowych korzysta z takich buforówlub je zwraca; należą do nich: FileReader#readAsArrayBuffer(..), XMLHttpRequest#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ślonegotypu. 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 buffjako 16-bitowe liczby całkowite bez znaku, co oznacza, że będzie ona miała 16 elementów.

Kolejność zapisu bajtówKoniecznie należy zrozumieć i zapamiętać, że tablica arr jest odwzorowywana z wykorzystaniemokreślonej kolejności zapisu bajtów (big-endian lub little-endian), odpowiadających ustawieniomplatformy, na której działa środowisko JavaScript. Ten fakt może być problemem, gdy dane binarnezostaną przygotowane z użyciem innej kolejności zapisu bajtów, lecz muszą być interpretowane naplatformie, 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:

0000110000001101 / 0c0d (w przypadku stosowania zapisu big-endian),

0000110100001100 / 0d0c (w przypadku stosowania zapisu little-endian).

Jeśli pobraliśmy bity 0000110100001100 reprezentujące liczbę 3085 w systemie korzystającym z zapisulittle-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).

Poleć książkęKup książkę

Page 7: Tytuł oryginału: You Don't Know JS: ES6 & Beyondpdf.helion.pl/tajnjs/tajnjs.pdfzyku ES6: x Int8Array (8-bitowe liczby całkowite ze znakiem), Uint8Array (8-bitowe liczby całkowite

Tablice określonego typu 149

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 przezkonsumenta.

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ądarekpowinna mieć wartość true. Powyższy kod korzysta z konstruktora DataView(..) zapewniającegonajwyż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ć zastosowanaw wykonywanej operacji.

Nie należy mylić kolejności zapisu danych binarnych przechowywanych w buforzetablicy ze sposobem reprezentacji danej liczby po udostępnieniu jej w programieJavaScript. 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 napojedynczym 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ówstosowanej przez używane środowisko.

Wiele widokówDo 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]; // 13view8[1]; // 12

view8[0].toString( 16 ); // "d"view8[1].toString( 16 ); // "c"

// zamiana (jakby zmiana kolejno ci zapisu!)var tmp = view8[0];view8[0] = view8[1];view8[1] = tmp;

view16[0]; // 3340

Poleć książkęKup książkę

Page 8: Tytuł oryginału: You Don't Know JS: ES6 & Beyondpdf.helion.pl/tajnjs/tajnjs.pdfzyku ES6: x Int8Array (8-bitowe liczby całkowite ze znakiem), Uint8Array (8-bitowe liczby całkowite

150 Rozdział 5. Kolekcje

Konstruktory tablic określonego typu mają wiele różnych sygnatur. Na razie przedstawione zostałytylko te, do których przekazywany jest sam bufor. Niemniej jednak ta forma konstruktorów umożliwiatakże podanie dwóch opcjonalnych parametrów: byteOffset oraz length. Oznacza to, że tworzonywidok tablicy określonego typu może się zaczynać w innym miejscu niż wskazywane przez indeks 0i 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 niemają 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 typuOpró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:

[konstruktor](length) — tworzy nowy widok na bazie nowego bufora o długości length.

[konstruktor](typedArr) — tworzy nowy widok oraz nowy bufor, kopiując do niego zawartośćprzekazanego widoku typedArr.

[konstruktor](obj) — tworzy nowy widok oraz nowy bufor, do którego zostają skopiowane danepobrane 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:

Int8Array (8-bitowe liczby całkowite ze znakiem), Uint8Array (8-bitowe liczby całkowite bezznaku),

Uint8ClampedArray (8-bitowe liczby całkowite bez znaku, przy czym podczas inicjalizacji każdawartość jest ograniczana do zakresu od 0 do 255),

Int16Array (16-bitowe liczby całkowite ze znakiem), Uint16Array (16-bitowe liczby całkowitebez znaku),

Int32Array (32-bitowe liczby całkowite ze znakiem), Uint32Array (32-bitowe liczby całkowitebez znaku),

Float32Array (32-bitowe liczby zmiennoprzecinkowe, IEEE-754),

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ślonegotypu mają stałą, niezmienną długość, a ich zawartość stanowią wartości tego samego „typu”.

Poleć książkęKup książkę

Page 9: Tytuł oryginału: You Don't Know JS: ES6 & Beyondpdf.helion.pl/tajnjs/tajnjs.pdfzyku ES6: x Int8Array (8-bitowe liczby całkowite ze znakiem), Uint8Array (8-bitowe liczby całkowite

Tablice określonego typu 151

Poza tymi rozbieżnościami oba rodzaje tablic dysponują tymi samymi metodami udostępnianymiprzez ich konstruktory. Z tego względu zapewne będziemy mogli używać ich jako zwyczajnychtablic 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 metodArray.prototype; chodzi o metody, których zastosowanie nie ma w danym przypadkusensu, 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 zapisanychw 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]

Poleć książkęKup książkę

Page 10: Tytuł oryginału: You Don't Know JS: ES6 & Beyondpdf.helion.pl/tajnjs/tajnjs.pdfzyku ES6: x Int8Array (8-bitowe liczby całkowite ze znakiem), Uint8Array (8-bitowe liczby całkowite

152 Rozdział 5. Kolekcje

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 argumentfunkcji 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 tablicokreś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(..).

MapyJeśli masz duże doświadczenie w programowaniu w języku JavaScript, to zapewne wiesz, że obiektysą podstawowym mechanizmem do tworzenia struktur danych zawierających nieuporządkowanepary 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 kluczeniebę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" );

Poleć książkęKup książkę

Page 11: Tytuł oryginału: You Don't Know JS: ES6 & Beyondpdf.helion.pl/tajnjs/tajnjs.pdfzyku ES6: x Int8Array (8-bitowe liczby całkowite ze znakiem), Uint8Array (8-bitowe liczby całkowite

Mapy 153

keys.push( y );vals.push( "bar" );

keys[0] === x; // truevals[0]; // "foo"

keys[1] === y; // truevals[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 takiegorozwiązania jego kolejną podstawową wadą jest to, że złożoność działania takiej struktury danychnie 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 zapisuz nawiasami kwadratowymi ([ ]), charakterystycznego dla tablic. Zamiast tego operacje te trzebawykonywać 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 (czyliliczbę 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 informacjina 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 nimwartością. Ten format iteracji idealnie odpowiada efektom działania metody entries(), opisanejw następnym punkcie rozdziału. Dzięki temu bardzo łatwo można tworzyć kopie map:

Poleć książkęKup książkę

Page 12: Tytuł oryginału: You Don't Know JS: ES6 & Beyondpdf.helion.pl/tajnjs/tajnjs.pdfzyku ES6: x Int8Array (8-bitowe liczby całkowite ze znakiem), Uint8Array (8-bitowe liczby całkowite

154 Rozdział 5. Kolekcje

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 jakmetoda 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 mapW 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 wykorzystaniatej metody:

var m = new Map();

var x = { id: 1 }, y = { id: 2 };

m.set( x, "foo" );m.set( y, "bar" );

var vals = [ ...m.entries() ];

Poleć książkęKup książkę

Page 13: Tytuł oryginału: You Don't Know JS: ES6 & Beyondpdf.helion.pl/tajnjs/tajnjs.pdfzyku ES6: x Int8Array (8-bitowe liczby całkowite ze znakiem), Uint8Array (8-bitowe liczby całkowite

Mapy 155

vals[0][0] === x; // truevals[0][1]; // "foo"

vals[1][0] === y; // truevals[1][1]; // "bar"

Klucze mapW 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; // truekeys[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 ); // truem.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 zwyczajnychobiektó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 jawnieusunąć. W następnym punkcie, poświęconym mapom typu WeakMap, poznamy lepszerozwiązanie, jeśli chodzi o stosowanie obiektów jako kluczy i odzyskiwanie pamięci.

Poleć książkęKup książkę

Page 14: Tytuł oryginału: You Don't Know JS: ES6 & Beyondpdf.helion.pl/tajnjs/tajnjs.pdfzyku ES6: x Int8Array (8-bitowe liczby całkowite ze znakiem), Uint8Array (8-bitowe liczby całkowite

156 Rozdział 5. Kolekcje

Mapy typu WeakMapTak zwane „słabe mapy”, WeakMap, to specjalny rodzaj map, który dysponuje w zasadzie takim samymzewnętrznym zachowaniem jak normalne mapy, lecz różni się od nich pod względem wewnętrznegosposobu 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ą przechowywanew 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ęciewszystkich referencji do tego obiektu — to z kolei oznacza, że w programie nie będzie żadnychreferencji, 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 przypominainterfejs 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 ); // truem.has( y ); // false

Mapy typu WeakMap nie udostępniają właściwości size, metody clear() ani iteratorów pozwalającychna 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 cykluodzyskiwania pamięci, to i tak nie będziemy mogli się o tym przekonać. Pozostanie nam jedyniewierzyć na słowo!

Mapy typu WeakMap, podobnie jak zwyczajne mapy, pozwalają na swoiste kojarzenie informacjiz 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 jakoklucze map mogą być usuwane i zajmowana przez nie pamięć powinna zostać zwolniona w ramachprocesu odzyskiwania pamięci (GC), to zastosowanie mapy typu WeakMap będzie optymalnymrozwią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 e zosta usuni ty przez GCy = null; // { id: 2 } mo e zosta usuni ty przez GC // ale tylko dlatego, e dotyczy to tak e obiektu { id: 1 }

Poleć książkęKup książkę

Page 15: Tytuł oryginału: You Don't Know JS: ES6 & Beyondpdf.helion.pl/tajnjs/tajnjs.pdfzyku ES6: x Int8Array (8-bitowe liczby całkowite ze znakiem), Uint8Array (8-bitowe liczby całkowite

Zbiory 157

m.set( z, w );w = null; // { id: 4 } nie mo e zosta usuni 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”.

ZbioryZbiory są kolekcjami unikalnych wartości (wszelkie duplikaty są pomijane).

Interfejs API zbiorów przypomina interfejs map. Zamiast metody set(..) zbiory używają metodyadd(..) (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 listywartoś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 ); // trues.has( y ); // false

Poleć książkęKup książkę

Page 16: Tytuł oryginału: You Don't Know JS: ES6 & Beyondpdf.helion.pl/tajnjs/tajnjs.pdfzyku ES6: x Int8Array (8-bitowe liczby całkowite ze znakiem), Uint8Array (8-bitowe liczby całkowite

158 Rozdział 5. Kolekcje

Algorytm porównywania używany przez metodę has(..) jest niemal identycznyz algorytmem stosowanym przez funkcję Object.is(..) (informacje na jego tematmoż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ówZbiory udostępniają te same metody zwracające iteratory co mapy. W przypadku zbiorów działanietych 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 iteratorentries() 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 WeakSetW 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).

Poleć książkęKup książkę

Page 17: Tytuł oryginału: You Don't Know JS: ES6 & Beyondpdf.helion.pl/tajnjs/tajnjs.pdfzyku ES6: x Int8Array (8-bitowe liczby całkowite ze znakiem), Uint8Array (8-bitowe liczby całkowite

Podsumowanie 159

var s = new WeakSet();

var x = { id: 1 }, y = { id: 2 };

s.add( x );s.add( y );

x = null; // obiekt 'x' mo e zosta usuni ty przez GCy = null; // obiekt 'y' mo e zosta usuni 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.

PodsumowanieW języku ES6 dodanych zostało kilka użytecznych kolekcji, dzięki którym operowanie na danychw strukturalny sposób stało się prostsze i wydajniejsze.

Tablice określonego typu tworzą „widoki” buforów zawierających dane binarne, dzięki którymdane 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 znacznieprostszej 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 nietylko wartości typów prostych i łańcuchy znaków. Zbiory są listami unikalnych wartości (dowolnegotypu).

Mapy typu WeakMap przechowują swoje klucze (obiekty) w słaby sposób, dzięki czemu mechanizmodzyskiwania pamięci może usunąć element mapy, jeśli to ona zawiera ostatnią referencję do danegoklucza. 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 muelement zbioru może zostać usunięty przez mechanizm odzyskiwania pamięci.

Poleć książkęKup książkę

Page 18: Tytuł oryginału: You Don't Know JS: ES6 & Beyondpdf.helion.pl/tajnjs/tajnjs.pdfzyku ES6: x Int8Array (8-bitowe liczby całkowite ze znakiem), Uint8Array (8-bitowe liczby całkowite

160 Rozdział 5. Kolekcje

Poleć książkęKup książkę

Page 19: Tytuł oryginału: You Don't Know JS: ES6 & Beyondpdf.helion.pl/tajnjs/tajnjs.pdfzyku ES6: x Int8Array (8-bitowe liczby całkowite ze znakiem), Uint8Array (8-bitowe liczby całkowite

235

Skorowidz

AAMD, Asynchronous Module Definition, 111API, 161asynchroniczne sterowanie przepływem, 137

Bbiblioteka FeatureTests.io, 208biblioteki

typu polyfill, 14typu shim, 14

błąd tymczasowej martwej strefy, 20błędy, 106

CCommonJS, 113

Ddeklaracja

const, 22let, 18, 21

deklaracje zakresu bloku, 17delegacja zwracania wartości, 99destrukturyzacja, 30, 41

obiektów, 31parametrów, 38tablicy, 31zagnieżdżona, 38

domyślne wartości parametrów, 26dopasowywanie globalne, 70

Eeksport

domyślny, 115nazwany, 114

eksportowanie interfejsów API, 113ES5, 11ES6, 217

Fflaga

sticky, 67Unicode, 66

funkcjaArray.from(..), 162, 164Array.of(..), 161indexOf(..), 225Number.isFinite(..), 176Number.isNaN(..), 175Object.assign(..), 172Object.getOwnPropertySymbols(..), 171Object.is(..), 170Object.observe(..), 221Object.setPrototypeOf(..), 171Object.unobserve(..), 224repeat(..), 179String.raw(..), 178Symbol.species(..), 135

funkcjeasynchroniczne, 218matematyczne, 173o zakresie bloku, 23sprawdzania łańcuchów znaków, 179

Poleć książkęKup książkę

Page 20: Tytuł oryginału: You Don't Know JS: ES6 & Beyondpdf.helion.pl/tajnjs/tajnjs.pdfzyku ES6: x Int8Array (8-bitowe liczby całkowite ze znakiem), Uint8Array (8-bitowe liczby całkowite

236 Skorowidz

funkcjestatyczne, 170, 175statyczne związane z liczbami całkowitymi, 176typu arrow function, 57, 63związane z Unicode, 178

Ggeneratory, 95, 143generowanie sekwencji wartości, 109

Iimport przestrzeni nazw, 120importowanie interfejsów API, 118interfejs, 86

API, 113, 202API obietnic, 141

iteracje, 87iteratorResult, 86iteratory, 85

iteratory niestandardowe, 90zbiorów, 158

Jjawność, 21

Kklasy, 126klucze map, 155kolejność

właściwości, 204zapisu bajtów, 148

kolekcje, 147konstruktory

klas pochodnych, 131tablic, 150

kontrola iteratora, 96, 100

Lliterały

liczbowe, 73szablonów, 51szablonów ze znacznikiem, 54

Łłańcuch, 199

magiczny znaków, 82znaków nieprzetworzony, 57

Mmapy, 152

klucze, 155typu WeakMap, 156wartości, 154

Math, 173metaprogramowanie, 181metawłaściwość, 133, 184metoda

array#includes(..), 225copyWithin(..), 166entries(), 169fill(..), 167find(..), 167findIndex(..), 168keys(), 169next(), 87notifier.notify(..), 223Promise.reject(..), 142then(..), 139values(), 169

metodyopcjonalne, 88prototypu, 166, 169statyczne, 134typu getter, 48typu setter, 48zwięzłe, 44

modułsposoby wczytywania, 126wczytywanie, 124

moduły, 110modyfikacje API, 161

Nnazwy

funkcji, 181identyfikatorów, 79

new.target, 133Number, 175

Poleć książkęKup książkę

Page 21: Tytuł oryginału: You Don't Know JS: ES6 & Beyondpdf.helion.pl/tajnjs/tajnjs.pdfzyku ES6: x Int8Array (8-bitowe liczby całkowite ze znakiem), Uint8Array (8-bitowe liczby całkowite

Skorowidz 237

Oobiekt

Math, 173Number, 175Object, 170Reflect, 202String, 178

obiektyobsługi, 191pośredniczące, 190

jako ostatnie, 195jako pierwsze, 195ograniczenia, 194z możliwością unieważnienia, 194zastosowania, 195

thenable, 140obietnice, 137, 143obliczane nazwy właściwości, 49obsługa błędów, 106odwzorowania, 164ograniczenia obiektów pośredniczących, 194OLOO, 11operator potęgi, 224optymalizacja, 211

TCO, 208, 213

Pparametr, 26pętla

for .. of, 62iteratora, 89

pozycja ogonowa, 210pozycjonowanie

w trybie sticky, 68znaków, 77

Proxy, 190przepisywanie wywołania ogonowego, 210przetwarzane literały łańcuchowe, 52przypisania powtarzane, 35przypisanie

destrukturyzacji, 35wartości domyślnej, 37właściwości obiektu, 31

pułapki, 190puste komórki, 164

Rramka stosu, 208rejestr symboli, 81rekurencja, 212restrukturyzacja, 41reszta, 24rozproszenie, 24rozszerzanie obiektów, 132rozszerzenia literałów

liczbowych, 73obiektowych, 44

rozwijanie rekurencji, 212

SSIMD, 226singleton, 81składnia, 17słowo kluczowe

class, 126extends, 128super, 51, 128yield, 97

specyfikator modułu, 119sprytne łańcuchy, 52stała, 22standard ES5, 11sterowanie przepływem, 137stosowanie

const, 23iteratorów, 94obiektów pośredniczących, 195obietnic, 138

String, 178strukturalne przypisanie, 30Symbol.hasInstance, 186Symbol.isConcatSpreadable, 189Symbol.iterator, 185Symbol.species, 187Symbol.toPrimitive, 187Symbol.toStringTag, 186Symbol.unscopables, 190symbole, 79, 83, 185

wbudowane, 83wyrażeń regularnych, 188

Poleć książkęKup książkę

Page 22: Tytuł oryginału: You Don't Know JS: ES6 & Beyondpdf.helion.pl/tajnjs/tajnjs.pdfzyku ES6: x Int8Array (8-bitowe liczby całkowite ze znakiem), Uint8Array (8-bitowe liczby całkowite

238 Skorowidz

Ttablice określonego typu, 147TDZ, Temporal Dead Zone, 20testowanie możliwości, 206trampolina, 211transformacja kodu, 13transpilacja, 13

generatora, 108tryb sticky, 70, 71tworzenie

kolejki zadań, 110obietnic, 138podtypów, 165tablic, 165

tymczasowa martwa strefa, TDZ, 20

UUnicode, 74ustawienie [[Prototype]], 50

Wwartości

domyślne destrukturyzacji, 40domyślne parametrów, 40domyślne zagnieżdżone, 41map, 154parametrów, 26

wartość NaN, 226WASM, 227wczesne

przerwanie, 105zakończenie, 103

wczytywanie modułów, 124WebAssembly, 227

wersje języka, 12wiązanie this, 60widok, 149właściwości

obiektów, 224statyczne, 175zwięzłe, 44

wnioskowanie, 183wyrażenia, 28

interpolowane, 53regularne, 65

wyrażenieyield, 96yield*, 99

wywołania ogonowe, 209, 210wzorzec przypisania, 31

Zzakotwiczenie, 71zakres

jawny bloku, 17, 19wyrażeń, 54

zależnościcykliczne, 123pomiędzy modułami, 123

zapis bajtów, 148zastosowania generatorów, 109zbiory, 157

typu WeakSet, 158zdarzenia niestandardowe zmian, 222znacznik, 55znaki

diakrytyczne, 76Unicode, 74

Poleć książkęKup książkę

Page 24: Tytuł oryginału: You Don't Know JS: ES6 & Beyondpdf.helion.pl/tajnjs/tajnjs.pdfzyku ES6: x Int8Array (8-bitowe liczby całkowite ze znakiem), Uint8Array (8-bitowe liczby całkowite