[js] wisselend aantal functie argumenten

Pagina: 1
Acties:

  • Clay
  • Registratie: Oktober 1999
  • Laatst online: 22-06 13:51

Clay

cookie erbij?

Topicstarter
Voor Beehive II ben ik bezig met een soort generieke opzet voor animatie functionaliteiten in dhtml; zo heb ik voor een layer een "moveTo" gedefinieerd, maar ik wil geen "slideTo" definieren, omdat dat inhoudt dat ik voor alles wat je met css kan doen een animatie variant moet maken. Een setOpacity zou dan een fadeTo moeten krijgen, en zo krijg je een giga stamp code om alles wat er is te kunnen animeren, waarvan je het meerendeel toch nooit gebruikt.

Wat ik dus nu verzonnen heb is een "run" functie, en die kan je een moveTo, of een setOpacity of [enz] voeren, met een paar parameters:

JavaScript:
1
2
3
// voorbeeld
dynObject.run('moveTo', 
   {from:[20,20], to:[100,100], time;2000});


Het probleem is alleen dat een moveTo 2 argumenten moet laten lopen, een clipTo zou er 4 moeten laten lopen, en een setOpacity maar 1tje. Het laten lopen van dat verschillende aantal params is niet zo'n probleem, maar die resultaten kan ik niet in 1 array naar de functie in kwestie sturen, want die gaat immers uit van losse ints. De resultaten moeten dus verdeeld worden en als losse params verstuurd, via een dynamische functie, alleen dat wisselt dus per type van de run. Nou zou dat vrij eenvoudig met eval kunnen, maar dat is vies :)

Wat ik nu heb bedacht is dit;
op basis van het aantal params wat moet lopen bak ik een string. Deze schrijft met tussengevoegde komma's gewoon a[0], a[1], a[2], enz achter elkaar, en dat plak ik in een functie, die in de init van de run wordt aangemaakt:

JavaScript:
1
2
// str == 'a[0], a[1], ...'
this.action = new Function('scope', 'ref', 'a', 'scope[ref](' + str + ')');     


en vanuit de interval die de animatie stuurt:

JavaScript:
1
2
3
// res == [20, 30, ... ] (de lijst met lopende params op punt x)
// scope == het object die de functie 'ref' heeft (dynObject of window)
this.action(this.scope, this.ref, res);


Dat werkt dus op zich goed, alleen vraag ik me af of er geen andere manieren zijn om dynamisch functies te genereren die wisselende hoeveelheden params kunnen versturen, want ergens is dit toch nog wel een soort verkapte eval :P
Heeft iemand al eens iets dergelijks gedaan?

Instagram | Flickr | "Let my music become battle cries" - Frédéric Chopin


  • 2
  • Registratie: November 2000
  • Laatst online: 31-03 13:52

2

Je kan geen array als tweede parameter sturen? Dus als eerste parameter het type run en als tweede in een array alle parameters die hij nodig heeft om dat type uit te voeren?

  • Clay
  • Registratie: Oktober 1999
  • Laatst online: 22-06 13:51

Clay

cookie erbij?

Topicstarter
Het probleem is dat ik obv het aantal parameters dat moet runnen (moveTo:2, setOpacity:1, clipTo:4 enz) een functie moet bouwen die dat aantal parameters doorstuurt naar die functie per run interval. de moveTo verwacht een x en een y, geen array met een n aantal waardes.

ff werkend voorbeeld:
JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
function fietsen() {
    var list = [];
    var str = ''
    
    // list vullen en inhoud van action samenstellen
    for(var i=0; i<arguments.length; i++) {
        list[i] = arguments[i];
        str += 'a[' + i + ']' +
            ((i < arguments.length - 1)? ',':'');
    }
        
    // action definieren
    action = new Function('scope',
        'ref','a','scope[ref](' + str + ')');
        
    // action aanroepen
    action(window, 'enumerate', list);
}

function enumerate() {
    // arguments == parameters
    for(var i=0; i<arguments.length; i++) 
        alert(arguments[i]);
}

// testen
alert('nummer 1:');
fietsen(3,4,5);

alert('nummer 2:');
fietsen(6,7);


dit werkt dus. fietsen telt het aantal params, en stelt weer een functie samen die dat aantal params weer doorgeeft, maar ik weet niet of dit de handigste manier is :)

Instagram | Flickr | "Let my music become battle cries" - Frédéric Chopin


Verwijderd

Heb je hier wat aan (het is een truc), want ik snap nog steeds niet wat je precies wilt:
PHP:
1
2
3
4
5
6
7
8
9
10
function fietsen() 
{
  enumerate.apply(window,arguments);
}

