ugrás a tartalomhoz

Method overloading (JavaScript)

Karvaly84 · 2012. Május. 23. (Sze), 15.29
Helló guruk!

Párszor esett már szó Weblaboron az overloading-ról a JavaScript vonatkozásában, de nem beszéltük teljességgel a dolgot. Szeretném bővíteni a repertoáromat a rugalmasság érdekében, és érdekelne a véleményetek/tapasztalatotok a kérdésben.

Több egyszerűbb megvalósítást láttam már, ahol csupán a paraméter szám alapján lett implementálva a dolog, vagy éppen a natív typeof-ra alapozva.

Az instanceof köztudott, hogy nem ugyan azt adja vissza minden esetben IE alatt mint más böngészőkben, pl. a Window vagy a Node objektumok esetében, viszont mégis jó lenne túl menni a typeof képességein.

Van egy két ötletem, de nem tudom meddig érdemes elmenni a megvalósításban. Aki ráér, kifejthetné gondolatait a témával kapcsolatban.
 
1

Tudnál mondani egy példát,

inf · 2012. Május. 23. (Sze), 16.25
Tudnál mondani egy példát, aminél értelme van overloading használatának?
3

Például van egy függvényed,

Hidvégi Gábor · 2012. Május. 23. (Sze), 16.44
Például van egy függvényed, amivel bizonyos adatokon lehet szűrést végezni, és nagyszámú paraméterrel dolgozik, de nem akarod mindig kitölteni a teljes paraméterlistáját.

show_ingatlans({id: 1234});

show_ingatlans({helyseg: "budapest", ar1: "5M", ar2: "10M"});
7

Hát ez pl nagyon nem

inf · 2012. Május. 23. (Sze), 19.55
Hát ez pl nagyon nem overload...
5

Példát ezret is tudnék... Pl.

Karvaly84 · 2012. Május. 23. (Sze), 19.37
Példát ezret is tudnék... Pl. a Poetro által lentebb említett jQuery.fn.css

De a jQuery sem végez típus ellenőrzést csak ha az adott függvényben erre külön kitérnek.

Ha pl. egy hasonló függvényt szeretnék mint a jQuery.fn.css, akkor én úgy szeretném hogy:

- (String name, String value) - vagyis két String típusú adat a stílus beállításához,
- vagy (StyleMap styles) - egy olyan objektum ami definiál több tulajdonságot, és beállítja azokat,
- vagy () az az semmi - ami mondjuk vissza adja az adott elemhez tartozó StyleMap-et.
- minden egyéb esetben ha pl. a paraméter egy Object de nem köthető a StyleMap osztályhoz akkor kivétellel tovább menni.
2

Objektum paraméternek

Hidvégi Gábor · 2012. Május. 23. (Sze), 16.36
Én ezt úgy szoktam megoldani, hogy a függvénynek paraméterként objektumot adok át, így szabadon változtatható minden.
6

Ezt én is szoktam, csak most

Karvaly84 · 2012. Május. 23. (Sze), 19.52
Ezt én is szoktam, csak most azt akarom elérni hogy pl. egy változó paraméterlistával tudjak dolgozni amiknek típusa határozza meg egy függvény működését. Persze úgy, hogy ne keljen mindig az if (argument instanceof Class) típusú blokkokat használnom.
8

Maga a probléma nagyon

inf · 2012. Május. 23. (Sze), 20.29
Maga a probléma nagyon hasonló egy router/dispatcher/switch -hez. Ott ugye az url.path-et darabolod fel node-okra, és utána az alapján osztod el a kéréseket. Két megoldás van rá, vagy csinálsz egy fát, amin végig tudsz menni a lehetséges csomópontokon, vagy csinálsz egy tömböt, amiben felsorolod az összes útvonalat. Ha vannak azonos csomópontok, (pl az első paraméter-nél ugyanazok a kikötések), akkor nyilván az kód duplázódást eredményez.

var condition1 = {argument: 1, type: String};
var condition2 = {argument: 2, type: Number};
var condition3 = {argument: 1, type: Number};
var f1 = function (str){}
var f2 = function (str,num){}
var f3 = function (num){}

Function.overload(
	[
		condition1, f1,
		[
			condition2, f2
		]
	],
	[
		condition3, f3
	]
);

