Ez a fordítás nem teljes. Kérjük segítsen ezt a cikket lefordítani angolról.

Hogy miért újboli bemutatása? Azért, mert a JavaScript a világ legfélreértetteb programozási nyelveként ismert. Gyakran lebecsülik mint egyfajta játékszert, de az egyszerűsége mellett, erőteljes nyelvi eszközökkel rendelkezik. A JavaScript -et manapság rengeteg fontos alkalmazásban használják, ami azt mutatja, hogy ennek a technológiának a tudása minden web - és mobilalkalmazás fejlesztőnek egy fontos tulajdonsága.

Érdemes a nyelv történetének áttekintésével kezdeni. A JavaScript -et Brendan Eich tervezte 1995 -ben, aki a Netscape - nél volt mérnök. A JavaScript először a Netscape 2 -vel lett kiadva 1996 elején. A nyelvet eredetileg LiveScript - nek nevezték, de egy szerencsétlen marketingdöntés miatt átnevezték, amivel a Sun Microsystems nyelvének, a Java -nak a népszerűségét akarták a hasznukra fordítani, noha a két nyelvben kevés közös dolog volt. Ez máig az összezavarodottság egyik forrása.

Néhány hónappal később Microsoft bemutatta a JScript - et az Internet Explorer 3 kiadásakor. Ez egy nagymértékben JavaScript kompatibilis nyelv volt. Néhány hónap elteltével a Netscape átadta a JavaScript - et az Ecma International - nek, és ez az európai szabványosító szervezet még ebben az évben nyilvánosságra hozta az ECMAScript első verzióját . A szabvány 1999 - ben jelentős frissitést kapott a ECMAScript edition 3 -al, és azóta nagyon stabil. A szabvány negyedik kiadása a politikai nézeteltérések, és a nyelv növekvő komplexitása miatt megszűnt. A negyedik verzió sok eleme képezte alapját a szabvány ötödik, és hatodik kiadásának, amiket 2009. decemberében, valamint 2015 Júniusában adtak ki.

Az ismertség miatt innentől az ECMAScript helyett a JavaScript megnevezést használjuk.

A legtöbb programozásinyelvtől eltérően, a JavaScript - ben nincs terv a standard ki - és bemenetre. A JavaScript arra lett tervezve, hogy egy szkriptként fusson egy hoszt környezetben, így ezen környezet mechanizmusainak feladata a külvilággal való kapcsolat biztosítása. A leggyakrabban erre használt hoszt környezet a böngésző, ám sok más alkalmazásban is található értelmező, mint a Node.js, NoSQL adatbázisok mint a nyílt forrású Apache CouchDB, beágyazott rendszerek, teljes asztali környezetek mint a GNOME (A GNU/Linux operációs rendszerek számára az egyik a legnépszerűbb grafikus felhasználói interfészek közül),  és még sok más.

Áttekintés

A JavaScript multi-paradigmájú nyelv, típusokkal, operátorokkal, beépített standard objektumokkal, és metódusokkal. A szintaxisa a Java és a C nyelvekel alapul — sok struktúra lett ezekből a nyelvekből a JavaScript - be átvéve. A JavaScript osztályok helyett prototípus objektumokkal támogatja az objektumorintáltságot (többet erről prototypical inheritance és ES2015 classes). A JavaScript a funkcionális programozást is támogatja — a függvények objektumok, lehetővé teszik a végrehajtható kód tárolását, és a függvények, mint minden objektum, továbbadhatóak.

Kezdjük a nyelv éíptőköveinek szemlélésével. A JavaScript programok értékeket manipulálnak, és minden értéknek van egy meghatározott típusa. JavaScript típusok a:

... ó, és undefined és a null, amik egy kissé speciálisak, az  Array, ami egy különleges fajtájú objektum, a Date és a RegExp, amiket csak úgy kapsz. Ha pontosak akarunk lenni, a függvények is egy fajta objektumok. Tehát a típusok felsorolása inkább így néz ki:

és van még néhány előre definiált Error típus. Az egyszerűség kedvéért most csak az első felsorolásra korlátozódunk.

Számok

A számok a JavaScript - ben a specifikáció szerint "dupla precíz 64-bit formátumú IEEE 754 értékek". Ennek van néhány érdekes következménye. JavaScript - ben nincs olyasmihez hasonlítható, mint az integer, ami miatt óvatosan kell az aritmetikával bánni, ha a matematikát úgy alkalmazzuk mint a C -ben, vagy Java -ban.

Figyelj oda olyan dolgokra, mint:

0.1 + 0.2 == 0.30000000000000004;

A gyakolatban az integer értékek 32-Bit -es integerként vannak kezelve, és egyes implemetációk még úgy is tárolják őket, amíg egy olyan utasítást kell végrehajtsanak ami egy számra, de nem egy integerre érvényes. Ez például a bitműveleteknél lehet fontos.

