Pole

Objekty umožňují ukládat klíčované kolekce hodnot. To je v pořádku.

Dost často ale zjistíme, že potřebujeme objednanou kolekci , kde máme 1., 2., 3. prvek a tak dále. Potřebujeme to například k uložení seznamu něčeho:uživatelů, zboží, prvků HTML atd.

Zde není vhodné používat objekt, protože neposkytuje žádné metody pro správu pořadí prvků. Nemůžeme vložit novou vlastnost „mezi“ stávající. Objekty prostě nejsou určeny k takovému použití.

Existuje speciální datová struktura s názvem Array , k uložení objednaných kolekcí.

Prohlášení

Existují dvě syntaxe pro vytvoření prázdného pole:

let arr = new Array();
let arr = [];

Téměř po celou dobu se používá druhá syntaxe. Počáteční prvky můžeme dodat v závorkách:

let fruits = ["Apple", "Orange", "Plum"];

Prvky pole jsou očíslovány, počínaje nulou.

Prvek můžeme získat jeho číslem v hranatých závorkách:

let fruits = ["Apple", "Orange", "Plum"];

alert( fruits[0] ); // Apple
alert( fruits[1] ); // Orange
alert( fruits[2] ); // Plum

Můžeme nahradit prvek:

fruits[2] = 'Pear'; // now ["Apple", "Orange", "Pear"]

…Nebo přidejte do pole nový:

fruits[3] = 'Lemon'; // now ["Apple", "Orange", "Pear", "Lemon"]

Celkový počet prvků v poli je jeho length :

let fruits = ["Apple", "Orange", "Plum"];

alert( fruits.length ); // 3

Můžeme také použít alert zobrazíte celé pole.

let fruits = ["Apple", "Orange", "Plum"];

alert( fruits ); // Apple,Orange,Plum

Pole může ukládat prvky libovolného typu.

Například:

// mix of values
let arr = [ 'Apple', { name: 'John' }, true, function() { alert('hello'); } ];

// get the object at index 1 and then show its name
alert( arr[1].name ); // John

// get the function at index 3 and run it
arr[3](); // hello
Koncová čárka

Pole, stejně jako objekt, může končit čárkou:

let fruits = [
 "Apple",
 "Orange",
 "Plum",
];

Styl „koncová čárka“ usnadňuje vkládání/odebírání položek, protože všechny řádky jsou stejné.

Získejte poslední prvky pomocí „at“

Nedávný přírůstek Toto je nedávný přírůstek do jazyka. Staré prohlížeče mohou vyžadovat polyfilly.

Řekněme, že chceme poslední prvek pole.

Některé programovací jazyky umožňují používat záporné indexy pro stejný účel, jako je fruits[-1] .

I když v JavaScriptu to nebude fungovat. Výsledek bude undefined , protože index v hranatých závorkách je zpracován doslova.

Můžeme explicitně vypočítat index posledního prvku a poté k němu přistupovat:fruits[fruits.length - 1] .

let fruits = ["Apple", "Orange", "Plum"];

alert( fruits[fruits.length-1] ); // Plum

Trochu těžkopádné, že? Název proměnné musíme napsat dvakrát.

Naštěstí existuje kratší syntaxe:fruits.at(-1) :

let fruits = ["Apple", "Orange", "Plum"];

// same as fruits[fruits.length-1]
alert( fruits.at(-1) ); // Plum

Jinými slovy arr.at(i) :

  • je úplně stejný jako arr[i] , pokud i >= 0 .
  • pro záporné hodnoty i , ustoupí od konce pole.

Metody pop/push, shift/unshift

Fronta je jedním z nejběžnějších použití pole. V informatice to znamená uspořádanou kolekci prvků, která podporuje dvě operace:

  • push připojí prvek na konec.
  • shift získat prvek od začátku, posouvat frontu, takže 2. prvek se stává 1..

Pole podporují obě operace.

V praxi to potřebujeme velmi často. Například fronta zpráv, které je třeba zobrazit na obrazovce.

Existuje další případ použití pro pole – datová struktura s názvem stack.

Podporuje dvě operace:

  • push přidá prvek na konec.
  • pop přebírá prvek z konce.

Nové prvky se tedy přidávají nebo berou vždy od „konce“.

Hromada je obvykle znázorněna jako balíček karet:nové karty se přidávají nahoru nebo se berou shora:

U stacků je jako první přijata poslední vložená položka, čemuž se také říká princip LIFO (Last-In-First-Out). Pro fronty máme FIFO (First-In-First-Out).

