JavaScript Object Oriented

C'è chi dice non sia utile, c'è chi dice non sia argomento semplice tanto quanto java o C# ... di fatto sviluppare JS in questo modo ne comporta i soliti vantaggi della programmazione ad oggetti, soprattutto oggi che JS sta prendendo sempre più piede, attraverso una semplicità nota ed intrinseca, tipica del JavaScript stesso. Questa pillola non è pensata per chi è a digiuno di JavaScript, bensì per chi ne conosce potenzialità e sintassi ma non l'ha ancora approfondito con gli oggetti.

Cosa si intende per classe
Una classe è una porzione di codice portabile e riutilizzabile in grado di effettuare una o più operazioni attraverso metodi (funzioni interne pubbliche o private) o di restituire informazioni attraverso i parametri (variabili interne pubbliche o private).

Una classe in JavaScript è una funzione ?
Si, è possibile affermarlo, ma non è altrettanto possibile farlo nei confronti dell'oggetto che ne istanzia una. Se una classe è di fatto un instanceof Function, un'istanza della classe non è un instanza di funzione ma esattamente un'istanza del nome di quella funzione, ovvero un'istanza di quella classe, o per comodità un oggetto "Classe".
function Test(){}; // funzione Test var test = new Test(); // oggetto Test // Test è istanziato come classe [grazie a new] alert(Test instanceof Function); alert(test instanceof Function); alert(test instanceof Test); // true // false // true

Una funzione può quindi essere usata sia come classe che come funzione ?
// funzione Test function Test(){ // assegnazione di un parametro interno pubblico this.parametro = "Ciao Mondo"; // operazione di funzione alert(this.parametro); }; // chiamata a funzione Test Test(); // Ciao Mondo // istanza di un nuovo oggetto Test var test = new Test(); // Ciao Mondo

La differenza fondamentale tra l'utilizzo della funzione Test come tale o come classe, è che nel secondo caso possiamo sfruttare in un secondo momento il parametro interno.
// richiamo il parametro della funzione alert(Test.parametro); // undefined // richiamo il parametro dell'oggetto di tipo Test alert(test.parametro); // Ciao Mondo

Questo accade perchè il riferimento all'interno della classe Test, ovvero il valore this, perde il referente di se stesso non appena la funzione cessa la sua esecuzione e nel caso di una chiamata diretta, Test.parametro, questo valore di fatto è come se non esistesse, visto che la funzione Test non è stata richiamata e visto che this.parametro è solo una parte di codice presente all'interno di questa funzione.

variabile o oggetto che sia. Scope ? .parametro). // funzione Test function Test(){ // parametro interno pubblico this..parametro).alert(new Test(). Lo scope è indispensabile per capire gli oggetti JavaScript ed il JavaScript stesso. // Ciao Mondo . Una volta capito alla perfezione come procedere per avere o non avere un determinato scope per ogni metodo. // Ciao Mondo alert(Test(). // istanza di un nuovo oggetto Test var test = new Test(). risulta essere raggiungibile o utilizzabile. perlomeno finchè l'oggetto istanziato ha una validità all'interno del proprio scope..parametro). e perchè non Rubamezzetto ? Lo scope è lo spazio in cui una variabile. per permettere al codice interno alla classe di risalire a se stesso in qualunque momento. la strada sarà tutta in discesa. funzione. // Ciao Mondo Parallelo alert(test. }. una funzione o in generale un oggetto. // parametro interno privato var parametro = "Ciao Mondo Parallelo". // operazione di funzione alert(parametro). // Errore: Test has no properties Per questo è necessario istanziare un oggetto attraverso l'uso di Test come classe e non come funzione (sempre grazie a new).parametro = "Ciao Mondo".