Támogatottak az alapértelmezett aritmetikai operátorok, beleértve az összeadást, kivonást, modulo (maradék), és így tovább. Ezenkívül létezik még egy Math objektum, ami olyan matematikai funkciókat nyújt, amik fent még nem lettek említve:

Math.sin(3.5);
var atmero = 2 * Math.PI * r;

Egy stringet a beépített parseInt() funkcióval integerré lehet konvertálni. A funkció megkapja a használt számrendszer alapját, ez a második opcionális paraméter, ami mindig meg kell legyen adva:

parseInt('123', 10); // 123
parseInt('010', 10); // 10

Régebbi böngészőkön a 0 -val kezdődő sztringek oktális számként értelmeződnek (8 -as alapú), de ez 2013 óta már nincs így. Ha nem vagyunk biztosak a string formátumban, a régebbi böngészők meglepő ereményhez vezethetnek:

parseInt('010');  //  8
parseInt('0x10'); // 16

Itt látható, hogy a parseInt() függvény a 0 előtag miatt az első stringet oktális számként, a második stringet a "0x" előtag miatt hexadecimális számként értelmezi. A hexadecimális szemlélet még mindig megengedett, csak az oktális lett eltávolítva.

Ha egy bináris számot szeretnél integerré alakítani, egyszerűen a bázist kell megváltoztatni 2 -re.

parseInt('11', 2); // 3

Ezzel a módszerrel lebegőpontos számokat is konvertálhatunk a parseFloat() függvény segítségével. A parseInt() függvénnyel ellentétben, a parseFloat() mindig 10 -es bázist használ.

Az unáris (egy operandusú) + operátor is használható számok konvertálására:

+ '42';   // 42
+ '010';  // 10
+ '0x10'; // 16

Ha egy string nem tartalmaz numerikus értékeet, egy speciális érték a NaN (A "Not a Number" rövidítése) adódik vissza.

parseInt('hello', 10); // NaN

A NaN veszélyes: Ha egy matematikai művelet egyik operandusaként használjuk, az eredmény is NaN lesz:

NaN + 5; // NaN

A beépített isNaN() függvény segítségével lehet megállapítani, hogy egy változó értéke NaN - e:

isNaN(NaN); // true

A JavaScript - ben vannak olyan speciális értékek is, mint az Infinity és a -Infinity:

 1 / 0; //  Infinity
-1 / 0; // -Infinity

Az Infinity, -Infinity és NaN értékeket a beépített isFinite() függvényekel lehet tesztelni:

isFinite(1 / 0); // false
isFinite(-Infinity); // false
isFinite(NaN); // false
A parseInt() és a parseFloat() addig olvassák a stringet, amíg nem találnak egy olyan karaktert, ami a számrendszerben nem található, és csak az eddig beolvasott számokat adja vissza értékül. Az unáris "+" viszont egyszerűen NaN értéket ad vissza, ha a string egy nem érvényes karaktert tartalmaz. Próbáld meg értelmezni a konzolban a "10.2abc" stringet, hogy jobban megérthesd a köztük lévő különbségeket.

Karakterláncok

A stringek a JavaScript -ben Unicode karakterek sorozata. Ez mindenki számára örvendetes lehet, akik több nyelvű szoftvereken dolgoznak. Még pontosabban fogalmazva, a stringek UTF-16 kódegységek sorozata; mindegy egyes egység egy 16-bites számmal van reprezentálva. Minden Unicode karakter egy, vagy két kódegységből áll.

Egyetlen karakter reprezentálásához egyszerűen egyetlen karaktert használunk.

Egy string hosszát (kód egységekben), a length property -vel (tulajdonság, az objektum tagváltozója) kapjuk meg:

'hello'.length; // 5

Ez az első találkozásunk egy JavaScript objektummal! Említettük már, hogy a stringeket úgy használhatjuk, mint az objektumokat ? Vannak nekik metódusaik is, amiknek segítségével megváltoztathatjuk a stringet, és információkat kaphatunk róla:

'hello'.charAt(0); // "h"
'hello, world'.replace('world', 'mars'); // "hello, mars"
'hello'.toUpperCase(); // "HELLO"

Egyéb típusok

A JavaScript különbséget tesz a null, ami egy "nem értéket" reprezentál, (és csak a null kulcsszón keresztül érhető el), és az undefined között, ami egy undefined típus, és egy nem inicializált értéket jelez, tehát hogy még nem lett a változónak érték adva. A változókról még később szó lesz, de a JavaScript -ben lehet úgy változókat deklarálni, hogy nem rendelünk hozzá értéket. Ha így teszel, a változó típusa undefined lesz. Az undefined egy konstans.

A JavaScript -nek van egy boolean típusa, true és false lehetséges értékkekkel (mindkettő ezek közül kulcsszó.) Minden érték boolean - é konvertálható a következő szabályok figyelembevételével:

  1. false, 0, üres stringek (""), NaN, null, és undefined értékekből false lesz.
  2. Minden más értékből true lesz.