Function.overload(
	[
		condition1, f1
	],
	[
		condition1, condition2, f2
	],
	[
		condition3, f3
	]
);
Amivel még lehet játszani az a feltételek sorrendje, pl lehet olyat csinálni, hogy a sorrend alapján mondja meg, hogy melyik paraméterre vonatkozik, vagy olyat is lehet, hogy a függvényből toString-el kiszedjük a paraméterek neveit, és azokra hivatkozunk. Amit még esetleg lehet, hogy magukban a függvényekben tárolod le, hogy mik az elvárásaik a paraméterek szempontjából, pl:

Function.overload(
	function (str,num){}.validate({
		str: String,
		num: Number
	}),
	function (str){}.validate({
		str: String
	}),
	function (num){}.validate({
		num: Number
	})
)
Nyilván itt a teljes útvonalakat kell megadni, nem lehet a csomópontokkal játszani...

Ugyanez chain-ben (mondjuk én ezt nagyon nem szeretem):

function (str,num){}
.overload(
	{str: String,num: Number},
	function (str){}
)
.overload(
	{str: String},
	function (num){}
)
.overload(
	{num: Number},
	function (){alert("hibás paraméter lista")}
)
Vagy chain2:

function (str,num){}.validate({str: String,num: Number})
.overload(function (str){}.validate({str: String}))
.overload(function (num){}.validate({num: Number}))
Ez sem tetszik, mert a blokk után van, hogy mik az elvárásaik paraméterek terén.

Ha olyat akarsz, hogy a függvények saját maguk is ellenőrizzék a paraméter listát, és kivételt dobjanak, akkor mindenképp kell nekik egy isValid(args) metódus, amivel tudathatják az overload-dal, hogy elfogadják e a paraméter listát vagy sem. A kivételek elkapása azért nem játszik, mert nem tudod megállapítani, hogy milyen mélységből jönnek, szóval hogy a függvény paraméterlistájának a validálása váltotta e ki őket, vagy a függvény blokkjában található kód.

Nem tudom ezeken kívül még milyen megoldások vannak, majd másold be, hogy melyik jött be neked...

Én mondjuk amellett vagyok, hogy ilyesmik általában akkor fodulnak elő, ha egy viszonylag nagy kódrészlethez akarsz egy egységes felületet csinálni. Ebben az esetben szerintem érdemes egy külön függvényt csinálni, amiben kézzel ellenőrzöd a paraméter listát, és aszerint osztod tovább a kéréseket. Nem hiszem, hogy érdemes erre általános megoldást készíteni ha nincsenek kész validator-aid, amiket amúgy máshol is felhasználsz...
9

Igazából onnan jött nekem az

Karvaly84 · 2012. Május. 23. (Sze), 23.00
Igazából onnan jött nekem az ötlet, hogy régebben csináltam 3 osztályt, amik az eseménykezelést valósítják meg. Kb. a használatuk és a felületük megegyezik a DOM specifikációban leírtakkal (anno erről akartam írni de később meggondoltam magam).

Nos van egy EventTarget aminek van egy dispatch metódusa ami ugye egy Event objektummal működik alap esetben. A dispatch metódust akarom átírni úgy, hogy ha nem egy Event objektumot kap akkor készítsen egyet és dolgozzon avval. A dispatch ebben az esetben plusz két esettel bővül, és így 3 alak lehetséges:

- dispatch(Event e)
- dispatch(String type)
- dispatch(String type, Object detail)

Ha nincs egyezés akkor hiba.

Ennek apropóján jutott eszembe az overloading, amit azért szeretnék mert a kódom még nem annyira nagy hogy az osztályokat nagy meló legyen átírogatni, és a későbbiekben ahogy bővítem a könyvtáramat akkor támaszkodhatnék erre a hasznos funkcióra ami a js-ből kimaradt.

------------------

Ez sem tetszik, mert a blokk után van, hogy mik az elvárásaik paraméterek terén.

Na ja ezt jobb elől látni mik az elvárások, de végső soron működhet ha máshogy nem lehet. :)

Egyébként még olyan problémám is van, hogy nem tudom hogy kezeljem azokat a objektumokat amiknek primitív típusa is van. Pl. ha van egy olyan feltétel hogy (String type) és függvényhíváskor egy primitív típussal paramétereznek akkor azt át kell e engedni vagy nem. Csak mert eléggé hosszú listákat lehet írni ha úgy kel írogatnom a függvényhívásokat, hogy: valamiHivas(new String("arg1"), new String("arg2")), szóval ezért kértem a véleményeteket mert sok a kérdőjel... :D
15

Csináltam egy kisebb

inf · 2012. Május. 24. (Cs), 16.59
Csináltam egy kisebb típusellenőrzőt. Ez csak a tényleges Object-ekre adja, hogy object. A natív típusokat meg lehet natív osztályokkal csekkolni vele. A DOM az nincs benne, ott vannak vicces dolgok.