// parametro pubblico this. alert(test. }. }.. poichè il suo scope persiste all'interno di quella porzione di codice.leggiParametro(). test.. Ciao Mondo Parallelo Ciao Mondo Ciao Mondo Parallelo . // metodo pubblico this.parametro).La variabile parametro all'interno di Test non cessa comunque di esistere dopo l'assegnazione ad oggetto..leggiParametro = function() { alert(parametro).parametro = "Ciao Mondo".leggiParametro().. non accade niente . // funzione Test function Test(){ // parametro privato var parametro = "Ciao Mondo Parallelo". var test = new Test(). test. // // // // .

}.leggiParametro().Ma la parola var prima del nome della variabile è indispensabile ? La parola var è una delle migliore amiche di JavaScript ed è quella che definisce esattamente lo scope di una variabile. function Test(){ // parametro privato senza var parametro = "Ciao Mondo Parallelo". // Ciao Mondo Parallelo Dichiarando invece la variabile di funzione con var non si rischia di sovrascrivere una globale ne di ritrovarsi sovrascritta la variabile di funzione. }. }.leggiParametro = function() { alert(parametro). alert(parametro). // Ciao Mondo Parallelo alert(parametro). this. }. this. var test = new Test(). // Variabile Globale var test = new Test(). test. test. // una variabile globale non ha bisogno della parola var // ma non è nemmeno sbagliato usare la parola // var per dichiarare una globale var parametro = "Variabile Globale".leggiParametro = function() { alert(parametro). // Variabile Globale . function Test(){ // parametro privato senza var parametro = "Ciao Mondo Parallelo".leggiParametro().leggiParametro(). test. // Ciao Mondo Parallelo var parametro = "Variabile Globale".

test. var parametro = "Variabile Globale". this. }. // Ciao Mondo Parallelo Ma se ho una variabile privata non posso risalire a quella globale ? Una variabile globale è come se fosse un parametro della super classe window. // Variabile Globale test.}.}.leggiParametro().leggiParametro = function() {alert(parametro). function Test(){ var parametro = "Ciao Mondo Parallelo". }.leggiParametroGlobale = function(){ alert(window.leggiParametro = function() {alert(parametro). test.leggiParametro(). var test = new Test(). // Variabile Globale test.parametro). // Ciao Mondo Parallelo . this.leggiParametroGlobale(). è quindi sempre possibile raggiungere il suo valore anche usando omonimi all'interno di una classe. this.Con var possiamo quindi stare sempre tranquilli function Test(){ var parametro = "Ciao Mondo Parallelo". }. // Ciao Mondo Parallelo alert(parametro). var test = new Test().leggiParametro(). var parametro = "Variabile Globale".

// operazione sempre lecita per un oggetto // il metodo pubblico dell'oggetto test è stato distruto test.metodoPubblico is not a function Qualora un metodo od una variabile dovessero essere utili solo per poter utilizzare altri metodi pubblici è consigliabile sfruttare funzioni o variabili interne. }. Uno dei limiti più consistenti della programmazione ad oggetti in JavaScript è infatti proprio quello di non poter garantire consistenza agli oggetti. questa operazione test.metodoPubblico = function() { alert("Ciao Mondo"). quindi private.Perchè usare variabili private e non globali all'interno di una classe ? Non c'è un motivo assoluto per scegliere di usare una variabile privata piuttosto che una globale se non quello di essere certi che il valore della variabile o del valore resttuito dalla funzione privata sarà esattamente quello che ci aspettiamo all'interno dell'oggetto e che nessuno potrà quindi modificarlo. // Error: test. function Test(){ this. "per sbaglio". var test = new Test().metodoPubblico(). test. // Ciao Mondo // un altra porzione di codice fa. al fine di limitare la possibilità ad altri di modificare porzioni indispensabili di codice. }. . Per consistenza si intende la possibilità di impedire ad altri di modificare metodi o parametri pubblici dei nostri oggetti.metodoPubblico().metodoPubblico = "Ciao Mondo".

come la super mega libreria "Cinema Effects with only 200Kb". senza usare i giusti accorgimenti potreste voi stessi distruggere il codice di altre librerie presenti che avete incluso nella pagina .Ma perchè continui a dire "altri"? Sono io che scrivo le classi ed il codice . potrebbero in qualche modo cambiare il vostro codice e viceversa. limitare l'utilizzo di nomi al fine di avere pubblici solo quelli fondamentali per l'oggetto può essere di aiuto JavaScript si include nelle pagine con una facilità impressionante. Google.. !!! Le considerazioni da fare sono almeno un paio in applicativi dove il codice è veramente tanto e le classi create da centinaia di linee può capitare di "dimenticarsi" di un nome di metodo o parametro già usati per altri scopi..

Allo stesso tempo all'interno di un metodo pubblico il this sarà riferito all'oggetto stesso. } }.mostraPippo(). }.pippo = "Yuk!". // false Questo permette al metodo pubblico di risalire ad altri metodi pubblici o variabili pubbliche attraverso l'uso del prefisso this.confronta(test). var oggetto = new Test(). oggetto. // Yuk! test. test. // true test.Ok per le variabili.confronta = function(variabile) { alert(this === variabile). qualcosa sulle funzioni / metodi privati ? Assegnare un parametro o un metodo pubblico è molto semplice.pippo). basta usare this davanti al nome del metodo o parametro.mostraPippo(). }. // undefined .mostraPippo = function() { alert(this.confronta(Test). oggetto. function Test(){ this. function Test(){ this. riferendosi sempre all'oggetto stesso. var test = new Test(). var test = new Test().