Pole v JavaScriptu mohou fungovat jako fronta i jako zásobník. Umožňují vám přidávat/odebírat prvky, jak na začátek, tak na konec.

V informatice se datová struktura, která to umožňuje, nazývá deque.

Metody, které pracují s koncem pole:

pop

Extrahuje poslední prvek pole a vrátí jej:

let fruits = ["Apple", "Orange", "Pear"];

alert( fruits.pop() ); // remove "Pear" and alert it

alert( fruits ); // Apple, Orange

Oba fruits.pop() a fruits.at(-1) vrátí poslední prvek pole, ale fruits.pop() také upraví pole jeho odstraněním.

push

Připojte prvek na konec pole:

let fruits = ["Apple", "Orange"];

fruits.push("Pear");

alert( fruits ); // Apple, Orange, Pear

Volání fruits.push(...) se rovná fruits[fruits.length] = ... .

Metody, které pracují se začátkem pole:

shift

Extrahuje první prvek pole a vrátí jej:

let fruits = ["Apple", "Orange", "Pear"];

alert( fruits.shift() ); // remove Apple and alert it

alert( fruits ); // Orange, Pear
unshift

Přidejte prvek na začátek pole:

let fruits = ["Orange", "Pear"];

fruits.unshift('Apple');

alert( fruits ); // Apple, Orange, Pear

Metody push a unshift může přidat více prvků najednou:

let fruits = ["Apple"];

fruits.push("Orange", "Peach");
fruits.unshift("Pineapple", "Lemon");

// ["Pineapple", "Lemon", "Apple", "Orange", "Peach"]
alert( fruits );

Interní

Pole je zvláštní druh objektu. Hranaté závorky používané pro přístup ke vlastnosti arr[0] ve skutečnosti pocházejí ze syntaxe objektu. To je v podstatě stejné jako obj[key] , kde arr je objekt, zatímco čísla se používají jako klíče.

Rozšiřují objekty poskytující speciální metody pro práci s uspořádanými kolekcemi dat a také length vlastnictví. Ale v jádru je to stále objekt.

Pamatujte, že v JavaScriptu existuje pouze osm základních datových typů (další informace naleznete v kapitole Datové typy). Pole je objekt, a proto se chová jako objekt.

Například je zkopírován odkazem:

let fruits = ["Banana"]

let arr = fruits; // copy by reference (two variables reference the same array)

alert( arr === fruits ); // true

arr.push("Pear"); // modify the array by reference

alert( fruits ); // Banana, Pear - 2 items now

…Ale to, co dělá pole opravdu zvláštní, je jejich vnitřní reprezentace. Engine se snaží ukládat své prvky do souvislé oblasti paměti, jeden po druhém, jak je znázorněno na ilustracích v této kapitole, a existují i ​​další optimalizace, aby pole fungovala opravdu rychle.

Ale všechny se rozbijí, pokud přestaneme pracovat s polem jako s „uspořádanou kolekcí“ a začneme s ním pracovat, jako by to byl běžný objekt.

Technicky můžeme například udělat toto:

let fruits = []; // make an array

fruits[99999] = 5; // assign a property with the index far greater than its length

fruits.age = 25; // create a property with an arbitrary name

To je možné, protože pole jsou objekty ve své základně. Můžeme k nim přidat libovolné vlastnosti.

Ale engine uvidí, že pracujeme s polem jako s běžným objektem. Optimalizace specifické pro pole nejsou pro takové případy vhodné a budou vypnuty, jejich výhody zmizí.

Způsoby zneužití pole:

  • Přidejte nečíselnou vlastnost, například arr.test = 5 .
  • Udělejte díry, například:přidejte arr[0] a poté arr[1000] (a nic mezi nimi).
  • Vyplňte pole v opačném pořadí, například arr[1000] , arr[999] a tak dále.

Představte si prosím pole jako speciální struktury pro práci s uspořádanými daty . Poskytují k tomu speciální metody. Pole jsou pečlivě vyladěna uvnitř JavaScriptových enginů, aby pracovala se souvislými uspořádanými daty, používejte je prosím tímto způsobem. A pokud potřebujete libovolné klíče, je vysoká pravděpodobnost, že skutečně potřebujete běžný objekt {} .

Výkon

Metody push/pop běžet rychle, zatímco shift/unshift jsou pomalé.

Proč je rychlejší pracovat s koncem pole než s jeho začátkem? Podívejme se, co se stane během provádění:

fruits.shift(); // take 1 element from the start

Nestačí vzít a odstranit prvek s indexem 0 . Ostatní prvky je také třeba přečíslovat.