A konverzió közvetlenül a Boolean() függvénnyel hajtható végre:

Boolean('');  // false
Boolean(234); // true

Viszont ez alig szükséges, mivel a JavaScript automatikusan elvégzi a konverziót,ha boolean értéket vár, mint például az if utasítás esetén (lásd lentebb). Ezen ok miatt gyakran beszélünk "true értékekről" és "false értékekről," ami alatt azt értjük, hogy az értékek true vagy false lesznek, miután Boolean -é lettek átalakítva. Ezek az értékek másképpen "truthy" és "falsy".

A Boolean műveletek mint például az && (logikai és), || (logikai vagy), és ! (logikai nem) támogatottak (lásd lentebb).

Változók

JavaScript-ben az új változókat három kulcsszóval lehet deklarálni: let, const, vagy var.

A let kulcsszóval blokk szintű változókat lehet deklarálni. Az így deklarált változó abból a kód blokkból érhető el, ahol azt definiálták.

let a;
let name = 'Simon';

A következő példa a let kulcsszóval deklarált változó láthatóságát mutatja be.

// A myLetVariable itt *NEM* látható.

for (let myLetVariable = 0; myLetVariable < 5; myLetVariable++) {
  // A myLetVariable csak itt elérhető
}

// A myLetVariable itt *NEM* látható.

A const kulcsszóval olyan változó deklarálható, aminek az értéke nem változik meg. A változó abból a kód blokkból érhető el, amiben deklarálták.

const Pi = 3.14; // A Pi változó értékének megadása 
Pi = 1; // kivételt fog dobni, mivel egy konstans értéke nem változtatható meg


A var a leghasználatosabb deklaratív kulcsszó, nincsenek olyan korlátozásai mint a másik kettőnek. Ennek az az oka, hogy hagyományosan ez volt az egyetlen mód egy változó deklarálására a JavaScriptben. A var kulcsszóval deklarált változó abban a függvényben érhető el, amiben azt deklarálták.

var a; 
var name = 'Simon';

A következő példa a var kulcsszóval deklarált változó láthatóságát mutatja be.

// A myVarVariable itt látható

for (var myVarVariable = 0; myVarVariable < 5; myVarVariable++) { 
  // A myVarVariable látható az egész függvényben
} 

// A myVarVariable itt is látható

Ha egy olyan változót deklarálunk, amihez nem lett érték hozzárendelve, a típusa undefined lesz.

Egy fontos különbség a JavaScript és más nyelvek között (pl. a Java),  hogy a JavaScript-ben a blokkoknak nincsen érvényességi területük (scope), Csak a függvényeknek. Szóval, ha egy var kulcsszóval definiálunk változót (például egy if vezérlőszerkezeten belül), Akkor az az egész függvényben látható lesz. Azonban az ECMAScript 2015-ös verziójától, a let és const kulcsszóval deklarált változók, lehetővé teszik a scope-ok létrehozását.

Operátorok

A JavaScript's numerikus operátorai a +, -, *, / és % ami a maradék operátor (Ami nem ugyanaz, mint a modulo.) Az értékadás az = használatával történik, és létezik összevont értékadás is mint a += és a -=. Ezek a következőféleképp értelmezendőek:
x = x operator y.

x += 5;
x = x + 5;

A ++ és -- operátorokat inkrementálásta and dekrementálásra lehet használni. Ezek használhatóak prefix és postfix operátorokként is.

A + operátor stringek egyesítésére is :

'hello' + ' world'; // "hello world"

Ha egy stringet és egy számot (vagy más értéket) összeadunk, először minden string-é konvertálódik. Ez néha problémák forrása:

'3' + 4 + 5;  // "345"
 3 + 4 + '5'; // "75"

Egy üres string hozzáadása egy értékhez jó módszer arra, hogy az értéket string-é konvertáljuk.

A JavaScriptben Összehasonlításokat a <, >, <= és >= operátorok használatával lehet elvégezni. Ezek stringek és számok esetén használhatóak. Az egyenlőség egy kicsit bonyolultabb. Az == operátor típuskonverziót kényszerít ki, ami érdekes eredményekhez vezethet:

123 == '123'; // true
1 == true; // true

A típuskonverzió elkerüléséhez használjuk a === (teljesen egyenlő) operátort:

123 === '123'; // false
1 === true;    // false

Léteznek != és !== operátorok is.

A JavaScript-ben vannak bitszintű műveletek is.

Vezérlő szerkezetek

A JavaScript hasonló a C nyelvcsalád nyelveihez hasonlóvezérlő szerkezetekkel rendelkezik. Feltételes utasítások az if és else kulcsszavakkal hozhatók létre. Ezek összeláncolhatóak:

var name = 'kittens';
if (name == 'puppies') {
  name += ' woof';
} else if (name == 'kittens') {
  name += ' meow';
} else {
  name += '!';
}
name == 'kittens meow';

