WEBES ALKALMAZÁSFEJLESZTÉS 1. Horváth Győző Egyetemi adjunktus 1117 Budapest, Pázmány Péter sétány 1/C, 2.420 Tel: (1) 372-2500/1816
Ismétlés – objektumok létrehozása 2
Két alappillér Objektumok
dinamikussága Prototípus-objektum
Kód újrahasznosítás Objektumlétrehozási minták Öröklési minták Magas szintű segédfüggvények
3
Objektumlétrehozás klasszikus OOP szintaxissal
new operátor 4
var c = new Child(); szintaktikai hasonlóság működésbeli eltérés operandus: speciálisan viselkedő függvény konstruktorfüggvények konstruktorhívási minta //A konstruktorfüggvény var Child = function Child() { this.name = 'Anonymous'; } //Konstruktorhívási minta var c = new Child();
c.name === 'Anonymous'
Függvény mint objektum 5
Adattagok és metódusok prototype,
length, call(), apply()
prototype: egy objektum, aminek egyetlen constructor nevű adattagja a függvényre mutat
Konstruktorhívás folyamata 6
üres objektum keletkezik, prototípusa a függvény prototype-jában megadott objektum a this hivatkozik rá adattagok és metódusok hozzáadása történik függvény végén impliciten visszaadjuk a this által mutatott objektumot (hacsak nem adunk vissza expliciten valami más objektumot)
Szemléltetés 7
var Child = function Child() { //Új objektum létrehozása a this-ben var this = Object.create(Child.prototype); //További tulajdonságok hozzáadása this.name = 'Anonymous'; //Visszatérés a létrehozott objektummal return this; }
Konstruktor visszatérési értéke 8
Konstruktorok impliciten a this-szel térnek vissza, ha nincsen return Ha van, akkor azzal az objektummal tér vissza var Child = function Child() { this.name = 'Anonymous'; return { something: 'else' }; } var c = new Child(); c.name === undefined c.something === 'else'
Konstruktor- vs. függvényhívás 9
new nélkül hívva meg egy konstruktorfüggvényt, a this a globális objektumra mutat Veszélyes! Megoldások Kódolási konvenció (nagy kezdőbetűs konstruktorok) that használata Hívási környezet észlelése Kerüljük a konstruktorhívási mintát
var Child = function Child() { this.name = 'Anonymous'; } var c = Child(); typeof c === undefined window.name === 'Anonymous'
that használata 10
this helyett adjuk a that-hez és térjünk vele vissza var Child = function Child() { var that = Object.create(Child.prototype); that.name = 'Anonymous'; return that; } var c1 = new Child(); var c2 = Child(); c1.name === 'Anonymous' c2.name === 'Anonymous' Object.getPrototypeOf(c2) === Child.prototype
Hívási környezet észlelése 11
Konstruktorban vizsgáljuk meg hova mutat a this Ha nem a konstruktorra, akkor new-val hívjuk meg var Child = function Child() { if (!(this instanceof Child)) { return new Child(); } this.name = 'Anonymous'; } var c1 = new Child(); var c2 = Child(); c1.name === 'Anonymous' c2.name === 'Anonymous' Object.getPrototypeOf(c2) === Child.prototype
Konstruktorhívási minta hátrányai 12
A prototípus-objektum beállítására van natív metódus Korábban
csak a konstruktorfüggvényeken keresztül
lehetett Most: Object.create(proto)
Explicit return hibára vezethet Elfelejtett new operátor Megoldás
körülményes
Konstruktorhívási minta hátrányai 13
Azt sugallja, hogy vannak osztályok nincsenek
osztályok a függvény nem osztály
Alternatívát állít a nyelv természetes adottságainak Klasszikus OOP fogalomkör kevésbé rugalmasm megoldásokat ad szoros kapcsolat két osztály között Öröklés helyett kompozíció (ld. GoF könyv) öröklés
Konstruktorhívási minta 14
Ismernünk kell! Jórészt ez terjedt el.
15
Objektumlétrehozási minták klasszikus OOP szintaxissal
Gyárfüggvények 16
Konstruktorfüggvény = gyárfüggvény var Child = function () { this.name = 'Anonymous'; this.dateOfBirth = { year: 1970, month: 1, day: 1 }; this.getName = function getName() { return this.name; }; this.setName = function setName(name) { this.name = name; }; };
Paraméteres gyárfüggvény 17
var Child = function (props) { extendDeep(this, { name: /*...*/, dateOfBirth: /*...*/, getName: /*...*/, setName: /*...*/ }, props || {}); };
Privát adattagok és privilegizált metódusok 18
var Child = function (props) { //Privát adattag var secretNickName = ''; extendDeep(this, { name: /*...*/, dateOfBirth: /*...*/, getName: /*...*/, setName: /*...*/, //Privilegizált metódusok setSecretNickName: function (name) { secretNickName = name; }, getSecretNickName: function () { return secretNickName; } }, props || {}); };
Metódusok hatékony tárolása 19
var Child = function (props) { extendDeep(this, { name: /*...*/, dateOfBirth: /*...*/ }, props || {}); }; extendShallow(Child.prototype, { getName: /*...*/, setName: /*...*/ });
Child.prototype constructor getName setName
Child prototype name dateOfBirth
Becsomagolás "osztály-modulba" 20
var Child = (function () { var Child = function (props) { extendDeep(this, { name: /*...*/, dateOfBirth: /*...*/ }, props || {}); }; extendShallow(Child.prototype, { getName: /*...*/, setName: /*...*/ }); return Child; })();
21
Öröklési minták klasszikus OOP szintaxissal
Sematikus ábra 22
Öröklés prototípuslánccal 23
Öröklés prototípuslánccal 24
P.prototype constructor
C.prototype prototype constructor
P prototype
C prototype
var inherit = function (C, P) { var F = function () {}; F.prototype = P.prototype; C.prototype = new F(); C.prototype._super = P.prototype; C.prototype.constructor = C; };
Öröklés – példa 25
var Preschool = (function (_super) { var Preschool = function (props) { extendFunc(this, _super); //vagy paramétert is átadva: //_super.call(this, props) extendDeep(this, { sign: 'default sign' }, props || {}); };
inherit(Preschool, _super); extendShallow(Preschool.prototype, { getSign: function getSign() { return this.sign; }, setSign: function setSign(sign) { this.sign = sign; }, getName: function getName() { var name = this._super.getName.call(this); return name + ' (preschool)'; } }); return Preschool; })(Child);
26
ES6 objektumkezelés
Gondok 27
Többféle lehetőség adatok és metódusok egységbe zárására Objektumgenerátorok Klasszikus OOP-t szimuláló függvénykönyvtárak Nem felcserélhetőek
ES6 28
Nyelvi szintű megoldás class kulcsszó DE ezek
továbbra is objektumok syntactic sugar függvények
és prototípusaik
ES6 osztály – példa 29
class Child { constructor(name, dateOfBirth) { this._name = name; this.dateOfBirth = 100; } say(something) { return this.name + ' says: ' + something; } get name() { return this._name; } set name(value) { if (value === '') { throw new Error('Name cannot be empty.'); } this._name = value; } }
ES6 öröklés – példa 30
class Preschool extends Child { constructor(name, dateOfBirth, sign) { super(name, dateOfBirth); this._sign = sign; }
get sign() { return this._sign; } set sign(value) { this._sign = value; } get name() { return super.name + ' (preschool)'; } }
Kipróbálás 31
Traceur (Google) TypeScript (Microsoft)
33
Kódszervezés és modularitás
Kódszervezési koncepciók 34
Cél
Kódszervezés
áttekinthetőség
függvény
karbantarthatóság
osztály
továbbfejleszthetőség
modul
Tagolás függ
az alkalmazás méretétől
Függvény 35
Elsődleges feladat strukturális
tagolás általánosított részfeladat (paraméterezés)
Az alkalmazás építőelemei Kis méretű alkalmazások tagolására elegendők
Irányelvek 36
Ne legyen ismétlődő kódrészlet (DRY) A függvény egy dologért legyen felelős (DOT) Legyen minél egyszerűbb (KISS) A kevesebb néha több Kerüljük a mellékhatásokat
Tiszta kód
Osztály 37
Funkcionális egységhez tartozó adatok feldolgozó
függvények
Egységbe zárás interfész implementáció
Modul 38
Egy adott funkcionalitást megvalósító független egység Alkalmazás egészének része Építőelemek Részei interfész implementáció
Más nyelvekben C++:
#include Pascal: uses Java: import
Modularitás alapelvei 39
Specializált Független Leválasztható Újrafelhasználható Helyettesíthető
Interfész tervezése 40
Nyílt-zárt szabály nyitott
a bővítésre zárt a módosításra
Megjósolhatóság Többrétegűség
Modul vs osztály 41
Hasonlók interfész implementáció
Modul fájlszintű
tagolás függőségek meghatározása
Sokszor 1 modul = 1 osztály
42
Kódszervezés JavaScriptben
Nyelvi lehetőségek 43
Objektumok Függvények
Függvények 44
Aszinkronitás callback
Tagolás
függvények
var elems = [], push = function (e) { elems.push(e); }, pop = function () { return elems.pop(); }, top = function () { return elems[size()-1]; }, size = function () { return elems.length; };
Objektumok 45
Adatok és függvények a globális névtérben keverednek funkcionális egység: objektumliterál Probléma minden
publikus inicializáló kód külön függvénybe
var stack = { elems: [], push: function (e) { this.elems.push(e); return this; }, pop: function () { return this.elems.pop(); }, top: function () { return this.elems[this.size()-1]; }, size: function () { return this.elems.length; } }; stack.push(10).push(20); stack.top() === 20 stack.size() === 2 stack.elems.join(',') === '10,20'
Névterek 46
Nincs rá nyelvi elem Objektumliterállal szimulálható Sok objektum helyett tetszőleges objektumhierarchia var myApp = myApp || {}; myApp.dataStructures = myApp.dataStructures || {}; myApp.dataStructures.stack = { elems: [], push: function (e) { /* ... */ }, pop: function () { /* ... */ }, top: function () { /* ... */ }, size: function () { /* ... */ } };
namespace() segédfüggvény var MyApp = MyApp || {}; 47 MyApp.namespace = function (ns) { var parts = ns.split('.'), parent = MyApp, i; //Ha MyApp-pal kezdõdik, akkor kihagyható if (parts[0] === "MyApp") { parts = parts.slice(1); } for (i = 0; i < parts.length; i += 1) { //Nem létezõ tulajdonság létrehozása if (typeof parent[parts[i]] === "undefined") { parent[parts[i]] = {}; } parent = parent[parts[i]]; } return parent; }; var stack = MyApp.namespace('MyApp.dataStructures.stack'); stack = { /* ... */ }; //vagy MyApp.namespace('dataStructures.stack') = { /* ... */ };
Modul minta 48
Függvény hatókört
ad closure-t definiál: implementációs részletek elrejtése
Objektumot ad vissza var module = (function () { //Inicializáló kód //Rejtett változók és függvények //Visszatérés egy objektummal return { //Publikus interfész }; })();
Példa 49
Privát adattagok this
nélkül
Publikus adattagok this-szel
Osztályok emulálása
var stack = (function () { var elems = []; return { push: function (e) { elems.push(e); return this; }, pop: function () { return elems.pop(); }, top: function () { return elems[this.size()-1]; }, size: function () { return elems.length; } }; })(); stack.push(10).push(20); stack.top() === 20 stack.size() === 2 stack.elems === undefined
Modul minta variánsai 50
Névterek Globális változók importálása Modul módosítása Gyárfüggvény visszaadása Felfedő modul minta Alkalmazásfüggetlen modul minta
Névterek 51
A modul által visszaadott objektumot névtér alá teszik Eredeti verzió
MyApp.namespace('dataStructures.stack') = (function () { /* ... */ })();
Globális változók importálása 52
Paraméterként megadni a külső függőségeket Ugyanannak a könyvtárnak több verziója is használható egyszerre var module = (function (win, doc, $, undefined) { /* ... */ })(window, document, jQuery);
Modul módosítása 53
Egy modul akár több fájlban Betöltési sorrendtől függően module vagy {} Felülírt metódusok használata var module = (function (module) { var old_method = module.method; /* ... */ return extendDeep(module, { //Kiegészítés, illetve felülírás method: function () { var old = old_method(); /* ... */ } }) })(module || {});
Gyárfüggvény visszaadása 54
Ld. a gyárfüggvények modulba csomagolását! var module = function () { }; //vagy var module = (function () { var Constr = function () {} { /* ... */ }; return Constr; })();
Felfedő modul minta 55
Eredeti modul mintában másképpen kell hivatkozni a privát és publikus adattagokra Felfeldő modul mintában minden
adat és metódus a rejtett részben deklarálva visszatérési objektum tulajdonságaihoz rendeljük hozzá azokat
Mindig this nélkül kell hivatkozni.
Példa 56
var stack = (function () { var elems = [], push = function (e) { elems.push(e); return this; }, pop = function () { return elems.pop(); }, top = function () { return elems[size()-1]; }, size = function () { return elems.length; };
return { push: push, pop: pop, top: top, size: size }; })();
Alkalmazásfüggetlen modul minta 57
Modul minta vagy
a globális névtér bővül vagy névtér
Névtér
(function (exports) { /* ... */ extendDeep(exports, { //Publikus interfész }) })(exports);
modul
definíciójába belekerül a modul névtere külső függőségekre is csak így hivatkozhatunk nem hordozható a modul kódja
exports objektum bővítése a modulon belül Rajta kívül döntjük el, hogy mit bővítünk vele
58
Modulkezelő könyvtárak
Modulkezelő könyvtárak 59
Két módszer CommonJS Aszinkron
Modul Definíció (AMD) +1: ES6 modul
Mindegyik az alkalmazásfüggetlen modul mintára vezethető vissza Két részük van modulok
definiálása (export) függőségek kezelése (import)
CommonJS 60
Definiálás Egyszerű
szintaxis Minden modul külön fájlban Nincs hatókört biztosító függvény Az API-t az exports objektumhoz kell kötni
Importálás require
metódus szinkron módon
Fő elterjedése szerveroldalon
CommonJS példa 61
//stack.js var elems = [], push = function (e) { /* ... */ }, pop = function () { /* ... */ }, top = function () { /* ... */ }, size = function () { /* ... */ }; extendDeep(exports, { push: push, pop: pop, top: top, size: size });
//app.js var stack = require('./stack.js'); stack.push(10).push(20);
Aszinkron Modul Definíció (AMD) 62
Kliensoldalon nem jó a szinkron betöltés vagy
megállítja a böngészőt párhuzamos betöltésnél a függőségek kezelése nem megoldott
Definiálás define()
fügvény [modulnév,] függőségek, hatókört adó függvény paraméterben a függőségek
Importálás require
blokk vagy függvény
AMD példa 63
define(modulnév, [modul1, modul2], function (modul1, modul2) { //Modul definíciója return { //Publikus API } define('stack', [], function () { }); var elems = [], push = function (e) { /* ... */ }, pop = function () { /* ... */ }, top = function () { /* ... */ }, size = function () { /* ... */ }; return { push: push, pop: pop, top: top, size: size }; });
require(['stack'], function (stack) { stack.push(10).push(20); });
Require.JS (AMD) 64
<meta charset="utf-8">
<script type="text/javascript" src="require.js" data-main="main.js">
require(['stack'], function (stack) { stack.push(10).push(20); });
Egységes megközelítés 65
Unified Module Definition (UMD) (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module. define(['exports', 'b'], factory); } else if (typeof exports === 'object') { // CommonJS factory(exports, require('b')); } else { // Browser globals factory((root.commonJsStrict = {}), root.b); } }(this, function (exports, b) { //use b in some fashion. // attach properties to the exports object to define // the exported module properties. exports.action = function () {}; }));
EcmaScript 6 modulok 66
Modulok egységes kezelése a cél Definiálás module
kulcsszó import: függőségek behúzása export: az API megadása
EcmaScript 6 modul – példa module 67
staff{ // specify (public) exports that can be consumed by // other modules export var baker = { bake: function( item ){ console.log( "Woo! I just baked " + item ); } module cakeFactory{ } // specify dependencies } import baker from staff; module skills{ // import everything with wildcards export var specialty = "baking"; import * from skills; export var experience = "5 years"; } export var oven = { makeCupcake: function( toppings ){ baker.bake( "cupcake", toppings ); }, makeMuffin: function( mSize ){ baker.bake( "muffin", size ); } } }