Toon posts:

[JS] Uitleg gevraagd over programmeertechnieken

Pagina: 1
Acties:
  • 128 views sinds 30-01-2008
  • Reageer

Verwijderd

Topicstarter
Ik heb een aantal open-source editors vergeleken, en het valt me op hoe verschillend deze in code opgebouwd zijn. Twee voorbeelden:
JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// CodePress
CodePress.run = function() {
  new CodePress(t[i]);
}

CodePress = function(obj) {
  self.initialize = function() {}
  self.toggleLineNumbers = function() {}
  self.getCode = function() {}
}

// WidgEditor
function widgInit() {
  new widgEditor(textarea.id)
}

function widgEditor(id) {
  this.theToolbar = new widgToolbar(this);
}

function widgToolbar(theEditor) {}
widgToolbar.prototype.addButton = function() {}
widgToolbar.prototype.setState = function() {}

Als ik het goed begrijp, maken beide manieren een instantie van een editor-object aan. De manier waarop de functies van het object worden toegevoegd is alleen anders. CodePress doet het binnen eenzelfde class (zeg ik dat goed?), WidgEditor loopt het object te prototypen met eigen functies.

Mijn vraag is: waarom werkt de eerste methode zonder prototype en de tweede niet? Ik begreep dat je prototype gebruikte om eigen methoden te definieren die de werking van javascript uitbreiden. Dat je dus meer bewerkingen kunt definieren voor bv. strings en arrays. Ik wist niet dat je prototype nodig had om een eigen object uit te breiden:
JavaScript:
1
2
3
4
5
6
widgToolbar.addButton = function() {} // werkt niet
widgToolbar.prototype.addButton = function() {} // werkt wel

// bij CodePress:
self.toggleLineNumbers = function() {} // werkt wel
self.prototype.toggleLineNumbers = function() {} // werkt niet

Ik ben erg in de war hierover. Wie kan mij verlichten?

  • crisp
  • Registratie: Februari 2000
  • Laatst online: 00:29

crisp

Devver

Pixelated

Verwijderd schreef op zondag 30 december 2007 @ 19:24:

Als ik het goed begrijp, maken beide manieren een instantie van een editor-object aan.
Dat klopt :)
De manier waarop de functies van het object worden toegevoegd is alleen anders. CodePress doet het binnen eenzelfde class (zeg ik dat goed?),
CodePress maakt inderdaad een instance aan maar definieert binnen die instance (voor elke instance dus) alle methods.
WidgEditor loopt het object te prototypen met eigen functies.
Jep
Mijn vraag is: waarom werkt de eerste methode zonder prototype en de tweede niet?
Vraag het de developers :P Persoonlijk snap ik niet waarom CodePress geen prototyping gebruikt, da's toch een stuk minder memory-intensief dan voor elke instance alle methods apart te definieren. Je wint er een heel klein beetje performance mee omdat de prototype-chain niet afgelopen hoeft te worden, maar die afweging is een dusdanige micro-optimalisatie dat dat geen reden mag zijn imo.
Ik begreep dat je prototype gebruikte om eigen methoden te definieren die de werking van javascript uitbreiden. Dat je dus meer bewerkingen kunt definieren voor bv. strings en arrays. Ik wist niet dat je prototype nodig had om een eigen object uit te breiden:
Eigen objecten zijn niet heel anders dan de native objecten in javascript hoor ;)

Intentionally left blank


Verwijderd

crisp schreef op zondag 30 december 2007 @ 22:39:
Vraag het de developers :P Persoonlijk snap ik niet waarom CodePress geen prototyping gebruikt, da's toch een stuk minder memory-intensief dan voor elke instance alle methods apart te definieren.
Ik doe het zelf eigenlijk alleen voor objecten waar zeer veel instanties van moeten komen. Door de methodes/properties buiten de constructor functie te definieren wordt de code er niet duidelijker op vind ik. Overigens heb ik zelf altijd mijn twijfels gehad over dat memory verhaal dus heb ik even een snel testje gedaan met de volgende test-objecten:
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
32
33
34
35
36
37
38
// prototyped
function PrototypedObject(a, b, c) {
    this.a = a;
    this.b = b;
    this.c = c;
};

PrototypedObject.prototype.getA = function( ) {
    return this.a;
};