A JavaScript-ben vannak while és do-while ciklusok. Az egyszerű alkalmas egyszerű ciklusok létrehozására; a második olyan ciklusok létrehozására használatos, ahol biztosítani kívánjuk, hogy a ciklus tartalma (ciklusmag) legalább egyszer lefusson:

while (true) {
  // végtelen ciklus!
}

var input;
do {
  input = get_input();
} while (inputIsNotValid(input));

A JavaScript for ciklusa ugyanaz mint a C-ben és Java-ban megszokott: A vezérlőinformációk egyetlen sorban megadhatóak.

for (var i = 0; i < 5; i++) {
  // ötször fut le
}

A JavaScript-ben van két másik népszerű for ciklus is:
for...of

for (let value of array) {
  // value feldolgozása
}

és a for...in:

for (let property in object) {
  // objektum tulajdonságának feldolgozása
}

A && és || operátorok a rövidzár logikát alkalmazzák, ami azt jelenti, hogy a második operandus végrehajtása az eslő operandustól függ. Ez hasznos null objektumok ellenőrzésére, mielőtt megpróbálnánk hozzáférni az attributúmaihoz:

var name = o && o.getName();

vagy értékek cachelésére (ha a falsy értékek érvénytelenek):

var name = cachedName || (cachedName = getName());

A JavaScript-ben van egy ternáris operátor, feltételek létrehozásához:

var allowed = (age > 18) ? 'yes' : 'no';

switch többszörös elágazásakhoz használható egy string vagy egy szám tartalmától függően:

switch (action) {
  case 'draw':
    drawIt();
    break;
  case 'eat':
    eatIt();
    break;
  default:
    doNothing();
}

Ha nem adunk hozzá break utasítást, a vezérlés átlép a következő case ágra. Ezt ritka esetekben használják  — ilyenkor megéri egy kommentet hozzáfűzni, hogy a későbbi hibakeresést megkönnyítse:

switch (a) {
  case 1: // átlép a következőre
  case 2:
    eatIt();
    break;
  default:
    doNothing();
}

A default ág nem kötelező. Ha szeretnénk a switch részben, és az case részben is lehetnek utasítások; Az összehasonlítás a kettő között a === operátorral történik:

switch (1 + 3) {
  case 2 + 2:
    yay();
    break;
  default:
    sosemtortenikmeg();
}

Objektumok

A JavaScript objektumok egyszerű név - érték párokból állnak. Ezek hasonlóak mint a:

  • Python Szótárak.
  • Perl és Ruby Hash-ek.
  • C és C++ Hash táblák.
  • Java HashMaps .
  • PHP asszociatív tömbök .

Az a tény, hogy ezt az adatstruktúrát olyan széles körben alkalmazzák, a sokoldalúságát bizonyítja. Mivel a JavaScriptben minden (a tiszta core típusok) egy objektum, úgy természetesen minden JavaScript program egy csomó keresési műveletet végez el a hash-táblákban. Jó hogy ezek ilyen gyorsak!

A "név" rész az egy JavaScript string, az érték viszont bármilyen JavaScript érték lehet, beleértve más objektumokat is. Ez lehetővé teszi a tetszőlegesen összetett adatstruktúrák létrehozását.

Egy üres objektum létrehozására két lehetőség van:

var obj = new Object();

és:

var obj = {};

Ezek szemantikailag egyformák; a másodikat object literal szintaxisnak hívják, és elterjedtebb. Ez a szintaxis a magja a JSON formátumnak is.

Az Object literal szintaxis egy átfogó objektum inicializálására használható:

var obj = {
  name: 'Carrot',
  for: 'Max', // 'for' egy foglalt szó, használjunk '_for' -t helyette.
  details: {
    color: 'orange',
    size: 12
  }
};

A tulajdonságok elérése összefűzhető:

obj.details.color; // orange
obj['details']['size']; // 12

A kövekező példa egy  Person prototípust készít, és ezen prototípusnak egy you nevű példányát.

function Person(name, age) {
  this.name = name;
  this.age = age;
}

// Define an object
var you = new Person('You', 24); 
// We are creating a new person named "You" aged 24.

Példányosítás után, egy objektum tulajdonságait két féle képpen lehet elérni:

// dot notation
obj.name = 'Simon';
var name = obj.name;

és...

// bracket notation
obj['name'] = 'Simon';
var name = obj['name'];
// egy változó használható kulcs létrehozására
var user = prompt('what is your key?')
obj[user] = prompt('what is its value?')

Ezek szemantikailag egyformák. A második módszer előnye, hogy a tulajdonság nevét stringként lehet megadni, ami azt jelenti, hogy futási időben dől el. Ám ez a módszer megakadályozza néhány JavaScript motor és minifier optimalizáció végrehajtását. Ez a módszer arra is alkalmazható, hogy olyan tulajdoságokat létrehozhassunk, és elérjünk amik  foglalt szavakat használnak.