function enumerate() {
    // arguments == parameters
    for(var i=0; i<arguments.length; i++) 
        alert(arguments[i]);
}

Die window als eerste parameter is arbitrair, ik neem ff voor het gemak aan dat je de calling-context niet gebruikt.

De apply-method werkt echter niet in alle browsers. Ik heb echter wel 's een keer een work-around gemaakt:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
if(!Function.apply) Function.prototype.apply=function(THIS,args)
{
  if(!THIS || typeof THIS!='object') 
  { 
    alert('First argument needs to be an object (this).'); 
    return; 
  }
  if(!args || !args.length)
  {
    alert('Second argument needs to be an array (arguments).'); 
    return; 
    
  }
  THIS.$apply=this;
  for(var i=0; i<args.length; i++)
  {
    this.arguments[this.arguments.length] = args[i];
  }
  return THIS.$apply();
}

Het is getest op x5.2 w6.0 w5.5 w5.01 w5.0 w4.0 m1.1 (x=Mac OS X, w=windows,m=mozilla).

Oh ja, je kunt het waarschijnlijk ook wel zonder die apply doen, en met de techniek die in de apply-work-around staat doen, maar ik snap even m'n eigen code niet (en ik heb geen zin om hard na te denken :O )

Suc6

  • Clay
  • Registratie: Oktober 1999
  • Laatst online: 22-06 13:51

Clay

cookie erbij?

Topicstarter
doh natuurlijk, apply. |:( :z bedankt, daar ga ik ff mee spelen. heb dat toch nodig voor verdere scripts, dus dan kan die workaround er ook bij.

Instagram | Flickr | "Let my music become battle cries" - Frédéric Chopin


Verwijderd

Jeez, Clay, ik dacht toch dat ik verstand had van JavaScript en niet helemaal dom ben ook...maar wat je hier probeert te doen gaat volledig aan me voorbij. Kun je voor mij nog een keer proberen uit te leggen wat het probleem is, en waarom apply de oplossing is? :D

  • Clay
  • Registratie: Oktober 1999
  • Laatst online: 22-06 13:51

Clay

cookie erbij?

Topicstarter
:) Ok, nog een poging. Ik ga het nu zo simpel mogelijk proberen uit te leggen :D

In Beehive I zat er een slideTo. daar gaf je de target coordinaten aan mee, en de tijd hoelang het moets duren. 3 parameters dus. er zat ook een fadeTo in, daaraan hoefde je alleen de opacity en de tijd me te geven. 2 parameters dus.
Maar nu wil ik een generieke oplossing bedenken en bouwen (ik heb al 1 oplossing, maar er zijn vast veel betere) zodat ik gewoon een moveTo en een resizeTo en een clipTo aan een soort "tread" kan voeren, zodat die functie over een beepaalde tijd die basis functies een aantal keren aanroept. die functie noem ik run. een moveTo in zo'n run ziet er dan zo uit:

JavaScript:
1
dynObject.run('moveTo', {from:[20,20], to:[100,100], time:1000});


dat zou dan in 1 seconde in een soort slideTo het htmlElement verplaatsen van (20,20) naar (100,100). Maar, als ik datzelfde met een clipTo doe zou het er zo uitzien:

JavaScript:
1
dynObject.run('clipTo', {from:[0,100,100,0], to:[10,70,70,10], time:1000});


Nou moet di e"run" terwijl die loopt de gegeven functie een aantal keren aanroepen, maar de moveTo moet die met 2 parameters aanroepen, en de clipTo moet hij met 4 aanroepen. Alleen, alles wat die krijgt is de naam van de functie, dat moet automagish omgezet worden ergens in die code naar een aanroep + parameters:

JavaScript:
1
2
// ergens in de run, ala
dynObject.moveTo(resultaat1, resultaat2)


maar bij de clip ala
JavaScript:
1
2
// ergens in de run, ala
dynObject.clipTo(resultaat1, resultaat2, resultaat3, resultaat4)


Het probleem is dat ik die resultaten niet in een array aan de gegeven functie van de run kan geven, omdat die functies losse ints verwachten. Ik moet dus dynamisch een functie aanroepen die de ene keer 2 parameters meestuurt, en de andere keer 4.

en het leuke aan apply daarbij, is dat dat toch een array verstuurt, maar dat hij dat op een bepaalde manier in de vorm van losse arguments aan laat komen bij de functie in kwestie, en dat zou mn hele probleem in 1 klap oplossen.