shift operace musí dělat 3 věci:

  1. Odstraňte prvek s indexem 0 .
  2. Přesunout všechny prvky doleva, přečíslovat je z indexu 1 na 0 , z 2 na 1 a tak dále.
  3. Aktualizujte length vlastnictví.

Čím více prvků v poli, tím více času na jejich přesun, více operací v paměti.

Podobná věc se stane s unshift :Chcete-li přidat prvek na začátek pole, musíme nejprve přesunout existující prvky doprava a zvýšit jejich indexy.

A co je s push/pop ? Nepotřebují nic přesouvat. Chcete-li extrahovat prvek z konce, pop metoda vyčistí index a zkrátí length .

Akce pro pop operace:

fruits.pop(); // take 1 element from the end

Číslo pop metoda nemusí nic přesouvat, protože ostatní prvky si uchovávají své indexy. Proto je neuvěřitelně rychlý.

Podobná věc s push metoda.

Smyčky

Jedním z nejstarších způsobů cyklování položek pole je for smyčka přes indexy:

let arr = ["Apple", "Orange", "Pear"];

for (let i = 0; i < arr.length; i++) {
 alert( arr[i] );
}

Ale pro pole existuje jiná forma smyčky, for..of :

let fruits = ["Apple", "Orange", "Plum"];

// iterates over array elements
for (let fruit of fruits) {
 alert( fruit );
}

for..of nedává přístup k číslu aktuálního prvku, pouze k jeho hodnotě, ale ve většině případů to stačí. A je kratší.

Technicky, protože pole jsou objekty, je také možné použít for..in :

let arr = ["Apple", "Orange", "Pear"];

for (let key in arr) {
 alert( arr[key] ); // Apple, Orange, Pear
}

Ale to je vlastně špatný nápad. Jsou s tím možné problémy:

  1. Smyčka for..in iteruje přes všechny vlastnosti , nejen číselné.

    V prohlížeči a v jiných prostředích jsou takzvané „array-like“ objekty, které vypadají jako pole . To znamená, že mají length a indexuje vlastnosti, ale mohou mít i jiné nenumerické vlastnosti a metody, které obvykle nepotřebujeme. for..in smyčka je však vypíše. Pokud tedy potřebujeme pracovat s objekty podobnými poli, mohou se tyto „extra“ vlastnosti stát problémem.

  2. for..in smyčka je optimalizována pro generické objekty, nikoli pole, a proto je 10-100krát pomalejší. Samozřejmě je to stále velmi rychlé. Na zrychlení může záležet pouze v úzkých hrdlech. Ale přesto bychom si měli být vědomi rozdílu.

Obecně bychom neměli používat for..in pro pole.

Něco o „délce“

length vlastnost se automaticky aktualizuje, když pole upravíme. Abychom byli přesní, ve skutečnosti to není počet hodnot v poli, ale největší číselný index plus jedna.

Například jeden prvek s velkým indexem dává velkou délku:

let fruits = [];
fruits[123] = "Apple";

alert( fruits.length ); // 124

Všimněte si, že taková pole obvykle nepoužíváme.

Další zajímavá věc o length vlastnost je, že je zapisovatelný.

Pokud jej zvýšíme ručně, nic zajímavého se neděje. Pokud jej ale zmenšíme, pole se zkrátí. Proces je nevratný, zde je příklad:

let arr = [1, 2, 3, 4, 5];

arr.length = 2; // truncate to 2 elements
alert( arr ); // [1, 2]

arr.length = 5; // return length back
alert( arr[3] ); // undefined: the values do not return

Takže nejjednodušší způsob, jak vymazat pole, je:arr.length = 0; .

nové Array()

Pro vytvoření pole existuje ještě jedna syntaxe:

let arr = new Array("Apple", "Pear", "etc");

Používá se zřídka, protože hranaté závorky [] jsou kratší. Kromě toho je zde jedna záludná funkce.

Pokud new Array je voláno s jedním argumentem, kterým je číslo, pak vytvoří pole bez položek, ale s danou délkou .

Pojďme se podívat, jak se člověk může střelit do nohy:

let arr = new Array(2); // will it create an array of [2] ?

alert( arr[0] ); // undefined! no elements.

alert( arr.length ); // length 2

Abychom se takovým překvapením vyhnuli, obvykle používáme hranaté závorky, pokud opravdu nevíme, co děláme.

Vícerozměrná pole

Pole mohou mít položky, které jsou také pole. Můžeme jej použít pro vícerozměrná pole, například pro ukládání matic:

let matrix = [
 [1, 2, 3],
 [4, 5, 6],
 [7, 8, 9]
];

alert( matrix[1][1] ); // 5, the central element

toString

