javascript-logo-bw

Incapsulazione con Javascript

Definizione diretta di classi

http://www.dafishinsea.com/blog/2012/02/23/encapsulation-in-javascript/

Introduzione

Per prima cosa bisogna tenere conto che javascript è “function scoped”: una variabile è visibile ovunque all’ interno della funzione in cui è definita, anche all’ interno delle funzioni che sono definite nello stesso scope. E’ la proprietà delle closures.

Altra cosa importante da ricordare è che javascript non ha le “classi”, ma funzioni “costruttore”:
quando si istanzia un oggetto con new viene chiamata la funzione costruttore dell’ oggetto, con this impostato sul prototype. Questo non è altro che un oggetto Object vuoto, ma è possibile dichiarare delle proprietà:

Dog.prototype = {
        bark: function () { }
}
dog = new Dog();
dog.bark() // viene chiamata la funzione

Incapsulazione delle variabili

Definendo una proprietà all’ interno di una funzione si ha una variabile privata; per renderla disponibile all’ esterno si definisce un metodo privilegiato:

function Dog (name) {
// la variabile name è definita come in "var name"
    this.getName = function () { return name; }
}

var fido = new Dog("Fido");
console.log(fido.getName());  //"Fido"
console.log(fido.name);       //undefined

Per definire una variabile privata che non sia un argomento del costruttore, basta dichiararla nel costruttore:

function Dog (name) {

    var age = 0;

    this.getName = function () { return name; }
    this.getAge = function () { return age; }

    //example of setter:
    this.setAge = function (newAge) { age = newAge; }
}

Si può operare in questo modo per tutte le variabili e funzioni private che si vuole, ma sorge un problema di efficienza: ogni volta che il costruttore viene chiamato (quando si istanzia un oggetto) le funzioni vengono ricreate sprecando memoria.

Prevenire lo spreco di memoria

Una soluzione al problema, non definitiva, dato che qualche funzione deve essere pur sempre ricreata, è quella di definire le variabili private con i loro getters/setters (solitamente leggeri) nel costruttore, e poi aggiungere nel prototype i metodi pubblici che fanno uso dei getters/setters: in questo modo si mantiene la protezione dei dati incapsulati e si beneficia dell’ ereditarietà prototipale.

In questo caso, bisogna ricordare di richiamare esplicitamente il costruttore del prototipo:

function Daschund(name) {
    //...
    Dog.call(this, name);
}

Daschund.prototype = new Dog();

Metodi privati

Vengono definiti nel costruttore:

function Dog (name) {

    var age = 0;

    //private function
    function scratch() { console.log("scratch"); }

    this.getName = function () { return name; }
    this.getAge = function () { return age; }

    //example of setter:
    this.setAge = function (newAge) { age = newAge; }
}

La principale limitazione delle funzioni così dichiarate è che sono disponibili nella funzione contenitrice (il costruttore), ma non sono visibili dalle funzioni prototipali.
Per accedere a simli funzioni private si potrebbero dichiarare i metodi pubblici nel costruttore, ma perderemmo i benefici di prototype.

Un sistema per superare questa limitazione è quello del module pattern: definire il costruttore e prototype all’ interno di una funzione closure che contenga anche le funzioni private.
Ma bisogna fare attenzione: i metodi nel modulo sono statici.

Incapsulazione con classe proxy

http://effprog.blogspot.it/2011/10/compact-and-efficient-javascript.html

function class(options){
    var     constructor = options.constructor,
        public = options.public,
        private = options.private,
        key;
        

    function hiddenClass(){}

    var hp = hiddenClass.prototype = {};

    function publicClass(){
        var obj = new hiddenClass();
        constructor && constructor.apply(obj, arguments);
        Object.defineProperty(this, 'obj', {value : obj});
    }

    var pp = publicClass.prototype = {};

    for(key in private){
        if(private.hasOwnProperty(key)){
            hp[key] = private[key];
        }
    }    

    for(key in public){
        if(public.hasOwnProperty(key)){
            var val = public[key];
            hp[key] = pp[key] = val;

            if(typeof val === 'function'){
                pp[key] = function(){
                    return hiddenClass.prototype[key].apply(this.obj, arguments);
                }    
            }
        }        
    }

    return publicClass;
}
/*
 * Esempio
 */
var c = class({
    
    constructor : function(a, b){
        this.a = a;
        this.b = b;
    },

    private: {
        helpMe : function(){
            console.log(this.a);
        }    
    },

    public: {
    
        c : 30,
        help : function(){
            console.log('Hello');
        },
        print : function(){
            this.help();
            this.helpMe();
            console.log(this.b);
            console.log(this.c);
        }
    }

});

var o = new c(10, 20);

o.print();

Lascia un commento

Inserisci i tuoi dati qui sotto o clicca su un'icona per effettuare l'accesso:

Logo WordPress.com

Stai commentando usando il tuo account WordPress.com. Chiudi sessione / Modifica )

Foto Twitter

Stai commentando usando il tuo account Twitter. Chiudi sessione / Modifica )

Foto di Facebook

Stai commentando usando il tuo account Facebook. Chiudi sessione / Modifica )

Google+ photo

Stai commentando usando il tuo account Google+. Chiudi sessione / Modifica )

Connessione a %s...