Proprietà private in Javascript

P

Proprietà private in Javascript

Una proprietà privata è una proprietà accessibile solo alle funzioni membro di istanze della stessa classe. A differenza di altri linguaggi, JavaScript non dispone ancora di supporto nativo per proprietà private. Col tempo le persone hanno trovato vari modi per emulare proprietà private utilizzando le funzioni linguistiche esistenti e le due tecniche comuni sono i prefissi e le closures.

Entrambi gli approcci hanno inconvenienti, sono rispettivamente non restrittivi o troppo restrittivi. Una migliore alternativa è quella di utilizzare oggetti WeakMap che risolvono entrambi questi problemi. Anche qui mostrato è come generalizzare l’idea di utilizzare WeakMaps, associando una o più proprietà private ad un oggetto, associando uno o più namespaces a ciascun oggetto. Uno namespace è semplicemente un oggetto su cui si definiscono una o più proprietà private.

 

Usando i Prefissi

Una tecnica comune per implementare proprietà private è quella di prefiggere ogni nome di proprietà privata con un sottolineatura. Si consideri il seguente esempio:

function Punto(x, y) {
this._x = x;
this._y = y;
}

Le proprietà _x e _y sono destinate a essere private e consentono l’accesso solo alle funzioni dei membri.

Per dare permessi di lettura e scrittura a una proprietà privata da qualsiasi funzione, una cosa comune è definire le funzioni getter / setter della proprietà, rispettivamente:

Punto.prototype.getX = function () {
return this._x;
};

Punto.prototype.setX = function (x) {
this._x = x;
};

Punto.prototype.getY = function () {
return this._y;
};

Punto.prototype.setY = function (y) {
this._y = y;
};

La tecnica di cui sopra è semplice e esprime in modo chiara l’intenzione. Tuttavia, l’utilizzo di un prefisso di sottolineatura è solo una convenzione di codifica e non è attuato dalla lingua: non c’è nulla per impedire ad un utente di accedere direttamente a una proprietà che dovrebbe essere privata.

 

Usando LE Closures

Un’altra tecnica comune è definire le proprietà private come variabili e le loro funzioni getter e setter come una closure su queste variabili:

function Punto(_x, _y) {
this.getX = function () {
return _x;
};

this.setX = function (x) {
_x = x;
};

this.getY = function () {
return _y;
};

this.setY = function (y) {
_y = y;
};
}

Questa tecnica richiede le funzioni membro che hanno bisogno di accedere a proprietà private da definire sull’oggetto stesso, anziché il suo prototipo. Ciò è leggermente meno efficace dell’utilizzo della convenzione di sottolineatura, ma non significativamente per la maggior parte delle applicazioni.

Il vantaggio di questa tecnica è che offre più protezione: non c’è modo per l’utente di accedere a una proprietà privata, ad eccezione dell’utilizzo della funzione getter o setter. Tuttavia, l’uso di closures rende le proprietà private troppo restrittive: poiché non vi è modo di accedere a variabili in una closure da un’altra closure, non c’è modo per gli oggetti della stessa classe di accedere alle proprietà private di ciascuno.

 

Da WeakMap a Namespace

Le tecniche sopra indicate non sono sufficientemente restrittive (prefissi) o troppo restrittive (closures), tuttavia la recente introduzione di WeakMaps fornisce una soluzione. WeakMaps sono state introdotte in JavaScript in ECMAScript 2015.

Usando i Weakmaps una soluzione migliore sarebbe quella di memorizzare tutte le proprietà private su un singolo oggetto, denominato spazio dei nomi, e quindi memorizzare lo spazio dei nomi come proprietà privata nell’oggetto originale. Utilizzando gli spazi dei nomi, l’esempio precedente può essere riscritto come segue:

const Punto = (function () {

    let map = new WeakMap();

    let internal = function (object) {
        if (!map.has(object)) map.set(object, {});
        return map.get(object);
    }

    function Punto(x, y) {
        internal(this).x = x;
        internal(this).y = y;
    }

    Punto.prototype.getX = function () {
        return internal(this).x;
    };

    Punto.prototype.setX = function (x) {
        internal(this).x = x;
    };

    Punto.prototype.getY = function () {
        return internal(this).y;
    };

    Punto.prototype.setY = function () {
        internal(this).y = y;
    };

    return Punto;

})();

 

Usando le funzionalità di ES6 la funzione costruttore Punto può essere scritta cosi:

class Punto {
constructor(x, y) {
internal(this).x = x;
internal(this).y = y;
}

getX() {
return internal(this).x;
}

setX(x) {
internal(this).x = x;
}

getY() {
return internal(this).y;
}

setY() {
internal(this).y = y;
}
}

L’unico modo che una funzione ha di accedere alle proprietà x e y è avere un riferimento ad un’istanza Point e al suo namespace interno. Mantenere lo namespace nascosto da tutte le funzioni, tranne i membri di Punto, implementa efficacemente proprietà private. Inoltre, poiché i membri di Punto hanno un riferimento allo namespace interno, possono accedere alle proprietà private in altre istanze di Punto.

 

A proposito di me

Andrew Raieta

Smanettone e appassionato di hardware fin da piccolo a 13 anni monta il suo primo PC, così iniziando ad esplorare quel mondo meraviglioso di linux ed essere sempre di più un utente attivo. Attualmetne studente di computer science presso l'università la Sapienza e nel frattempo decide di dedicarsi allo studio per formarsi come data science. Nell'ottobre del 2017 decide di scrivere sul blog per far scoprire il mondo di linux e condividere interessanti articoli tecnici sullo scripting.

Di Andrew Raieta

Gli articoli più letti

Articoli recenti

Commenti recenti