PrototypedObject.prototype.getB = function( ) {
    return this.b;
};

PrototypedObject.prototype.getC = function( ) {
    return this.b;
};


// non-prototyped
function NonPrototypedObject(a, b, c) {
    this.a = a;
    this.b = b;
    this.c = c;
    
    this.getA = function( ) {
        return this.a;
    };
    
    this.getB = function( ) {
        return this.b;
    };
    
    this.getC = function( ) {
        return this.b;
    };
};


Als ik daar in een for-loopje een aantal keren 10000 instanties van maak krijg ik de volgende resultaten (geheugengebruik in Mb):

browser0 (start)1 x 100002 x 100003 x 10000
Firefox 2 Prototyped23.826.028.431.1
Firefox 2 Non-prototyped 23.630.136.342.8
IE 7 Prototyped 21.427.934.340.6
IE 7 Non-prototyped 21.544.767.790.8
Safari 3 Prototyped 15.220.324.530.9
Safari 3 Non-prototyped 13.021.332.242.4


Overigens kostte het bij Firefox en IE meer dan 2x zo lang om 10000 non-prototyped instanties aan te maken (+200ms vs 100ms).

Let wel, we praten hier over tienduizenden instanties (zij het van een klein object). In hoeverre dit in de meeste real-life scenario's merkbaar is vind ik moeilijk te zeggen. Volgens mij valt het wel mee allemaal. :)

  • crisp
  • Registratie: Februari 2000
  • Laatst online: 00:29

crisp

Devver

Pixelated

Verwijderd schreef op zondag 30 december 2007 @ 23:31:
[...]

Ik doe het zelf eigenlijk alleen voor objecten waar zeer veel instanties van moeten komen. Door de methodes/properties buiten de constructor functie te definieren wordt de code er niet duidelijker op vind ik. Overigens heb ik zelf altijd mijn twijfels gehad over dat memory verhaal dus heb ik even een snel testje gedaan met de volgende test-objecten:

[...]

Overigens kostte het bij Firefox en IE meer dan 2x zo lang om 10000 non-prototyped instanties aan te maken (+200ms vs 100ms).

Let wel, we praten hier over tienduizenden instanties (zij het van een klein object). In hoeverre dit in de meeste real-life scenario's merkbaar is vind ik moeilijk te zeggen. Volgens mij valt het wel mee allemaal. :)
Tsja, wel tekenent maar IRL praat je toch over methods met iets meer functie-body dus ook meer performance- en memory-impact.

Het 'duidelijkheids'-argument vind ik persoonlijk weer zwak, maar je kiest nu ook wel voor de meest verbose manier om methods te prototypen ;)
Zelf doe ik het zo:

JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function PrototypedObject(a, b, c)
{
    this.a = a;
    this.b = b;
    this.c = c;
};
Object.extend(PrototypedObject.prototype,
{
    getA: function( )
    {
        return this.a;
    },
    getB: function( )
    {
        return this.b;
    },
    getC: function( )
    {
        return this.c;
    }
});

Waarbij ik Object.extend() 'geleend' heb van prototype.js (en daar een optioneel 'overWrite' argument aan toegevoegd heb).

Doe overigens nooit dit:
JavaScript:
1
PrototypedObject.prototype = { /* methods */ };

daarmee overschrijf je ook de constructor property wat in bepaalde (heel specifieke) gevallen voor rare bugs kan zorgen ;)

Intentionally left blank


  • SchizoDuckie
  • Registratie: April 2001
  • Laatst online: 18-02-2025

SchizoDuckie

Kwaak

Wat heerlijk dan toch, die gestructureerdheid van mootools O+ Je levert er misschien wat snelheid voor in maar je hoeft je niet druk te maken over prototypes, references, etc.

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
Schaap = new Class({

initialize: function(options) { 
  this.setOptions(options);
  this.poten = 4;
  this.schattigheid = 1;
},

mekker: function() {
 alert('bêêh');
} 

});

Lammetje = Schaap.extend({

initialize: function() { 
   this.parent(); // parent constructor
   this.schattigheid = 10;
},

mekker: function() { // gewoon overschrijven die hap
 alert('mêêh');
}

});

Schaap.implement(new Options);

Stop uploading passwords to Github!


