3 modi per definire una “classe” JavaScript

JavaScript è un linguaggio molto flessibile orientato agli oggetti per quando riguarda la sintassi. In questo articolo potete trovare tre modi di definire e istanziare un oggetto. Anche se avete già scelto il vostro modo preferito per farlo, è utile conoscere alcune alternative per poter leggere il codice di altre persone.

È importante notare che non ci sono classi in JavaScript. Le funzioni possono essere utilizzati per simulare in qualche modo le classi, ma in generale JavaScript è un linguaggio di classe inferiore. Tutto è un oggetto. E quando si tratta di eredità, gli oggetti ereditano da oggetti e non classi da classi come nella “classi”-ci linguaggi di programmazione.

1. Utilizzare una funzione

Questo è probabilmente uno dei modi più comuni. È possibile definire una funzione JavaScript normale e quindi si crea un oggetto utilizzando la parola chiave new. Per definire le proprietà e i metodi di un oggetto creato con la funzione(), si utilizza la parola chiave this, come si vede nel seguente esempio.

function Apple (type) {
    this.type = type;
    this.color = "red";
    this.getInfo = getAppleInfo;
}
 
// anti-modello! continuate a leggere...
function getAppleInfo() {
    return this.color + ' ' + this.type + ' apple';
}

Per creare un’istanza di un oggetto utilizzando la funzione di costruzione di “Apple”, impostiamo alcune proprietà e chiamamo i metodi che si possono fare, con le seguenti operazioni:

var apple = new Apple('macintosh');
apple.color = "reddish";
alert(apple.getInfo());

1.1. Metodi definiti internamente

Nell’esempio precedente si vede che il metodo “getInfo()” della “classe” Apple è stata definito nella funzione separata “getAppleInfo()”. Anche se questo funziona bene, ha uno svantaggio – si può finire per definire di un sacco di queste funzioni e sono tutte nel “namespece globale”. Questo significa che si può incorrere in conflitti di denominazione, se tu (o un’altra libreria che stai utilizzando) decidi di creare un’altra funzione con lo stesso nome. Il modo per prevenire l’inquinamento dello spazio dei nomi globale, è definire i metodi all’interno della funzione di costruzione, come in questo caso:

function Apple (type) {
    this.type = type;
    this.color = "red";
    this.getInfo = function() {
        return this.color + ' ' + this.type + ' apple';
    };
}

Usando questa sintassi, non cambia nulla nel modo in cui si crea un’istanza dell’oggetto e nel modo in cui si utilizzano le sue proprietà e metodi.

1.2. Metodi aggiunti al prototipo

Un inconveniente dell’esempio 1.1. è che il metodo “getInfo()” viene ricreato ogni volta che si crea un nuovo oggetto. A volte questo può essere ciò che si vuole, ma è raro. Un modo più economico è quello di aggiungere “getInfo()” al prototipo della funzione di costruzione.

  function Apple (type) {
    this.type = type;
    this.color = "red";
}
 
Apple.prototype.getInfo = function() {
    return this.color + ' ' + this.type + ' apple';
};

Anche in questo caso, è possibile utilizzare i nuovi oggetti esattamente nello stesso modo come in 1. e 1.1.

2. Utilizzando gli “oggetti letterali”

I “Letterali” sono il modo più breve per definire gli oggetti e gli array in JavaScript. Per creare un oggetto vuoto si può fare:

var o = {};

invece del modo “normale”:

var o = new Object ();

Per gli array si può fare:

var a = [];

invece di:

var a = new Array ();

Quindi, è possibile saltare il processo delle simil-classi e creare un’istanza (oggetto) immediatamente. Ecco la stessa funzionalità descritta negli esempi precedenti, ma utilizzando questa volta la sintassi letterale dell’oggetto:

var apple = {
    type: "macintosh",
    color: "red",
    getInfo: function () {
        return this.color + ' ' + this.type + ' apple';
    }
}

In questo caso non è necessario (e non può), creare un’istanza della classe, esiste già. Quindi, è sufficiente usare questo esempio.

apple.color = "reddish";
alert(apple.getInfo());