Is het nu duidelijker? :D anders geef ik het op, en post ik wel een link naar een end code ;)

Instagram | Flickr | "Let my music become battle cries" - Frédéric Chopin


  • Clay
  • Registratie: Oktober 1999
  • Laatst online: 22-06 13:51

Clay

cookie erbij?

Topicstarter
Doekman, ik vrees dat ik een probleem heb met je apply workaround.
Ik test em hier op IE5.0, en het werkt niet. probeer dit voorbeeld eens.

In de code definieer ik een Unit object, met life, en daaronder een Marine. De marine is een subclass van de Unit, en met de apply zou de Marine de this.life property moeten krijgen door de apply. Waar apply ondersteund wordt krijg ik een alert met "100", omdat ik van de marine de stats oproep, maar op IE5.0 krijgt de marine geen life property en heb ik een js error, terwijl de apply workaround dit zou moeten fixen.

Instagram | Flickr | "Let my music become battle cries" - Frédéric Chopin


Verwijderd

Heb je build 29nogwat of build 33nogwat. Ik moet dit so-wie-so op het werk doen, want ik heb hier IE6. Ik ga er morgen mee aan de gang.

  • Clay
  • Registratie: Oktober 1999
  • Laatst online: 22-06 13:51

Clay

cookie erbij?

Topicstarter
als ik de functie naar apply2 rename doet IE6 het ook niet :{

ik heb em nu zo:
JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
if(!Function.apply) 
Function.prototype.apply = function(obj, args) {
    var sArg = [];
    for(var i=0; i<args.length; i++) {
        sArg[i] = 'arg['+i+']';
    }

    var action = new Function('obj', 'f', 'arg',
        'obj.$apply = f; obj.$apply(' + sArg.join(',') + ')');
    
    action(obj, this, args);
}


dat werkt eindelijk :D en doet zowaar precies wat apply volgens het boekje doet. Ik moet nu ook moeite doen om nu mn eigen code nog te begrijpen :D maar begin nou eindelijk te vatten wat apply nou echt doet :)
Dit is zo'n beetje een samenraapsel van jou versie, een andere versie die ik ergens anders vandaan had, en wat eigen ingredienten, maar dat new Function gedoe vind ik eerlijk gezegd eigenlijk nog net zo vies als een eval. :{ en ik blijf argumenten tellen hier.

Instagram | Flickr | "Let my music become battle cries" - Frédéric Chopin


Verwijderd

Ach, een window.setTimeout met een string is ook net zo vies (en je moet wel, want Safari ondersteund geen functie-references). Een new Function in een uitzonderingssituatie moet kunnen, als het niet anders kan.

Maar over mijn apply. Ik snap er helemaal niets van. Ik snap de code nu: als je funktie.apply(obj,[]) doet, dan is THIS==obj en this==funktie. Dat snap ik wel. Maar ik snap niet dat ik dit ooit goed getest heb :? Op IE5.2.2/Mac doet 'ie het ook niet.

Mijn idee was indertijd, dat je voor de aanroep de argumenten als array kunt initialiseren, maar zoals het in mijn code staat werkt het niet. Van te voren is de de arguments property null en hij lijkt readonly te zijn. Dat moet ik nog 's beter testen.

Anyways, jou work-around werkt tenminste, bedankt :)

  • Clay
  • Registratie: Oktober 1999
  • Laatst online: 22-06 13:51

Clay

cookie erbij?