obj.for = 'Simon'; // Syntax error, mert a 'for' egy foglalt szó
obj['for'] = 'Simon'; // működik

Az ECMAScript 5-től kezdve, a foglalt szavak is használhatóak az objektumliterálokban. ez azt jelenti, hogy nem kell "idézőjelek kozé" tenni. Lásd ES5 Spec.

További információk az objektumokról, és prototípusokról a Object.prototype cikkben olvashatóak. Az obejktumprototípusok, és az objektumprototípus-láncok  magyarűzatáhpz lásd az Inheritance and the prototype chain cikket.

Az ECMAScript 5-től kezdve, az objektumok névpárját  (kulcsait) szögletes zárójelek közé tett változóval is lehet használni {[phoneType]: 12345}, nem csak így:  var userPhone = {}; userPhone[phoneType] = 12345.

Tömbök

A tömbök a JavaScriptben az objektumok egy különleges fajtája. Messzemenőkig úgy működnek mint a normális objektumok, (A numerikus tulajdonságok csak a [] szintaxissal érhetőek el) de van egy plusz tulajdonságuk is, a 'length'. Ez mindigy egyel több, mint a legmagasabb index a tömbben.

Egy tömböt a következő képpen lehet létrehozni:

var a = new Array();
a[0] = 'dog';
a[1] = 'cat';
a[2] = 'hen';
a.length; // 3

Még népszerűbb ezen a módon egy literállal.

var a = ['dog', 'cat', 'hen'];
a.length; // 3

Tartsuk észben, hogy az array.length nem feltétlenül az elemek számát jelenti. Nézzük a következő példát:

var a = ['dog', 'cat', 'hen'];
a[100] = 'fox';
a.length; // 101

Ne feledjük: a tömb hossza (length) mindig a legmagasabb index + 1 értéket jelenti.

Ha egy nem létező indexhez próbálunk hozzáférni, akkor undefined értéket kapunk vissza:

typeof a[90]; // undefined

A fenti [] -ket és a length tulajdonságot használva, végiglépkedhetünk a tömbön a következő for ciklus segítségével:

for (var i = 0; i < a.length; i++) {
  // a[i] feldolgozása
}

Az ES2015 óta a fenti módszernek létezik egy egyszerűbb formája is, a for...of ciklus, ami olyan objektumoknál használható, mint a tömbök:

for (const currentValue of a) {
  // currentValue feldolgozása
}

Egy  for...in ciklussal is végig lehet lépkedni egy tömbön, ám ez nem a tömb elemein lépked végig, hanem a tömb indexein. Továbbá, ha valaki új tulajdonságokat ad hozzá az Array.prototype-hoz, akkor azokon is egy ilyen ciklus lépkedne végig. Emiatt ez a fajta ciklus nem ajánlott tömbök iterálására.

Az ECMAScript 5-ben egy másik módszer is adott, hogy végig lépkedjünk egy tömbön, a forEach():

['dog', 'cat', 'hen'].forEach(function(currentValue, index, array) {
  // currentValue vagy array[index] feldolgozása
});

Ha egy elemt szeretnénk a tömbhöz hozzáadni, egyszerűen így tehetjük meg:

a.push(item);

Egy tömbnek sok metódusa van. Lásd a full documentation for array methods cikket is.

Method name Description
a.toString() Egy stringet ad vissza, amiben minden objektum veszzővel van elválasztva. 
a.toLocaleString() Egy stringet ad vissza, amiben minden objektum veszzővel van elválasztva, dátumoknál a helyi idő szerinti formátum
a.concat(item1[, item2[, ...[, itemN]]]) Egy új tömböt ad vissza, a hozzáadott elemekkel együtt.
a.join(sep) Egy stringé alakítja át a tömböt, amiben a sep paraméterben megadottal lesznek az egyes elemek elválasztva
a.pop() Eltávolítja az utolsó elemet, és visszaadja azt.
a.push(item1, ..., itemN) A tömb végére hozzáfűz egy elemet
a.reverse() Megfordítja a tömb sorrendjét.
a.shift() Eltávolítja az első elemet, és visszaadja azt.
a.slice(start[, end]) Egy altömböt ad vissza.
a.sort([cmpfn]) Rendezi a tömböt. Opcionálisan egy függvényt is meg lehet adni.
a.splice(start, delcount[, item1[, ...[, itemN]]]) Módosít egy tömböt úgy, hogy egy része törölve, és azok több elemekkel cserélve lesznek.
a.unshift(item1[, item2[, ...[, itemN]]]) A tömb elejére elemeket szúr be.

Függvények

Az objektumok mellet, a függvények a JavaScript központi összetevői. Egy egyszerű függvény szintaxisa alig lehetne egyszerűebb:

function add(x, y) {
  var total = x + y;
  return total;
}