Verwijderd

Topicstarter
@crisp, @Blues - bedankt voor jullie reakties, ik lees er veel van :) Ik zag zojuist dat Drupal het weer zo doet:
JavaScript:
1
2
3
4
5
6
7
var Drupal = Drupal || {};

Drupal.redirectFormButton = function() {}
Drupal.absolutePosition = function() {}

Drupal.jsUpload = function() {}
Drupal.jsUpload.prototype.onsubmit = function () {}

Is dit nu hetzelfde als hieronder?
JavaScript:
1
2
3
4
5
Drupal = function {
  self.redirectFormButton = function() {}
  self.absolutePosition = function() {}
  self.jsUpload = function() {}
}

En waarom dan wel jsUpload prototypen voor een onsubmit method? Is het niet mogelijk dit zonder prototype binnen het Drupal object te definieren?
JavaScript:
1
2
3
4
5
6
Drupal = function {
  // ...
  self.jsUpload = function() {
    // hier zonder prototype onsubmit definieren
  }
}

Ik ben het met Blues eens als hij zegt
Door de methodes/properties buiten de constructor functie te definieren wordt de code er niet duidelijker op vind ik.
Maar de manier van crisp vind ik dan weer een mooie tussenoplossing.

  • crisp
  • Registratie: Februari 2000
  • Laatst online: 00:29

crisp

Devver

Pixelated

SchizoDuckie schreef op maandag 31 december 2007 @ 00:09:
Wat heerlijk dan toch, die gestructureerdheid van mootools O+ Je levert er misschien wat snelheid voor in maar je hoeft je niet druk te maken over prototypes, references, etc.
Class-based OO simuleren in javascript is eigenlijk een vorm van blasfemie, maar het illustreert wel mooi de kracht van de taal zelf ;)

Intentionally left blank


  • SchizoDuckie
  • Registratie: April 2001
  • Laatst online: 18-02-2025

SchizoDuckie

Kwaak

crisp schreef op maandag 31 december 2007 @ 01:38:
[...]

Class-based OO simuleren in javascript is eigenlijk een vorm van blasfemie, maar het illustreert wel mooi de kracht van de taal zelf ;)
noem het blasfemie, noem het homogeniteit creëren tussen programmeerscript talen ik noem het geniaal ;) Trouwens, crisp, kijk eens naar Mootools 1.2 daar zitten tricks in waar zelfs jij nog wat van zou kunnen leren :o

Stop uploading passwords to Github!


Verwijderd

crisp schreef op zondag 30 december 2007 @ 23:56:
Het 'duidelijkheids'-argument vind ik persoonlijk weer zwak, maar je kiest nu ook wel voor de meest verbose manier om methods te prototypen ;)
Is ook een zwak argument :) maar ik vind code lezen een stuk moeilijker dan code schrijven.
crisp schreef op maandag 31 december 2007 @ 01:38:
[...]

Class-based OO simuleren in javascript is eigenlijk een vorm van blasfemie, maar het illustreert wel mooi de kracht van de taal zelf ;)
Eens. Zeker als prototyped-based OO prima voldoet in dit geval:
JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function Schaap() {
    this.poten = 4;
    this.schattigheid = 1;
    this.mekker = function() {
        alert('bêêh');
    }
}

function Lammetje() {
    this.schattigheid = 10;
    this.mekker = function() {
        alert('mêêh');
    }
}
Lammetje.prototype = new Schaap();

Verwijderd

Topicstarter
In het verlengde van deze discussie vraag ik me af hoe je het beste een javascript library voor je eigen website kunt opzetten, bijvoorbeeld:
JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var Zecco = {};

Zecco.info = {
  version : '0.1 beta',
  type    : 'uncompressed'
}

Zecco.textEditor = function() {
  init : function() { alert('hoi'); }
}

Zecco.colorPicker = function() {
  init : function() { alert('hai'); }
}

Zecco.getCookie = function() {}
Zecco.setCookie = function() {}

Element.prototype.hasClass = function() {}
String.prototype.ucfirst = function() {}

Hier declareer ik op regel 1 het object. Op regel 3 t/m 6 wat info en in regel 8 en 12 functies van het object waarvan meerdere instanties zouden kunnen voorkomen. Op regel 16 en 17 twee generieke functies, die als method van Zecco zijn gedefinieerd om verwarring met getCookie functies van een andere library te voorkomen. Tenslotte een aantal prototype functies.

