3. hét
Javascript nyelvi elemek
Ez sajnos igen száraz anyag,
Viszont a megértékhez és a nyelv elsajátításához kell.
Próbáljuk meg random gifekkel feldobni.
var valtozo = 'ertek'; // ez egy comment let masodik = "ertek2"; const harmadik = 3; /* Tobbsoros comment */ function egyfv (parameter1, paramter2){ return 'visszateresi ertek'; }
Javascript gyengén típusos nyelv, lényegében vannak típusok, de minden konverzió automatikusan történik.
Az ellenőrzéseknél oda kell figyelni, hogy érték szerint egyenlő (==) vagy típus és érték szerint egyenlő (===) alapján ellenőrzünk.
Még értelmezett operátorok: != , !== , > , < , >= , <=
Az if, while, switch (típusos egyenlőségként) a megszokotthoz hasonlóan működik.
const a = '2'; const b = 2; if (a == b) {} //true if (a === b) {} //false console.log('3' > 2) //true if (valami){ }else if (valami2){ }
Switch típusosan ellenőriz.
const b='0'; switch (b){ case 0: console.log('oh'); break; case '0': console.log('hopp'); break; default: console.log('meh'); break; }
let b=1; while (b!==3){ console.log(b); //1,2,3 b+=1; }
C++:
int szam = 2;
szam = 'valami'; // Error
string szoveg = new std::string('boo');
void valami = szoveg + szam; // Error
Javascript:
let szam = 2; szam = 'valami'; const szoveg = 'boo'; let valami = szoveg + szam; //'boovalami' vagy //ha nincs a 2. sor, akkor 'boo2'
Egyszerű írni és olvasni!
Viszont nem jelzi fordítási időben a hibákat, a működés nem mindig az, mint aminek kellene lennie
const a = 2; const b = '2';console.log(a+b); // 22 (string) console.log(a/b); // 1 (number) console.log(a*b); // 4 (number)
Hogyan tudom meg, hogy mi milyen típus?
const a = 2; const b = 'valami'; let c; console.log(typeof a); //number console.log(typeof b); //string console.log(typeof c); //undefined
Alaptípusok:
Pár "nem létező" osztály, ami a nyelv megértéséhez elengedhetetlen:
A nyelv dinamikus, tehát egy adott ponton egy változó akár lehet még nem definiált (akár globálisan is).
A globális névtérben van egy undefined változó definiálva, aminek a típusa Undefined.
const b = {}; console.log(typeof b.a); //undefined console.log(typeof undefined); //undefined const a = 2; console.log(typeof a); //number (hiszen az előbb definiáltuk)
Az undefined globális változó (vagy bármi, ami definiált de a típusa undefined) részt vehet műveletekben.
console.log(undefined + 'alma') ; //'undefinedalma' console.log(undefined + 3); //NaN (ez egy number) const myun = undefined; if (myun === undefined) {} // true // ReferenceError: nemletezo is not defined if (nemletezo === undefined) {}
Hogyan ellenőrzöm, hogy létezik az adott változó?
if (typeof myun !== 'undefined'){ // ... }
C++ irányból jövőknek kell megnyutatásként, hogy van valami, ami nem 0 de mégis "valami". Undefinedhoz hasonló, és a szabvány miatt a typeof értéke object.
const e = null; console.log(e == 0); //false console.log(e == false); //false console.log(e == true); //false
Két értéke lehet, true és false.
A kapcsolata a többi osztállyal kissé kaotikus:
const u = undefined;
console.log(false == u, true == u, !u, !!u) //false false true false
console.log(false == null, true == null, !null, !!null) //false false true false
console.log(false == 0, true == 0, !0, !!0) //true false true false
console.log(false == 11, true == 11, !11, !!11) //false false false true
console.log(false == '', true == '', !'', !!'') //true false true false
console.log(false == 'a', true == 'a', !'a', !!'a') //false false false true
console.log(false == [], true == [], ![], !![]) //true false false true
console.log(false == ['a'], true == ['a'], !['a'], !!['a']) //false false false true
console.log(false == {}, true == {}, !{}, !!{}) //false false false true
console.log(false == {a:1}, true == {a:1}, !{a:1}, !!{a:1}) //false false false true
If a value can be converted to true, the value is so-called truthy. If a value can be converted to false, the value is so-called falsy.
All values are truthy unless they are defined as falsy (i.e., except for false, 0, -0, 0n, "", null, undefined, and NaN). https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_NOT https://developer.mozilla.org/en-US/docs/Glossary/Falsy https://developer.mozilla.org/en-US/docs/Glossary/Truthy
Szövegek tárolását oldja meg, nagyon sok kiegészítő funkcióval. Primitív típus, de osztályként viselkedik, amikor műveleteket végzünk rajta. A + concatenál, ami a stringgé való konvertálást is elintézi.
A ' és " közül bármelyikkel definiálható.
const s = 'Lorem ipsum'; const g = "ipsum"; console.log(s); // Lorem ipsum console.log(s+g); // Lorem ipsumipsum console.log(s+42+g); // Lorem ipsum42ipsum
const s = 'Lorem ipsum'; const g = "ipsum"; console.log(s.indexOf(g)) // 6 console.log(s.indexOf('boo')) // -1 console.log(s.substr(1,3)); //ore
Számok tárolására való, gyakorlatilag a float-nak felel meg (de intet is tárolhat). A műveletek a Number "terén" belül maradnak általában, bármit is tesztünk.
const i = 2; const j = 5.222; console.log(i); //2 console.log(j); //5.222 console.log(i+j); //7.222 console.log(i/3); //0.6666666666666666 console.log(i/0); //Infinity , de nem dob hibát!!! console.log(i/'2'); //1 console.log(i/'2a'); //NaN (Not-a-Number) //number, tehát typeof NaN == number... console.log(typeof(i/'2a')); console.log(typeof(i/0)); //number console.log(typeof(i+j)); //number
Ezen az objektumon nem definiáltak osztály típusú műveleteket, e helyett a Math segédosztályt kell használni.
const e = 5.6; console.log(Math.round(e)); //6 console.log(Math.floor(e)); //5 console.log(Math.random()); //0.800291629973799
Stringből hogyan lesz number biztonságosan?
parseInt("10") //10 parseInt("10.00") //10 parseInt("10.33") //10 parseInt("34 45 66") //34 parseInt(" 60 ") //60 parseInt("40 years") //40 parseInt("He was 40") //NaN parseFloat("10") //10 parseFloat("10.00") //10 parseFloat("10.33") //10.33 parseFloat("34 45 66") //34 parseFloat(" 60 ") //60 parseFloat("40 years") //40 parseFloat("He was 40") //NaN
Typeof szerint nincs külön lista típus, de a valóságban lényegében van. Nagyon sokszor használjuk, így egyszerű definiálni, illetve FIFO és FILO módban is nagyon jól tud működni.
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] console.log(typeof lista); //object
let lista = [1,2,3,'4pista']; let ret = []; lista.forEach(function(e){ ret.push(parseInt(e)*2); }); console.log(ret); // [2, 4, 6, 8] console.log(ret.indexOf(4)); //1 console.log(ret.indexOf('4')); //-1
A legfontosabb, hogy az Object itt nem Class, definíció szerint:
Object is an unordered collection of key-value pairs.
Bizonyos nyelvekben ez a map, viszont javascriptben, majd látjuk, erősen keveredik az Object az OOP dolgokkal.
Az object egy dinamikus dolog, tehát bármikor bármelyik részét módosíthatjuk, vagy módosulhat, létrejöhet vagy törlődhet.
let emptyObj = {}; //tagváltozó nélküli objektum let kutya = { lab: 4, fej: 1, ugat: function(){ console.log('Nyauuu'); } }; console.log(kutya.lab); //4 console.log(kutya.fej + 3); //4 kutya.ugat(); // Nyauuu console.log(kutya['lab']); //4: igen, így is elérhető ez
Mivel dinamikus, bármikor módosíthatok bármit.
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 console.log(typeof(kutya)); //object
Ugye hogy mennyire "majdnem" OOP.
Az objectek magukra a this -el tudnak hivatkozni, ami megint csak OOP szerű.
const kutya = { say: 'Wuf', ugat: function(){ console.log(this.say); } };kutya.ugat(); //Wuf
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(nums); console.log(one); console.log(two); console.log(three);
Lexical this!!!!
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);
Mivel az OOP szemlélet ennyire kaotikus, így nagyon nagy hangsúly helyeződik a függvényekre és főleg a callbackekre az aszinkron működés miatt.
function osszead(a,b){ return a + b; } osszead(1,3); //4
A változók definiálására figyelni kell, a változó értelmezésének terére (scope). Ezt kerülni kell, mert a nem definiálta változók a globális térben kerülnek létrehozásra!!
function osszead(a,b){ c = a + b; return c; } osszead(1,3); //4 console.log(c); //4 !!!!
Ez BUG, nem FEATURE!!
Maguk a függvények is elmenthetőek változókba,illetve az egyik függvény ezáltal átadható a másiknak.
const osszead = function (a,b){ return a + b; } osszead(1,3); //4
Függvény paraméterként átadva
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ények amik függvényeket adnak vissza. :)
function ugat(mit) { return function(){ console.log(mit); }; } const valami = 'vau'; const wuf = ugat(valami); wuf();
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 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.
setTimeout(fv, ms) itt a ms késleltetés millisecben.
Nézzünk egy egyszerű példát:
let a=0; setTimeout(function () { a+=10; console.log('Hopp: ' + a); },1000); a+=1; console.log('Kopp: ' + a);
Milyen sorrendben futnak le a parancsok?
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
Így ez lesz az eredmény:
Kopp: 1
Hopp: 11
És ha ezt a kódot nézzük?
function belso(a){ return function () { a+=10; console.log('Hopp: ' + a); }; } let a=0; setTimeout(belso(a),1000); a+=1; console.log('Kopp: ' + a);
Egy extra sor beszúrásával árnyaltabb lesz a kép. Futási sorrend jelezve van megint.
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 a belső fv is lefut, egy fv-t ad vissza a+=1; //4 console.log('Kopp: ' + a); //5
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);
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.
Ez látszik az előző dián.
Ez így elég meta, hol szokott gondot okozni. For ciklusokban async vagy fv definícióknál:
for (let i=0;i<4;i++){ setTimeout(function(){ console.log(i); },100); } //4 db 4-est ír ki, mert az i változik
Ez helyesen:
for (let i=0;i<4;i++){ setTimeout((function(szam){return function(){ console.log(szam); }})(i),100); } //itt már 0,1,2,3 íródik ki, mert létrejön //egy szam nevü lokális változó (ami lehetne i is)
Ezt sokszor még a profik is benézik, főleg ha egy egyszerű típusokkal dolgozó függvényt kell refaktorálni objektekké, akkor nem triviális, hogy az eredeti lefutás hogyan törénik.
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!