Ez egy egyszerű függvényt mutat be. Egy JavaScript függvénynek 0 vagy több megnevezett paramétere lehet. A függvény törzse tetszüleges számú utasítást tartalmazhat, és egyéni lokális változók is deklarálhatóak benne. A return utasítás bárhol használható értékek visszaadására, és a függvény befejezésére. ha nem használunk return utasítást, (vagy érték nélkül használjuk), a függvény visszatérése undefined lesz.

A megnevezett paraméterek inkább ajánlás, mint kötelezően megadandó adatok. A függvények meghívhatóak a várt paraméterek nélkül is, ez esetben azok értéke undefined lesz.

add(); // NaN
// Nem lehet hozzáadást végezni undefined értékekkel

You can also pass in more arguments than the function is expecting:

add(2, 3, 4); // 5
// az első két paraméter össze lesz adva; a 4 figyelmen kívül marad

Talán egy kicsit furcsának tűnhet, de a függvények a függvénytörzsben hozzáférnek egy plusz változóhoz is, az arguments-hez, ami egy tömb szerű objektum, és a megadott paramétereket tartalmazza. Írjuk újra a függvényt úgy hogy tetszőleges számú paramétert dolgozzon fel:

function add() {
  var sum = 0;
  for (var i = 0, j = arguments.length; i < j; i++) {
    sum += arguments[i];
  }
  return sum;
}

add(2, 3, 4, 5); // 14

Ám ez nem hasznosabb annál, mint leírni, hogy 2 + 3 + 4 + 5. Készítsünk egy átlagszámítást végző függvényt:

function avg() {
  var sum = 0;
  for (var i = 0, j = arguments.length; i < j; i++) {
    sum += arguments[i];
  }
  return sum / arguments.length;
}

avg(2, 3, 4, 5); // 3.5

Ez nagyon hasznos, mégis egy kicsit hosszúnak látszik. Hogy lerövidíthessük egy kicsit jobban a kódot, az arguments tömb használatát a Rest parameter syntax -ra is cserélhetjük, így tetszőleges sok paraméter adható át, és a kód minimális marad. A függvényekben a rest parameter operator a ...variable formátummal írható le, és tartalmazza a függvény számára összes nem megnevezett paramétert. A for ciklus helyett használhatunk for...of ciklust is.

function avg(...args) {
  var sum = 0;
  for (let value of args) {
    sum += value;
  }
  return sum / args.length;
}

avg(2, 3, 4, 5); // 3.5
A fenti kódban, az args változó tárolja az összes értéket, amit a függvénynek adtunk át.

It is important to note that wherever the rest parameter operator is placed in a function declaration it will store all arguments after its declaration, but not before. i.e. function avg(firstValue, ...args) will store the first value passed into the function in the firstValue variable and the remaining arguments in args. That's another useful language feature but it does lead us to a new problem. The avg() function takes a comma-separated list of arguments — but what if you want to find the average of an array? You could just rewrite the function as follows:
function avgArray(arr) {
  var sum = 0;
  for (var i = 0, j = arr.length; i < j; i++) {
    sum += arr[i];
  }
  return sum / arr.length;
}

avgArray([2, 3, 4, 5]); // 3.5

But it would be nice to be able to reuse the function that we've already created. Luckily, JavaScript lets you call a function with an arbitrary array of arguments, using the apply() method of any function object.

avg.apply(null, [2, 3, 4, 5]); // 3.5

The second argument to apply() is the array to use as arguments; the first will be discussed later on. This emphasizes the fact that functions are objects too.

You can achieve the same result using the spread operator in the function call.

For instance: avg(...numbers)

JavaScript lets you create anonymous functions.

var avg = function() {
  var sum = 0;
  for (var i = 0, j = arguments.length; i < j; i++) {
    sum += arguments[i];
  }
  return sum / arguments.length;
};

This is semantically equivalent to the function avg() form. It's extremely powerful, as it lets you put a full function definition anywhere that you would normally put an expression. This enables all sorts of clever tricks. Here's a way of "hiding" some local variables — like block scope in C:

var a = 1;
var b = 2;

(function() {
  var b = 3;
  a += b;
})();

a; // 4
b; // 2

JavaScript allows you to call functions recursively. This is particularly useful for dealing with tree structures, such as those found in the browser DOM.

function countChars(elm) {
  if (elm.nodeType == 3) { // TEXT_NODE
    return elm.nodeValue.length;
  }
  var count = 0;
  for (var i = 0, child; child = elm.childNodes[i]; i++) {
    count += countChars(child);
  }
  return count;
}

This highlights a potential problem with anonymous functions: how do you call them recursively if they don't have a name? JavaScript lets you name function expressions for this. You can use named IIFEs (Immediately Invoked Function Expressions) as shown below:

var charsInBody = (function counter(elm) {
  if (elm.nodeType == 3) { // TEXT_NODE
    return elm.nodeValue.length;
  }
  var count = 0;
  for (var i = 0, child; child = elm.childNodes[i]; i++) {
    count += counter(child);
  }
  return count;
})(document.body);