Ami érdekesség: a null object típusú, de nem Object instance, az undefined ellenben már undefined típusú. A natív osztályok példányai Object instance-ok is egyben, ami valahol érthető lenne, ha máshol nem lenne káosz. Régebbi msie-nél vagy 9-esnél ha nem adsz meg doctype-ot, akkor a DOM kezelhetetlen, mert nem Object instance-ok vannak benne, és ezért hibával elszállnak, ha tulajdonságot akarsz elkérni vagy beállítani. Azt hiszem régebbi opera-nál a DOM Node-ok típusa function volt, nem tudom, most mi a helyzet vele. A NaN (not a number) típusa number. ^^
Kihagytam valamit?

Function.prototype.type = "object";
Function.type = "function";
String.type = "string";
Number.type = "number";
Boolean.type = "boolean";
Function.prototype.check = function (value) {
	return (typeof(value) != Object.type)?(typeof(value) == this.type):(value instanceof this);
};
Null = function (){return null};
Null.type = "undefined";
Null.check = function (value) {
	return (value === null || value === undefined);
};
Object.check = function (value) {
	return !Null.check(value) && Function.prototype.check.apply(this,arguments) && (value.constructor.type == Object.type);
};
Number.check = function (value){
	return !isNaN(value) && Function.prototype.check.apply(this,arguments);
}

console.log(
	Object.check({}) && Object.check(new Date()) && Object.check(/aa/) && !Object.check(null) && !Object.check(undefined) && !Object.check(new String()) && 
	Function.check(function (){}) && Function.check(new Function) && !Function.check({}) && !Function.check(0) &&
	String.check("string") && String.check(new String()) && !String.check(0) && !String.check(null) &&
	Number.check(0) && Number.check(1) && !Number.check(NaN) && !Number.check("0") && !Number.check(null) && !Number.check(undefined) &&
	Boolean.check(false) && Boolean.check(true) && Boolean.check(new Boolean()) && !Boolean.check(null) && !Boolean.check(undefined) && !Boolean.check(0) &&
	Null.check(undefined) && Null.check(null) && !Null.check(0) && !Null.check("") && !Null.check({})
)
A konkrét példához visszatérve, én csekkolnám a helyedben, hogy az első paraméter Event instance e, és ha nem, akkor elküldeném egy factory-nek, a paraméter listát, hogy csináljon belőle event-et. Nem igazán látom értelmét az overload-nak csak azért, hogy a típusellenőrzés ne a függvényen belül legyen, hanem azon kívül... Pár if-else az egész...
19

Object.prototype.toString.call

T.G · 2012. Május. 24. (Cs), 20.22
Az objektum vizsgálatot a null miatti anomália miatt szokták még a Object.prototype.toString.call –lal is ellenőrizni:

Object.prototype.toString.call(null); // "[object Null]"
Object.prototype.toString.call(undefined); // "[object Undefined]"
Object.prototype.toString.call({}); // "[object Object]"
Object.prototype.toString.call(new String()); // "[object String]"
UPDATE: IE7 alatt az első kettő szintén Object. :(
20

A null-nál az a zavaró, hogy

inf · 2012. Május. 24. (Cs), 21.42
A null-nál az a zavaró, hogy typeof-ra object, instanceof-ra viszont már nem. Ha már js szintaxist követünk, akkor szerintem úgy lenne logikus, ha az instanceof Object mindenre true-t adna, az undefined eltűnne, az instanceof meg a sima sztring típusra (nem String példányra) is működne, meg mondjuk lenne olyan, hogy Null osztály, meg mondjuk valami Native osztály, aminek a leszármazottja lenne a Null, String, Number, Boolean, RegExp, Date, Array, Function és amire szintén lehetne tesztelni.
pl:

"aaa" instanceof String -> true
null instanceof Null -> true
"aaa" instanceof Native -> true
{} instanceof Native -> false
"aaa" instanceof Object -> true
{} instanceof Object -> true
stb...
Ami még nem tetszik az az instanceof feldolgozása. Legalábbis ha jól tudom, akkor így működik:

"aaa" instanceof String && "bbb" instanceof String
->
("aaa" instanceof String && "bbb") instanceof String
->
false
Ha öröklés van, akkor külön teszt kell az ős osztályok megnézésére is. Kéne valami szabványt csinálniuk arra, hogy hogyan örököltetünk, hogy utána lehessen ezzel kapcsolatban is típust ellenőrizni. Amit még hiányolok az az interface-ek valamilyen formában. Mondjuk legyen meg minden metódus, és egyezzen meg a paraméter lista. Típust nem kéne ellenőrizni, csak legalább ez lenne automatizálva. Na mindegy, álmodik a nyomor :D Nekem egyébként jó így is a js, nem szeretném, ha olyan féltípusos gányolt nyelvet csinálnának belőle, mint a php....
4

jQuery példája

Poetro · 2012. Május. 23. (Sze), 17.04
A jQuery ugye megenged többféle szintaxist ugyanarra a függvényre, ami esetleg befolyásolja a függvény működését (pl. css), illetve automatikusan átrendezi a paramétereket azok típusától függően (bind). Ha ez nem nyújt elegendő rugalmasságot, akkor fogadhatsz paramétereket objektum formájában, majd ezeket vagy kiegészíted alapértelmezett értékekkel, vagy pedig pusztán objektumban megadott tulajdonságokra (illetve azok meglétére) alapozol. Ez utóbbit is alkalmazza a jQuery például az ajax függvényben
10

Pontosan mi a cél?

T.G · 2012. Május. 24. (Cs), 08.02
Pontosan mi a cél? Függvénynevekkel spórolás? Nekem néha a JQuery megoldásai is neccesek. Az még rendben van, hogy height() visszaadja az magasságot, height(100) állítja a magasságot és visszaadja az elemet, de a $.extend első paramétere true vagy sem, az azért nehezen magyarázható.

Igaz, hogy néhány karakterrel hosszabb, de a getHeight(), setHeight(100), apply illetve applyIf sokkal olvashatóbb kódot eredményez. És felesleges ellenőrzésektől mentes, kód írásakor pontosan tudom, hogy mit szeretnék, nem kell a böngészőnek futás időben kitalálni…

A fenti show_ingatlans példát még nézni is rossz. Bocs, de tényleg!

Amit én használnék, ha az lenne a cél, hogy 1. átadhatunk egy elemet, vagy tömbben több elemet, illetve 2. átadhatunk ingatlan azonosítót, vagy ingatlant, akkor valami ilyesmit csinálnék:

function showRealEstates (list) {
	list = [].concat(list);
	for (var i = 0; i < list.length; i++) {
		if (typeof list[i] === 'number') {
			list[i] = getRealEstateById(list[i])
		}
		// ...
	}
}
Ám igyekszem még ezeket is kerülni...
11

Amit szeretnék az a

Karvaly84 · 2012. Május. 24. (Cs), 08.58
Amit szeretnék az a típusellenőrzések ki kerülése a függvényekben, azokban az esetekben amikor azok a argumentumok helyességére irányulna. Illetve azon függvények amik egy feladatot hajtanak végre, és változó hosszúságú, illetve típusú adattal képesek dolgozni, leredukálni egy függvényre, ahol a törzsben már csak a feladattal kel foglalkozni. Magyarán szólva a poliformizmus egyfajta megvalósulása a célom, hogy szebb, jobb, és könnyeben fejleszthető kódtömegen dolgozzak.

Pl. amit nem szeretek az a sok if else ág, és amikor változók tartalmát cserélgetem fel, a változó paraméterhossz miatt.
13

Egy típusnélküli nyelvben nem

Hidvégi Gábor · 2012. Május. 24. (Cs), 09.08
Egy típusnélküli nyelvben nem tudod kikerülni a típusellenőrzést. Ha nagyon nem tetszik a rendszer, írj másik függvényt erre a feladatra, hogy külön tudjad választani a szezont a fazontól.
14

Más nyelv

Poetro · 2012. Május. 24. (Cs), 09.46
Ehhez szerintem használj más nyelvet, ami JavaScript-re fordul, például lehet a Darts, Clojure, Haskell képes erre. Már csak azért is, mert a JavaScript alapértelmezetten nem alkalmas erre, ezért felesleges megerőszakolni, mert elveszted a nyelv hatékonyságának alapjait. De érdemes azért rákeresni a JavaScript function overloading-ra a kedvenc keresődben, és azért lehet találni pár érdekes megoldást.
16

+1, szerintem sincs értelme

inf · 2012. Május. 24. (Cs), 17.08
+1, szerintem sincs értelme (amúgy próbálkoztam vele régebben én is...)
22

A GWT nem jött be. A Darts

Karvaly84 · 2012. Május. 24. (Cs), 22.36
A GWT nem jött be. A Darts amivel szemezgetek csak még kivárási fázisban vagyok, hogy mi lesz annak a jövője, a másik kettőt meg megnézem, köszi a tippet.
12

A fenti show_ingatlans példát

Hidvégi Gábor · 2012. Május. 24. (Cs), 09.06
A fenti show_ingatlans példát még nézni is rossz. Bocs, de tényleg!
Miért? A te megoldásod miben jobb? Van egy listád típusnélküli adatokkal, aztán mindenféle mágiával kell eldöntened, hogy vajon melyik elemben mi lehet. Hogy különbözteted meg például a "Budapest"-et a "Somogy megyé"-től?
17

Mea culpa!

T.G · 2012. Május. 24. (Cs), 19.00
Mea culpa! Utólag vettem észre, hogy félreérttetem a show_ingatlans nevű függvényedet! :) Bár mentségemre legyen, hogy a paraméter túlterhelés témában ingatlanok megmutatása nevű függvényt hívtál meg egyszer úgy, hogy egy id-t (objektumba csomagolva) adtál, egyszer meg egyéb objektumot. A szövegen (valahogy) átugrottam, és nem esett le, hogy ez egy szűrő függvény. Inf3rno megjegyzését meg szimplán betudtam annak, hogy nem nagyon jól néz ki. :)