Pole mají vlastní implementaci toString metoda, která vrací čárkami oddělený seznam prvků.

Například:

let arr = [1, 2, 3];

alert( arr ); // 1,2,3
alert( String(arr) === '1,2,3' ); // true

Zkusme také toto:

alert( [] + 1 ); // "1"
alert( [1] + 1 ); // "11"
alert( [1,2] + 1 ); // "1,21"

Pole nemají Symbol.toPrimitive , ani životaschopný valueOf , implementují pouze toString konverze, takže zde [] se stane prázdným řetězcem [1] se změní na "1" a [1,2] se změní na "1,2" .

Když je binární plus "+" operátor přidá něco do řetězce, převede to také na řetězec, takže další krok vypadá takto:

alert( "" + 1 ); // "1"
alert( "1" + 1 ); // "11"
alert( "1,2" + 1 ); // "1,21"

Neporovnávejte pole s ==

Pole v JavaScriptu by se na rozdíl od některých jiných programovacích jazyků neměla srovnávat s operátorem == .

Tento operátor nemá pro pole žádnou speciální úpravu, pracuje s nimi jako s jinými objekty.

Připomeňme si pravidla:

  • Dva objekty se rovnají == pouze pokud se jedná o odkazy na stejný objekt.
  • Pokud je jedním z argumentů == je objekt a ten druhý je primitivní, pak se objekt převede na primitivní, jak je vysvětleno v kapitole Převod objektu na primitiv.
  • …S výjimkou null a undefined která se rovná == navzájem a nic jiného.

Přísné srovnání === je ještě jednodušší, protože nepřevádí typy.

Pokud tedy porovnáme pole s == , nikdy nejsou stejné, pokud neporovnáme dvě proměnné, které odkazují na přesně stejné pole.

Například:

alert( [] == [] ); // false
alert( [0] == [0] ); // false

Tato pole jsou technicky odlišné objekty. Takže si nejsou rovni. == operátor neprovádí porovnání jednotlivých položek.

Srovnání s primitivy může také přinést zdánlivě podivné výsledky:

alert( 0 == [] ); // true

alert('0' == [] ); // false

Zde v obou případech porovnáváme primitivum s objektem pole. Tedy pole [] se pro účely srovnání převede na primitivní a stane se prázdným řetězcem '' .

Poté proces porovnávání pokračuje s primitivy, jak je popsáno v kapitole Převody typů:

// after [] was converted to ''
alert( 0 == '' ); // true, as '' becomes converted to number 0

alert('0' == '' ); // false, no type conversion, different strings

Jak tedy porovnat pole?

To je jednoduché:nepoužívejte == operátor. Místo toho je porovnávejte položku po položce ve smyčce nebo pomocí iteračních metod vysvětlených v další kapitole.

Shrnutí

Pole je speciální druh objektu, vhodný pro ukládání a správu objednaných datových položek.

Prohlášení:

// square brackets (usual)
let arr = [item1, item2...];

// new Array (exceptionally rare)
let arr = new Array(item1, item2...);

Volání na new Array(number) vytvoří pole s danou délkou, ale bez prvků.

  • length vlastnost je délka pole nebo, abychom byli přesní, jeho poslední číselný index plus jedna. Je automaticky upravena metodami pole.
  • Pokud zkrátíme length ručně se pole ořízne.

Získání prvků:

  • prvek můžeme získat podle jeho indexu, například arr[0]
  • můžeme také použít at(i) metoda, která umožňuje záporné indexy. Pro záporné hodnoty i , ustoupí od konce pole. Pokud i >= 0 , funguje stejně jako arr[i] .

Pole můžeme použít jako deque s následujícími operacemi:

  • push(...items) přidá items do konce.
  • pop() odebere prvek z konce a vrátí jej.
  • shift() odebere prvek od začátku a vrátí jej.
  • unshift(...items) přidá items na začátek.

Chcete-li opakovat prvky pole:

  • for (let i=0; i<arr.length; i++) – funguje nejrychleji, kompatibilní se starým prohlížečem.
  • for (let item of arr) – moderní syntaxe pouze pro položky,
  • for (let i in arr) – nikdy nepoužívejte.

Chcete-li porovnat pole, nepoužívejte == operátor (stejně jako > , < a další), protože nemají žádnou speciální úpravu pro pole. Zacházejí s nimi jako s jakýmikoli předměty a my to obvykle nechceme.

Místo toho můžete použít for..of smyčka pro porovnání polí položku po položce.

Budeme pokračovat s poli a studovat další metody pro přidávání, odebírání, extrahování prvků a řazení polí v další kapitole Metody polí.