avviso().mostraAvviso = function(messaggio) { // funzione locale function avviso(){alert(messaggio)}. all'interno del metodo. }. poichè this. come Test.avviso = function() { avviso(). sulle funzioni innestate e sui metodi privati. inviata al metodo dell'oggetto. poichè lo scope della variabile messaggio. le funzioni a scope locale.avviso(). Altra nota di rilievo è che in questo caso non è possibile.avviso. come è importante notare che la funzione innestata al metodo mostraAvviso non ha in questo caso nemmeno bisogno di una variabile come argomento.Quanto detto serve per introdurre i metodi privati. Queste funzioni possono essere innestate nella classe come nel metodo pubblico e sebbene non abbiano la parola var davanti hanno le stesse identiche regole di scope delle variabili. . E' importante notare che la funzione locale al metodo mostraAvviso nonsovrascrive quella privata definita nello scope della classe (la prima). test. ha validità per tutto il metodo stesso ed ogni porzione di codice creata al suo interno potrà sfruttare questa variabile. risalire alla stessa.mostraAvviso("Ciao Mondo"). test. una volta oscurato lo scope della prima funzione "avviso()". }. var test = new Test(). this. function Test(){ // "metodo" privato / funzione privata function avviso(){alert("Avviso Generico")}. }. this. o per meglio definirli. // Ciao Mondo // Avviso Generico In questo "semplice" esempio c'è molto di quanto detto sullo scope.

encodeURI.Avviso..pippo() infatti richiamerà esattamente quella funzione (o quel suo metodo) come descritto nella parte relativa allo scope di questa pillola. .parametro o new Classe) credo sia doveroso specificare che JavaScript è comunque un linguaggio Object Oriented. Essendo tutto sotto window ad eccezzione delle top level functions (eval. sempre all'interno o fuori dall'oggetto. non saranno utilizzabili. ne è l'esempio più semplice..toString()) . sebbene sia un modo apparentemente procedurale di utilizzare JS il core del browser interpreta pippo come metodo della super classe window. proprio grazie al fatto di essere private. le "primitive" sono già classi e tutti i derivati di window sono già oggetti.. si sta comunque sviluppando con un linguaggio Object Oriented.. alert . window. function pippo(){}. e poco altro) a prescindere che si utilizzi JavaScript con le classi che non lo si utilizzi in questo modo.methodo() o istanza. Dopo aver preso spranghe sui denti nella ML di Python per il mio concetto sintattico / visuale della programmazione ad oggetti (istanza. escape. alert((1). encodeURIComponent. Approfondimento breve ma IMPORTANTE Ho scritto questa pillola come se JavaScript fosse Object Oriented solo con le classi.

Digging a real OO class system with jQuery At Digg we use jQuery extensively. modeled after the original Class object in Prototype but with significantly greater capabilities -including namespacing.often trying to shoehorn traditional OOP patterns in where they don't quite fit -. Class adds OOP patterns to jQuery.so we rolled our own. How does it work? . Existing OOJS libraries weren't a good fit -. limited classical inheritance and more robust prototypal inheritance. but it doesn't offer a much in the way of Object-Oriented JavaScript.

// And now.is the same as this $. foo.extend(MyClass.create is boolean. Anytime the last argument passed to Class.create({ init: function(){ console. }.log('You instantiated a Class!'). MyClass.namespace('foo').create(true). a static class. myProp: 'foo' }. MyClass. var MyClass = Class. //this. //. method). it’ll serve as the static switch (defaults to false). }). var foo = new MyClass(). // Namespaces var MyClass = Class.someFunc().create(true). // Add constructor and methods var method = { someFunc: function(){} } var MyClass = Class..namespace('bar'). var mc = new MyClass().create(true).. . myFunc: function(){}.create().. { bar: Class..// Hello World var MyClass = Class.

pluginDoSomethingCool().pluginRemove('other stuff'). $('#test').js jQuery. you may feel that jQuery's plugins leave a lot to be desired.myplugin = function() { // Do some cool stuff here } While that seems all fine and dandy for simple plugins. .pluginAdd('stuff'). Some plugins get around this by adding tons of methods to jQuery's plugin namespace. If you come from an ObjectOriented background like me. $('#test').plugin(). often in a non-linear fashion. $('#test'). The basic formula to create a jQuery plugin is to extend the plugin namespace with a single method: #myplugin. $('#test').fn.Building an Object-Oriented jQuery Plugin So you've been using jQuery as your Javascript framework and now you need to write a plugin. you may need to create more robust plugins that do many things.

or blindly accept any string as a method name. especially if the plugin accepts an options object the first time it is created.I personally don't like that approach because it pollutes the jQuery plugin namespace with lots of methods.js (function($){ // Your plugin code goes here })(jQuery). To get around these hurdles.plugin(). #myplugin. $('#test'). This approachs means you would have to either write a switch of all the methods you want to expose. I personally like to stick to just one plugin method per plugin. This will help keep things nice and tidy without creating global variables.plugin('remove'. I've created a basic template for jQuery plugins that provides access to an Object-Oriented interface if needed while still maintaining jQuery's simplicity of a single method in the plugin namespace. 'stuff'). $('#test'). .plugin('add'. I think this approach is a little awkward.plugin('doSomethingCool'). 'other stuff'). The first thing you need to do is wrap all your plugin code in an anonymous function. $('#test'). Other plugins use the first parameter of the plugin to call methods: $('#test').

})(jQuery). }.js (function($){ var MyPlugin = function(element) { var elem = $(element). .Next.log('publicMethod() called!'). }. // Public method this.publicMethod = function() { console. #myplugin. create your plugin as a class. var obj = this. where the first parameter is a single DOM element.

myplugin = function() { return this.log('publicMethod() called!'). }. }).each(function() { var myplugin = new MyPlugin(this). })(jQuery).fn.js (function($){ var MyPlugin = function(element) { var elem = $(element). }.publicMethod = function() { console. $.To make your new object-oriented class available as a jQuery plugin. . }. write a simple wrapper function in the plugin namespace: #myplugin. // Public method this. var obj = this.

// Public method this. when you call $(element).Now.js (function($){ var MyPlugin = function(element) { var elem = $(element). This provides easy access to the object while allowing you to prevent accidental double instantiation in the event that the plugin was called again on the same element. passing the element as the first argument. I usually store the object in the elements data. .log('publicMethod() called!'). }.publicMethod = function() { console. var obj = this. For this. the jQuery plugin instantiates an instance of MyPlugin. }.myplugin(). #myplugin. But now there's a problem of how to get the object "myplugin" once it's been created.

data('myplugin')) return. }.data('myplugin'). Now you have easy access to the object should you need to run methods on it.data('myplugin'. myplugin). })(jQuery). // prints "publicMethod() called!" to console .$.fn. }). var myplugin = $('#test'). // Store plugin object in this element's data element. $('#test'). myplugin. var myplugin = new MyPlugin(this).each(function() { var element = $(this).publicMethod().myplugin().myplugin = function() { return this. // Return early if this element already has a plugin instance if (element.

