## Szerver oldali JavaScript **2. hét** Javascript nyelvi elemek -- PAGE -- ### Nyelvi alapok: Ez sajnos igen száraz anyag, Viszont a megértékhez és a nyelv elsajátításához kell. -- PAGE -- Próbáljuk meg random gifekkel feldobni. -- PAGE -- ### Miről lesz szó - Alapok - Típusok - Osztályok - Függvények - Closure -- PAGE -- ### Alapok: hogy néz ki a kód ```js var valtozo = 'ertek'; // ez egy comment let masodik = "ertek2"; const harmadik = 3; /* Tobbsoros comment */ function egyfv (parameter1, paramter2){ return 'visszateresi ertek'; } ``` -- PAGE -- ### Alapok: operátorok Javascript gyengén típusos nyelv, lényegében vannak típusok, de minden konverzió automatikusan történik. Érték szerint egyenlő (**==**) vagy típus és érték szerint egyenlő (**===**)?
Ökölszabály
**Mindig `===` és `!==`.** A `==` konverziós szabályait senki nem tartja fejben, és ma már nincs is rá szükség.
Még értelmezett: `>`, `<`, `>=`, `<=`
-- PAGE -- ### Alapok: if Az **if**, **while**, **switch** a megszokotthoz hasonlóan működik. ```js const a = '2'; const b = 2; if (a == b) {} //true <- ezért nem használjuk if (a === b) {} //false <- ezt használjuk ``` -- PAGE -- -- PAGE -- ### Típusok C++: ``` int szam = 2; szam = 'valami'; // Error ``` Javascript: ```js let szam = 2; szam = 'valami'; // simán megy ``` Egyszerű írni és olvasni: automatikus típuskonverzió, nincs memória- és referenciakezelés. Cserébe fordítási időben **semmit** nem szól. -- PAGE -- ### Típusok Ezért érdemes a típusokra figyelni: ```js const a = 2; const b = '2'; console.log(a+b); // 22 (string!) console.log(a/b); // 1 (number) console.log(a*b); // 4 (number) ``` A **`+` a kakukktojás**: ha az egyik oldal string, akkor összefűz. A többi operátor számmá alakít.
TLDR
ha számot vársz, alakítsd te: `Number(b)` vagy `parseInt(b, 10)`. -- PAGE -- ### Típusok Hogyan tudom meg, hogy mi milyen típus? ```js const a = 2; const b = 'valami'; let c; console.log(typeof a); //number console.log(typeof b); //string console.log(typeof c); //undefined ``` -- PAGE -- ### Típusok Alaptípusok: - Undefined - Null - Boolean - String - Number - Object -- PAGE -- -- PAGE -- ### Undefined A nyelv dinamikus: egy változó egy adott ponton lehet még nem definiált. Az **undefined** egy globális változó, aminek a típusa is Undefined. ```js const b = {}; console.log(typeof b.a); //undefined console.log(undefined + 'alma');//'undefinedalma' console.log(undefined + 3); //NaN // de a nem létező változó már hiba: if (nemletezo === undefined) {} //ReferenceError ```
TLDR
`typeof myun !== 'undefined'` — a `typeof` az egyetlen, ami nem definiált változóra sem dob hibát. -- PAGE -- ### Null C++ irányból jövőknek megnyugtatásként: van valami, ami nem 0, de mégis „valami". Az undefinedhoz hasonló, és a szabvány miatt a `typeof` értéke `object`. Ritkán használjuk — mi a `null`-t inkább csak akkor látjuk majd, amikor a mongoose vagy egy callback adja vissza. -- PAGE -- ### Template literal Sztringbe értéket beilleszteni **ne** pluszjelekkel próbáljunk. Visszafelé dőlő aposztróf, és `${}`: ```js const nev = 'Tudor'; const db = 3; const regi = 'A ' + nev + ' nevű törpének ' + db + ' kalapja van.'; const uj = `A ${nev} nevű törpének ${db} kalapja van.`; const tobbsoros = `első sor második sor`; // a sortörés is megmarad ``` A `${}` belsejébe **bármilyen kifejezés** mehet: `${db * 2}`, `${nev.toUpperCase()}`. -- PAGE -- ### `??` — nullish coalescing „Ha nincs értéke, legyen ez." De **csak** `null` és `undefined` esetén: ```js const a = null ?? 'alapérték'; // 'alapérték' const b = undefined ?? 'alapérték'; // 'alapérték' const c = 0 ?? 'alapérték'; // 0 <- megmarad! const d = '' ?? 'alapérték'; // '' <- megmarad! const e = 0 || 'alapérték'; // 'alapérték' <- a || ezt elrontja ``` Ezért jobb a `||`-nál mindenhol, ahol a `0` vagy az üres sztring **érvényes érték**. -- PAGE -- ### Boolean — a furcsaság Két értéke van, **true** és **false**. A kapcsolata a többivel viszont kaotikus: ```js if ([]) { } // lefut! az üres tömb truthy if ({}) { } // lefut! az üres objektum is if ('0') { } // lefut! a nem üres string mindig az if (0) { } // nem fut le ``` Nem kell fejben tartani a szabályokat — elég **egy** listát tudni. → -- PAGE -- ### Boolean
Falsy, és kész
**`false`, `0`, `''`, `null`, `undefined`, `NaN`.** Minden más truthy. Ennyi. Az üres tömb és az üres objektum is truthy. Amit a gyakorlatban csinálj: - listánál `if (lista.length)`, ne `if (lista)` - sztringnél `if (szoveg !== '')`, ha a `0` vagy `''` érvényes érték - alapértékhez `??`, ne `||`
https://developer.mozilla.org/en-US/docs/Glossary/Falsy
-- PAGE -- -- PAGE -- ### String Primitív típus, de osztályként viselkedik. A `'` és `"` közül bármelyikkel definiálható, a `+` összefűz (és stringgé konvertál). ```js const s = 'Lorem ipsum'; const g = "ipsum"; console.log(s + 42 + g); // Lorem ipsum42ipsum console.log(s.indexOf(g)); // 6 (nincs találat: -1) console.log(s.includes(g)); // true console.log(s.slice(1, 4)); // ore ```
A régi `.substr()` elavult, helyette `.slice()`. A többi metódust úgyis a dokumentációban nézed meg.
-- PAGE -- ### Number Gyakorlatilag float (de intet is tárol). A műveletek a Number „terén" belül maradnak — **hibát szinte soha nem dob**: ```js console.log(2 / 0); //Infinity , nem hiba! console.log(2 / '2a'); //NaN (Not-a-Number) console.log(typeof NaN); //'number' ... igen console.log(NaN === NaN); //false <- ezért kell Number.isNaN() ```
TLDR
számolás után `Number.isNaN(x)` vagy `Number.isFinite(x)`, ha a bemenet felhasználótól jön. -- PAGE -- ### Number Stringből szám biztonságosan: ```js Number('10.33') //10.33 Number('40 years') //NaN <- szigorú, ezért jó parseInt('40 years') //40 <- megeszi, amit tud parseInt('He was 40') //NaN ``` Kerekítés és társai a **Math** segédosztályban: `Math.round()`, `Math.floor()`, `Math.random()` -- PAGE -- -- PAGE -- ### List `typeof` szerint nincs külön lista típus (`object`), a valóságban viszont van. FIFO és FILO módban is jól működik. ```js let lista = []; //üres lista lista.push(1); //lista: [1] lista.push(2,3); //lista: [1,2,3] console.log(lista[1]); //2 let elem = lista.pop(); //elem:3 , lista: [1,2] elem = lista.shift(); //elem:1 , lista: [2] ``` -- PAGE -- ### List Bejárás és keresés: ```js const lista = [1, 2, 3, '4pista']; const ret = lista.map((e) => parseInt(e) * 2); console.log(ret); // [2, 4, 6, 8] console.log(ret.indexOf(4)); //1 console.log(ret.indexOf('4')); //-1 <- típusra is illeszkedik ```
`forEach` ugyanez, csak nem ad vissza semmit.
-- PAGE -- -- PAGE -- ### Object A legfontosabb, hogy az Object itt nem Class: *Object is an unordered collection of key-value pairs.* ```js let emptyObj = {}; //tagváltozó nélküli objektum let kutya = { lab: 4, fej: 1, ugat: function(){ console.log('Nyauuu'); } }; console.log(kutya.lab); //4 kutya.ugat(); // Nyauuu console.log(kutya['lab']); //4: igen, így is elérhető ez ``` -- PAGE -- ### Object Mivel dinamikus, bármikor módosíthatok bármit. ```js kutya.fej=3; console.log(kutya.fej); //3 kutya.ugat = function(){ console.log('Wuf'); } kutya.ugat(); //Wuf //törölhetőek is a tagváltozók delete kutya.lab; console.log(kutya.lab); //undefined ``` Ugye hogy mennyire "majdnem" OOP. -- PAGE -- ### Object Az objectek magukra a *this* -el tudnak hivatkozni, ami megint csak OOP szerű. ```js const kutya = { say: 'Wuf', ugat: function(){ console.log(this.say); } }; kutya.ugat(); //Wuf ``` De ez még mindig nem osztály típusú működés, nincs konstruktor, nincs semmi. Mégis hogyan lesz ebből osztály? -- PAGE -- ### Class ```js class Kutya { constructor(say) { this.say = say; } ugat() { console.log(this.say); } } const kutya = new Kutya("Wuf"); kutya.ugat(); ``` -- PAGE -- -- PAGE -- ### Destrukturálás Objektumból vagy listából egy lépésben kiszedni a részeket: ```js const torpe = { nev: 'Tudor', sapka: 'kék', kor: 132 }; const { nev, sapka } = torpe; // nev = 'Tudor', sapka = 'kék' const { kor: eletkor } = torpe; // átnevezve: eletkor = 132 const { hajszin = 'ősz' } = torpe; // ha nincs ilyen kulcs: 'ősz' const [elso, masodik] = [10, 20]; // listával is megy ``` Ezt fogod látni **minden** modern kódban, és a middleware-eknél mi is használjuk majd: `const { TorpeModel } = objRepo;` -- PAGE -- ### `?.` — optional chaining Emlékszel, hogy a nem létező tulajdonság `undefined`, de az `undefined` tulajdonsága már **hiba**? ```js const torpe = { nev: 'Tudor' }; torpe.cim.varos // TypeError: Cannot read properties of undefined torpe.cim?.varos // undefined — nem dob hibát torpe.koszon?.() // ha nincs ilyen függvény, nem hívja meg ``` Pontosan azt a hibaosztályt szünteti meg, amivel az `undefined`-nél ijesztgettelek titeket. -- PAGE -- ### Spread Operator ```js const a = [ "hello", true, 7 ] const b = [ 1, 2, ...a ]; console.log(b); const c = { a: "hello", b: true, c: 7 } const d = {d: "foo", ...c } console.log(d); ``` -- PAGE -- -- PAGE -- ### Függvények Mivel az OOP szemlélet ennyire kaotikus, nagy hangsúly kerül a függvényekre és főleg a callbackekre az aszinkron működés miatt. ```js function osszead(a,b){ return a + b; } // ugyanez változóba mentve, így már átadható másnak: const osszead2 = function (a,b){ return a + b; } osszead(1,3); //4 ``` -- PAGE -- ### Függvények A változó **scope**-jára figyelni kell: ```js function osszead(a,b){ c = a + b; // nincs let/const! return c; } osszead(1,3); //4 console.log(c); //4 !!!! kiszivárgott a globális térbe ``` **Ez BUG, nem FEATURE!!**
TLDR
mindig `let`/`const`, ha a fájl tetején `'use strict';` akkor hibát dob, ha nincs `let`/`const`. -- PAGE -- ### Függvények ```js function muvelet(a,b,amit){ console.log("a: " + a + " b: "+ b); console.log("eredmeny: " + amit(a, b)); } const osszead = function (a,b){ return a + b; } const kivon = function (a,b){ return a - b; } muvelet(1,3,osszead); //a: 1 b: 3 eredmeny: 4 muvelet(1,3,kivon); //a: 1 b: 3 eredmeny: -2 ``` Függvény paraméterként átadva -- PAGE -- ### Függvények Függvények amik függvényeket adnak vissza. :) ```js function ugat(mit) { return function(){ console.log(mit); }; } const valami = 'vau'; const wuf = ugat(valami); wuf(); ``` -- PAGE -- ### Arrow function ;s map ```js const nums = [1,2,3,4,5,6,7,8]; const one = nums.map(v => v + 1) const two = nums.map(v => { return v + 1;}) const three = nums.map((v, i) => v + i) console.log(one); console.log(three); ``` Lexical this!!!! -- PAGE -- ### Függvények Viszont mivel nincs referencia / érték alapú átadás, csak paraméter átadás, mi történik, ha valamelyik paramétert megváltoztatom futás közben? #### Closures Closures are functions that refer to independent (free) variables. In other words, the function defined in the closure 'remembers' the environment in which it was created. Most tessék kapaszkodni. -- PAGE -- -- PAGE -- ### Closures bevezetés **setTimeout(fv, ms)** — milyen sorrendben futnak le a parancsok? ```js let a=0; //1 setTimeout(function () { a+=10; //5 console.log('Hopp: ' + a); //6 },1000); //2, a setTimeout hívás //, nem a belső függvény a+=1; //3 console.log('Kopp: ' + a); //4 ``` -- PAGE -- ### Closures bevezetés És ha a paramétert becsomagoljuk? Futási sorrend jelezve. ```js function belso(a){ let b=a; //3 return function () { b+=10; //6 console.log('Hopp: ' + b); //7 }; } let a=0; //1 setTimeout(belso(a),1000); //2, belső fv lefut, egy fv-t ad vissza a+=1; //4 console.log('Kopp: ' + a); //5 ``` -- PAGE -- ### Closures bevezetés ```js function belso(a){ return function () { a.ertek+=10; console.log('Hopp: ' + a.ertek); }; } let a={ertek:0}; setTimeout(belso(a),1000); a.ertek+=1; a = {ertek:2}; //ez a lényeg console.log('Kopp: ' + a.ertek); ``` -- PAGE -- -- PAGE -- ### Hogy is van ez?
A belső függvények olyan külső (akár globális akár lokális) változókra hivatkoznak, amikkel a függvény létrehozása és meghívása során bármi történhet. Így a javascript engine a változókat "megpróbálja megtartani" referenciaként.
Objectek esetében ha az őket tartalmazó változót módosítom, akkor lényegében új objektumot hozok létre,
és nem az eredeti referencia értékét módosítom. Egyszerű típusok (string,number) esetében ezzel nincs gond, mert nem hozok létre új objektumot, hanem a meglévő értékét módosítom.
-- PAGE -- -- PAGE -- ### Lényegében ennyi Erre épül a nyelv, persze, sok extrával és hasznos kis aprósággal, de a többit majd csakorlatban. **Köszönöm a kitartást!**