The name provided to a function expression as above is only available to the function's own scope. This allows more optimizations to be done by the engine and results in more readable code. The name also shows up in the debugger and some stack traces, which can save you time when debugging.

Note that JavaScript functions are themselves objects — like everything else in JavaScript — and you can add or change properties on them just like we've seen earlier in the Objects section.

Custom objects

For a more detailed discussion of object-oriented programming in JavaScript, see Introduction to Object-Oriented JavaScript.

In classic Object Oriented Programming, objects are collections of data and methods that operate on that data. JavaScript is a prototype-based language that contains no class statement, as you'd find in C++ or Java (this is sometimes confusing for programmers accustomed to languages with a class statement). Instead, JavaScript uses functions as classes. Let's consider a person object with first and last name fields. There are two ways in which the name might be displayed: as "first last" or as "last, first". Using the functions and objects that we've discussed previously, we could display the data like this:

function makePerson(first, last) {
  return {
    first: first,
    last: last
  };
}
function personFullName(person) {
  return person.first + ' ' + person.last;
}
function personFullNameReversed(person) {
  return person.last + ', ' + person.first;
}

var s = makePerson('Simon', 'Willison');
personFullName(s); // "Simon Willison"
personFullNameReversed(s); // "Willison, Simon"

This works, but it's pretty ugly. You end up with dozens of functions in your global namespace. What we really need is a way to attach a function to an object. Since functions are objects, this is easy:

function makePerson(first, last) {
  return {
    first: first,
    last: last,
    fullName: function() {
      return this.first + ' ' + this.last;
    },
    fullNameReversed: function() {
      return this.last + ', ' + this.first;
    }
  };
}

var s = makePerson('Simon', 'Willison');
s.fullName(); // "Simon Willison"
s.fullNameReversed(); // "Willison, Simon"

There's something here we haven't seen before: the this keyword. Used inside a function, this refers to the current object. What that actually means is specified by the way in which you called that function. If you called it using dot notation or bracket notation on an object, that object becomes this. If dot notation wasn't used for the call, this refers to the global object.

Note that this is a frequent cause of mistakes. For example:

var s = makePerson('Simon', 'Willison');
var fullName = s.fullName;
fullName(); // undefined undefined

When we call fullName() alone, without using s.fullName(), this is bound to the global object. Since there are no global variables called first or last we get undefined for each one.

We can take advantage of the this keyword to improve our makePerson function:

function Person(first, last) {
  this.first = first;
  this.last = last;
  this.fullName = function() {
    return this.first + ' ' + this.last;
  };
  this.fullNameReversed = function() {
    return this.last + ', ' + this.first;
  };
}
var s = new Person('Simon', 'Willison');

We have introduced another keyword: new. new is strongly related to this. It creates a brand new empty object, and then calls the function specified, with this set to that new object. Notice though that the function specified with this does not return a value but merely modifies the this object. It's new that returns the this object to the calling site. Functions that are designed to be called by new are called constructor functions. Common practice is to capitalize these functions as a reminder to call them with new.

The improved function still has the same pitfall with calling fullName() alone.

Our person objects are getting better, but there are still some ugly edges to them. Every time we create a person object we are creating two brand new function objects within it — wouldn't it be better if this code was shared?

function personFullName() {
  return this.first + ' ' + this.last;
}
function personFullNameReversed() {
  return this.last + ', ' + this.first;
}
function Person(first, last) {
  this.first = first;
  this.last = last;
  this.fullName = personFullName;
  this.fullNameReversed = personFullNameReversed;
}

That's better: we are creating the method functions only once, and assigning references to them inside the constructor. Can we do any better than that? The answer is yes:

function Person(first, last) {
  this.first = first;
  this.last = last;
}
Person.prototype.fullName = function() {
  return this.first + ' ' + this.last;
};
Person.prototype.fullNameReversed = function() {
  return this.last + ', ' + this.first;
};

Person.prototype is an object shared by all instances of Person. It forms part of a lookup chain (that has a special name, "prototype chain"): any time you attempt to access a property of Person that isn't set, JavaScript will check Person.prototype to see if that property exists there instead. As a result, anything assigned to Person.prototype becomes available to all instances of that constructor via the this object.

This is an incredibly powerful tool. JavaScript lets you modify something's prototype at any time in your program, which means you can add extra methods to existing objects at runtime:

var s = new Person('Simon', 'Willison');
s.firstNameCaps(); // TypeError on line 1: s.firstNameCaps is not a function

Person.prototype.firstNameCaps = function() {
  return this.first.toUpperCase();
};
s.firstNameCaps(); // "SIMON"

Interestingly, you can also add things to the prototype of built-in JavaScript objects. Let's add a method to String that returns that string in reverse:

var s = 'Simon';
s.reversed(); // TypeError on line 1: s.reversed is not a function

String.prototype.reversed = function() {
  var r = '';
  for (var i = this.length - 1; i >= 0; i--) {
    r += this[i];
  }
  return r;
};

s.reversed(); // nomiS

Our new method even works on string literals!

'This can now be reversed'.reversed(); // desrever eb won nac sihT

As mentioned before, the prototype forms part of a chain. The root of that chain is Object.prototype, whose methods include toString() — it is this method that is called when you try to represent an object as a string. This is useful for debugging our Person objects:

var s = new Person('Simon', 'Willison');
s.toString(); // [object Object]

Person.prototype.toString = function() {
  return '<Person: ' + this.fullName() + '>';
}

s.toString(); // "<Person: Simon Willison>"

Remember how avg.apply() had a null first argument? We can revisit that now. The first argument to apply() is the object that should be treated as 'this'. For example, here's a trivial implementation of new:

function trivialNew(constructor, ...args) {
  var o = {}; // Create an object
  constructor.apply(o, args);
  return o;
}

This isn't an exact replica of new as it doesn't set up the prototype chain (it would be difficult to illustrate). This is not something you use very often, but it's useful to know about. In this snippet, ...args (including the ellipsis) is called the "rest arguments" — as the name implies, this contains the rest of the arguments.

Calling

var bill = trivialNew(Person, 'William', 'Orange');

is therefore almost equivalent to

var bill = new Person('William', 'Orange');

apply() has a sister function named call, which again lets you set this but takes an expanded argument list as opposed to an array.

function lastNameCaps() {
  return this.last.toUpperCase();
}
var s = new Person('Simon', 'Willison');
lastNameCaps.call(s);
// Is the same as:
s.lastNameCaps = lastNameCaps;
s.lastNameCaps(); // WILLISON

Inner functions

JavaScript function declarations are allowed inside other functions. We've seen this once before, with an earlier makePerson() function. An important detail of nested functions in JavaScript is that they can access variables in their parent function's scope:

function parentFunc() {
  var a = 1;

  function nestedFunc() {
    var b = 4; // parentFunc can't use this
    return a + b; 
  }
  return nestedFunc(); // 5
}

This provides a great deal of utility in writing more maintainable code. If a called function relies on one or two other functions that are not useful to any other part of your code, you can nest those utility functions inside it. This keeps the number of functions that are in the global scope down, which is always a good thing.

This is also a great counter to the lure of global variables. When writing complex code it is often tempting to use global variables to share values between multiple functions — which leads to code that is hard to maintain. Nested functions can share variables in their parent, so you can use that mechanism to couple functions together when it makes sense without polluting your global namespace — "local globals" if you like. This technique should be used with caution, but it's a useful ability to have.

Closures

This leads us to one of the most powerful abstractions that JavaScript has to offer — but also the most potentially confusing. What does this do?

function makeAdder(a) {
  return function(b) {
    return a + b;
  };
}
var x = makeAdder(5);
var y = makeAdder(20);
x(6); // ?
y(7); // ?

The name of the makeAdder() function should give it away: it creates new 'adder' functions, each of which, when called with one argument, adds it to the argument that it was created with.

What's happening here is pretty much the same as was happening with the inner functions earlier on: a function defined inside another function has access to the outer function's variables. The only difference here is that the outer function has returned, and hence common sense would seem to dictate that its local variables no longer exist. But they do still exist — otherwise, the adder functions would be unable to work. What's more, there are two different "copies" of makeAdder()'s local variables — one in which a is 5 and the other one where a is 20. So the result of that function calls is as follows:

x(6); // returns 11
y(7); // returns 27

Here's what's actually happening. Whenever JavaScript executes a function, a 'scope' object is created to hold the local variables created within that function. It is initialized with any variables passed in as function parameters. This is similar to the global object that all global variables and functions live in, but with a couple of important differences: firstly, a brand new scope object is created every time a function starts executing, and secondly, unlike the global object (which is accessible as this and in browsers as window) these scope objects cannot be directly accessed from your JavaScript code. There is no mechanism for iterating over the properties of the current scope object, for example.

So when makeAdder() is called, a scope object is created with one property: a, which is the argument passed to the makeAdder() function. makeAdder() then returns a newly created function. Normally JavaScript's garbage collector would clean up the scope object created for makeAdder() at this point, but the returned function maintains a reference back to that scope object. As a result, the scope object will not be garbage-collected until there are no more references to the function object that makeAdder() returned.

Scope objects form a chain called the scope chain, similar to the prototype chain used by JavaScript's object system.

A closure is the combination of a function and the scope object in which it was created. Closures let you save state — as such, they can often be used in place of objects. You can find several excellent introductions to closures.

Dokumentum címkék és a közreműködők

Ezen oldal közreműködői: mdnwebdocs-bot, kisjoke91
Utoljára frissítve: mdnwebdocs-bot,