options || {}). just pass them from the jQuery plugin to your plugin's constructor: #myplugin. options) { var elem = $(element). }.If you need to get fancy and add options parameter or other required parameters. // Merge options with defaults var settings = $.js (function($){ var MyPlugin = function(element.log('publicMethod() called!'). var obj = this. }. // Public method this.extend({ param: 'defaultValue' }.publicMethod = function() { console. .

data('myplugin'. })(jQuery). // Return early if this element already has a plugin instance if (element.fn.myplugin = function(options) { return this.data('myplugin')) return. options).each(function() { var element = $(this). // Store plugin object in this element's data element. }). // pass options to plugin constructor var myplugin = new MyPlugin(this. . }.$. myplugin).

To make a private method. options) { var elem = $(element).extend({ param: 'defaultValue' }.can be called from client code this.log('public method called!').You may also want to expose some of your object's methods while keeping others private. }. . var settings = $.js (function($){ var MyPlugin = function(element. }. // Public method .can only be called from within this object var privateMethod = function() { console. create a local function within your object using the var keyword: #myplugin.publicMethod = function() { console. // Private method . options || {}). var obj = this. }.log('private method called!').

$. // Return early if this element already has a plugin instance if (element.each(function() { var element = $(this). }). To see an example of a plugin I wrote that uses this template.myplugin = function(options) { return this.data('myplugin')) return. options). check out my Tagger plugin. }. })(jQuery).fn. myplugin). // Store plugin object in this element's data element.data('myplugin'. . // pass options to plugin constructor var myplugin = new MyPlugin(this.