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 szintaktikai hasonlóság var c = new Child(); 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 öröklés szoros kapcsolat két osztály között Öröklés helyett kompozíció (ld. GoF könyv)
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 {); ;
18 Privát adattagok és privilegizált metódusok 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
22 Sematikus ábra
23 Öröklés prototípuslánccal
Ö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 áttekinthetőség karbantarthatóság továbbfejleszthetőség Tagolás függ az alkalmazás méretétől Kódszervezés függvény osztály modul
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 függvények Tagolás var elems = [], push = function (e) { elems.push(e);, pop = function () { return elems.pop();, top = function () { return elems[size()-1];, size = function () { return elems.length; ;
45 Objektumok 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 ; )();
49 Példa 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.
56 Példa 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 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 (function (exports) { /*... */ extenddeep(exports, { //Publikus interfész ) )(exports); 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 <!doctype html> <html> <meta charset="utf-8"> <head> <title></title> <script type="text/javascript" src="require.js" data-main="main.js"></script> </head> <body> </body> </html> 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 67 module 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{ export var specialty = "baking"; export var experience = "5 years"; // import everything with wildcards import * from skills; export var oven = { makecupcake: function( toppings ){ baker.bake( "cupcake", toppings );, makemuffin: function( msize ){ baker.bake( "muffin", size );