Een aantal vragen:
  • wat vinden jullie van zo'n opbouw? Hoe bepaal je hoe je een functie declareert?
    JavaScript:
    1
    2
    
    function getCookie() {} //of 
    Zecco.getCookie = function() {}
  • waarom is "Zecco.textEditor.init not a function"? Waarschijnlijk omdat ik moet prototypen, maar ik snap niet waarom dat dan bij Zecco.textEditor niet hoeft...

[ Voor 4% gewijzigd door Verwijderd op 31-12-2007 12:39 ]


Verwijderd

Verwijderd schreef op maandag 31 december 2007 @ 12:36:
JavaScript:
1
2
3
Zecco.textEditor = function() {
  init : function() { alert('hoi'); }
}
Je haalt hier wat dingen door elkaar ben ik bang.
JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
// zo maak je direct een nieuw object+methode aan:
Zecco.textEditor = {
  init : function() { alert('hoi'); }
}

// zo maak je een constructor aan waar je meerdere instanties mee kan maken:
Zecco.textEditor = function() {
  this.init = function() { alert('hoi'); }
}

// en zo maak je dan een instantie:
var te = new Zecco.textEditor();

Een object kun je op allerlei verschillende maken en de verschillende bekende libraries doen er nog een schepje bovenop met hun variaties op Class.create(). Ik vermoed dat je daar ook de init() methode vandaan hebt die dan automatisch voor nieuwe instanties wordt aangeroepen. Dit is bij de standaard methodes van objecten creeeren zoals hierboven niet het geval dus let daar goed op!

  • crisp
  • Registratie: Februari 2000
  • Laatst online: 00:29

crisp

Devver

Pixelated

SchizoDuckie schreef op maandag 31 december 2007 @ 02:40:
[...]

noem het blasfemie, noem het homogeniteit creëren tussen programmeerscript talen ik noem het geniaal ;)
Ik noem het luiheid van serverside programmeurs die geen andere taal willen leren :+
javascript is javascript, daar moet je niet iets anders van willen maken (en anders gewoon wachten op ES4 ;))
Trouwens, crisp, kijk eens naar Mootools 1.2 daar zitten tricks in waar zelfs jij nog wat van zou kunnen leren :o
zoals? of verwacht je nu dat ik die hele sourcecode ga doorspitten?

Intentionally left blank


  • crisp
  • Registratie: Februari 2000
  • Laatst online: 00:29

crisp

Devver

Pixelated

Verwijderd schreef op maandag 31 december 2007 @ 12:36:
[...]
Een aantal vragen:
  • wat vinden jullie van zo'n opbouw? Hoe bepaal je hoe je een functie declareert?
    JavaScript:
    1
    2
    
    function getCookie() {} //of 
    Zecco.getCookie = function() {}
In het eerste geval maak je een globale functie aan, in het tweede geval maak je de functie aan als property van je Zecco 'namespace'. Dat laatste wordt gedaan om de global namespace zo min mogelijk te vervuilen en daarmee interoperability problemen tussen verschillende scripts/libraries te voorkomen (hoewel ik iedereen zou afraden verschillende libraries simultaan te gebruiken en zeker niet allerhande 3rd party scripts te gaan includen als je niet weet wat ze precies allemaal doen).

Intentionally left blank


Verwijderd

Topicstarter
Dus als we het wel of niet prototypen even buiten beschouwing laten, zou dit dan een redelijke opzet zijn? Wat zouden de nadelen zijn? De voordelen zijn volgens mij dat de global namespace niet wordt "vervuild" en dat je een sterke - en dus snel doorzoekbare - structuur hebt :)
JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var Zecco = {};

Zecco.info = {
  version : '0.1 beta',
  type    : 'uncompressed'
}

Zecco.Tools = {
  getCookie : function() {},
  setCookie : function() {}
}

Zecco.textEditor = function() {
  this.init = function() {}
}

Element.prototype.hasClass = function() {}
String.prototype.ucfirst = function() {}

[ Voor 15% gewijzigd door Verwijderd op 31-12-2007 15:18 ]

Pagina: 1