Topicstarter
Mijn idee was indertijd, dat je voor de aanroep de argumenten als array kunt initialiseren, maar zoals het in mijn code staat werkt het niet. Van te voren is de de arguments property null en hij lijkt readonly te zijn. Dat moet ik nog 's beter testen.
yup, en het leek mij ook te werken, maar de eerste test die ik deed teste nou juist niet dat wat ik eigenlijk had moeten testen :D Ik denk trouwens ook niet dat je de arguments property van te voren, in, of na de functie ueberhaupt aan kan passen.
Bij obj.$apply = this; zet je de functie (this) alleen maar in de scope van obj. Binnen de aanroep obj.$apply(); wordt die arguments property pas gemaakt, maar dat is op basis van argumenten die je meegeeft en op de verdere uitvoer heb je geen invloed :{ en op de volgende regel ben je de hele functie uitvoer al voorbij, dus dat is te laat. Dan blijft eigenlijk alleen argumenten tellen over.

Dat Safari geen functie refs ondersteunt wist ik niet trouwens :( triest. Is vrij essentieel eigenlijk...

Instagram | Flickr | "Let my music become battle cries" - Frédéric Chopin


  • oh,when?
  • Registratie: April 2000
  • Niet online

oh,when?

...

zo kom net thuus...en zag deze thread. Wat ik me afvraag of het hier om events gaat die synchroon met elkaar moeten lopen, of achter elkaar gaan lopen?

Ik kan me een Sequence Class herinneren geschreven in Actionscript die ongeveer hetzelfde emuleert zoals jij bescrijft...kzal eens kijken of ik het nog kan vindne.

:)

"You're only as good, as what you did last week."


  • oh,when?
  • Registratie: April 2000
  • Niet online

oh,when?

...

Zoals beloofd...hier de code, en link naar de volledige code ( met commentaar )


Flash ActionScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
///////////////////////////////// Command Class 1.09 ///////////////////////////////////
//                              Tatsuo Kato  Nov. 2002
////////////////////////////////////////////////////////////////////////////////////////
            
_global.Command = function() {
    if (arguments[0] == undefined) return;
    var a = arguments[0];
    if (arguments.length > 1) {
        this.obj = a;
        this.method = a[arguments[1]];
        this.args = arguments.slice(2);
    } else if (a instanceof Function) {
        this.method = a;
    } else if (typeof a == "string") {
        var idxOfParensL = a.indexOf("(");
        var pathToMethod = a.slice(0, idxOfParensL);
        this.obj = eval(pathToMethod.slice(0, pathToMethod.lastIndexOf(".")));
        this.method = eval(pathToMethod);
        this.args = a.slice(idxOfParensL + 1, a.lastIndexOf(")")).split(",");
    }
};

Command.prototype.execute = function() {
    var cmd = this;
    Command.callback = function(params) {
        Command.data = arguments;
        cmd.callbackCommand.execute(); 
        cmd.onComplete();
        /*
        'callbackCommand' is a Command object for internal use such as in Sequence 
        class, while 'onComplete' is an event handler for user.
        */
    };
};

////////////////////////////////////////////////////////////////////////////////////////

//Sequence class
_global.Sequence = function() {
    if (!(this instanceof Sequence)) {
        //For the case 'Sequence' is called as a function
        var seq = new Sequence();
        Sequence.apply(seq, arguments);
        return seq.execute();
    }
    if (arguments[0] == undefined) return;
    
    //Constructor part
    this.nChains = 0;
    this.lastIndex = null;
    this.acts = new Array();
    var len = arguments.length;
    for (var i = 0; i < len; ++i) {
        this.addCommand(arguments[i]);
    }
};

pt = Sequence.prototype;

pt.intervalTime = 80;
pt.setIntervalTime = function(intervalTime) {
    this.intervalTime = intervalTime;
};
    
pt.addCommand = function(act) {
    //act: an array like [5, myObj, "myMethod", arg1, arg2] 
    //where '5' is a tick count, which is used as an index number of the command 
    //in this sequence object.
    
    var index = act[0];
    if (index == "onSequenceComplete") {
        this.onSequenceComplete = act[1];
    } else {
        var cmd = new Command();
        Command.apply(cmd, act.slice(1));
        if (this.acts[index]) {
            var chainHead = this.acts[index];
            cmd.callbackCommand = chainHead.chainEnd.callbackCommand;
            chainHead.chainEnd.callbackCommand = new Command(cmd, "execute");
            chainHead.chainEnd = cmd;
        } else {
            cmd.callbackCommand = new Command(this, "onChainComplete");
            cmd.chainEnd = cmd;
            this.acts[index] = cmd;
            this.nChains++;
            this.lastIndex = Math.max(this.lastIndex, index);
        }
    }
};
pt.execute = function () {
    //For the case there is a command object that executes this sequence object,
    //store the callback function passed from the Command object.
    this.callback = Command.callback;
    
    this.nChainsRemain = this.nChains;
    var acts = this.acts;
    var i = 0;
    var lastIndex = this.lastIndex;
    var seq = this;
    
    this.id = setInterval(
        function () {
            if (acts[++i]) {
                acts[i].execute();
                if (i == lastIndex) clearInterval(seq.id);
            }
        }, 
        this.intervalTime
    );
    
    return acts[0].execute();
};
pt.onChainComplete = function() {
    if (--this.nChainsRemain == 0) {
        this.onSequenceComplete();
        this.callback();
    }
};
pt.stop = function() {
    clearInterval(this.id);
    delete this.id;
};
delete pt;
/////////////////////////////////////////////////////////////////////////////////////////////////

"You're only as good, as what you did last week."

Pagina: 1