Az általad mutatott két paraméter az ugyanolyan szűrő, gyakorlatilag semmi különbség nincs benne:

var list = [
    {id: 1234, helyseg: "Esztergom"},
    {id: 2000, helyseg: "budapest", ar1: "5M", ar2: "10M"}
];
function show_ingatlans (filter) {
    ciklus: for (var i = 0; i < list.length; i++) {
        for (var f in filter) {
            if (filter.hasOwnProperty(f)) {
                if (list[i][f] !== filter[f]) {
                    continue ciklus;
                }
            }
        }
        console.log(list[i]);
    }
}
show_ingatlans({helyseg: 'Esztergom'});
Ami meg a mágiát illeti, magam részéről a rövid, egyértelmű függvényeket támogatom, ám nagyon ritkán megengedhető, hogy kihasználjuk a JS gyenge típusosságát és két nagyon hasonló függvényt összevonjunk, például ilyen a lista / egy elem átadása, vagy az azonosító átadása/konkrét elem átadása. De ahogy a fenti bekezdésben írtam, az a függvény nem szól másról, mint a paraméterben kapott elemek megjelenítéséről, amikor azt írtam nem tudtam, hogy ennek a függvénynek szűrnie is kell. :)
18

Fátylat rá

Hidvégi Gábor · 2012. Május. 24. (Cs), 19.14
A szűrést csak példának hoztam, hogy az "overloading"-ra nekem eddig milyen esetben volt szükségem, mert ez tipikusan olyan függvény, hogy sok bemenő paramétere lehet, de azokból nem mindig szerepel mindegyik.
21

Srácok köszönöm az eszme

Karvaly84 · 2012. Május. 24. (Cs), 22.35
Srácok köszönöm az eszme cserét. Igazából bekövetkezett amitől féltem... Megint darázsfészekbe nyúltam a JavaScript-el :D Valahogy mindig sikerül... :(
23

Igazából ez a paraméterezés

inf · 2012. Május. 25. (P), 14.53
Igazából ez a paraméterezés nem annyira egyértelmű téma. Nekem most legtöbbször két dologgal van gondom ezzel kapcsolatban:
- ha asszociatív tömböt adok át, akkor nem tudok automatikus kiegészítést használni hozzá, viszont ha meg nem azzal paraméterezem az osztályaimat, akkor meg egy tonna kódot és vátozót használok, és nem lesz annyira átlátható
- vannak olyan metódusok, amiknek akkor kell csak egyszer lefutnia, ha minden paraméter be van állítva, sem előbb, sem később
A dependency injection megoldás lenne ezekre, csak úgy érzem, hogy 2-3 paraméter miatt még egy osztályt behozni a képbe kicsit túlbonyolítása a problémának. Jó lenne, ha lenne egy olyan nyelv, aminél a paraméterezés alapból asszociatív tömbbel menne, és működne rá az automatikus kiegészítő.
24

Ruby, Objective-C

MadBence · 2012. Május. 25. (P), 16.08
Ezeknél kb így működik a metódushívás (A ruby ugye kényelmesen hasheket használ, ez viszont nem működik olyan jól a kódkiegészítőkkel, az objective-c meg alapból nevesíti a paramétereket, jól működik a kódkiegészítőkkel, cserébe nyelvi szinten nem egy hashként kell rá gondolni, mondjuk szerintem neked sem ez lenne a lényeg).