Tale oggetto è talvolta chiamato anche “singleton”. E un “classico” per linguaggi come Java, “singleton” significa che si può avere solo una singola istanza di questa classe, in qualsiasi momento, e non è possibile creare più oggetti della stessa classe. In Javascript (non esiste nessuna classe, ricordate?!) questo concetto non ha senso più dal momento che tutti gli oggetti sono “single” fin da subito!

3. Singleton utilizzando una funzione

(Di nuovo il “singleton”, eh?)

Il terzo modo presentato in questo articolo è una combinazione degli altri due già visti. È possibile utilizzare una funzione per definire un oggetto “Singleton”. Ecco la sintassi:

var apple = new function() {
    this.type = "macintosh";
    this.color = "red";
    this.getInfo = function () {
        return this.color + ' ' + this.type + ' apple';
    };
}

Così si vede questo modo è molto simile al metodo 1.1. discusso in precedenza, ma il modo di utilizzare l’oggetto è esattamente come nell’esempio 2.

apple.color = "reddish";
alert(apple.getInfo());

new function(){...} fa due cose allo stesso tempo: definire una funzione (una funzione di costruzione anonima) e invocarla con una nuova. Potrebbe sembrare un po’ strano e mettere confusione se non ci siete abituati ad esso, e non è un modo troppo comune, ma hey, è una opzione, quando si vuole veramente una funzione di costruzione che verrà utilizzato solo una volta e non c’è senso di dare un nome.

Ecco un po’ di ulteriori esempi!

01

Il modo migliore / corretta per creare una classe personalizzata in javascript è quello di utilizzare l’oggetto prototipo.

function myClass(opts){
//constructor here
}

myClass.prototype.myMethod=function(){
//method logic here
}

…che nel “concreto” diventa:

//Definiamo una classe, così
function Person(name, gender){

   // Aggiungiamo le proprietà degli oggetti, come queste
   this.name = name;
   this.gender = gender;
}

// Aggiunge metodi come questo. Tutti gli oggetti "persona" saranno in grado di invocarlo:
Person.prototype.speak = function(){
    alert("Howdy, my name is" + this.name);
}

// Possimamo istanziare nuovi oggetti con il 'new'
var person = new Person("Bob", "M");

// ...e richiamare metodi come questo:
person.speak(); // alerts "Ciao, il mio nome è Bob"

Questa è una sorta di mix tra i metodi 1 e 1.1. Il name space globale è lasciato indenne dal metodo come nell’esempio 1, e non c’è utilizzo di memorioa inutile come nell’esempio 1.1.

Per dichiarare più proprietà e metodi nel prototipo, creare un oggetto come nell’esempio 2.

myClass.prototype = {
myProperty:”this is a string”,
myMethod:function(parms){
//method logic here
}
}

Poi si crea una nuova istanza della classe … var esempio = new MyClass ();

02

L’esempio seguente sfrutta lo scoping per consentire campi e metodi privati​​.

mployee = (function(){

  // campo statico privato
  var staticVar;

  // funzione "class" aka di costruzione, o "constructor"
  function cls()
  {
    // campo di istanza privata
    var name = "";
    var self = this;

    // campo di istanza pubblica
    this.age = 10;

    // metodo di istanza privata
    function increment()
    {
        // va usato "self" anziché "this"
        self.age ++;
    }

    // metodo di istanza pubblica  
    this.getName = function(){
        return cls.capitalize(name);
    };

    this.setName = function(name2){
        name = name2;
    };

    this.increment = function(){
        increment();
    };

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

  // campo statico pubblico
  cls.staticVar = 0;

  // metodo statico pubblico
  cls.capitalize = function(name){
      return name.substring(0, 1).toUpperCase() + 
          name.substring(1).toLowerCase();
  };

  // metodo statico privato
  function createWithName(name)
  {
    var obj = new cls();
    obj.setName(cls.capitalize(name));
    return obj;
  }

  return cls;
})();

john = new Employee();
john.setName("john");

mary = new Employee();
mary.setName("mary");
mary.increment();

alert("John's name: " + john.getName() + ", age==10: "+john.getAge());
alert("Mary's name: " + mary.getName() + ", age==11: "+mary.getAge());

Ecco infine un link utile per capire meglio il concetto di
ereditarietà nelle ‘classi’ JavaScript.