2012-03-24 10 views
32

Sono un programmatore che ha programmato in diverse lingue, sia funzionale che orientato OO. Ho programmato anche qualche Javascript, ma non ho mai usato (o dovuto usare) il polimorfismo in esso.Devo usare il polimorfismo in javascript?

Ora, come una sorta di progetto per hobby, vorrei portare alcune app che sono state scritte in Java e C# che usano pesantemente il polimorfismo in Javascript.

Ma a un primo sguardo ho letto un sacco di 'E' possibile, ma ...'

Così, c'è un'alternativa ad esso?

Un esempio di quello che vorrei scrivere in JS in pseudolang:

abstract class Shape{ { printSurface() } ; } 

class Rect : Shape() { printSurface() { print (sideA*sideB}} 

class Circle : Shape() { printSurface() { print { pi*r*r }} 

TheApp { myshapes.iterate(shape s) {s.printSurface() } } 

Quindi un caso classico polimorfico: iterare su baseclass.

Mi piacerebbe ottenere questo tipo di comportamento. Lo so che lo è il polimorismo, ma ci sono altri "schemi" che sto trascurando che raggiungono questo tipo di comportamento o dovrei studiare le possibilità di ereditarietà in Javascript?

risposta

43

Come detto, JavaScript è un linguaggio digitato dinamico basato sull'ereditarietà prototipale, quindi non è possibile utilizzare lo stesso approccio delle lingue tipizzate.Una versione JS di quello che hai scritto, potrebbe essere:

function Shape(){ 
    throw new Error("Abstract class") 
} 

Shape.prototype.printSurface = function() { 
    throw new Error ("Not implemented"); 
} 

function Rect() { 
    // constructor; 
} 

Rect.prototype = Object.create(Shape.prototype); 
Rect.prototype.printSurface = function() { 
    // Rect implementation 
} 

function Circle() { 
    // constructor; 
} 

Circle.prototype = Object.create(Shape.prototype); 
Circle.prototype.printSurface = function() { 
    // Circle implementation 
} 

Poi, nella tua app:

var obj = new Circle(); 

if (obj instanceof Shape) { 
    // do something with a shape object 
} 

Oppure, con duck typing:

if ("printSurface" in obj) 
    obj.printSurface(); 

// or 

if (obj.printSurface) 
    obj.printSurface(); 

// or a more specific check 
if (typeof obj.printSurface === "function") 
    obj.printSurface(); 

Hai freddo anche avere Shape come oggetto senza alcun costruttore, che è più "classe astratta" come:

var Shape = { 
    printSurface : function() { 
     throw new Error ("Not implemented"); 
    } 
} 

function Rect() { 
    // constructor; 
} 

Rect.prototype = Object.create(Shape); 
Rect.prototype.printSurface = function() { 
    // Rect implementation 
} 

function Circle() { 
    // constructor; 
} 

Circle.prototype = Object.create(Shape); 
Circle.prototype.printSurface = function() { 
    // Circle implementation 
} 

Si noti che in questo caso, non è possibile utilizzare instanceof più, quindi o si fallback di digitazione anatra o è necessario utilizzare isPrototypeOf, ma è disponibile solo negli ultimi browser:

if (Shape.isPrototypeOf(obj)) { 
    // do something with obj 
} 

Object.create non è disponibile in browser che non implementa le specifiche ES5, ma puoi facilmente utilizzare un polyfill (vedi il link).

+0

Tx, risposta molto bella. Ma pragmaticamente, andrò per JOOSE, la struttura di classe. – Peter

+0

Questo non funziona per Documento. Volevo creare NMLDocument che erediti dal documento ma è emerso un errore strano '[LenientThis]'. – m93a

14

Il "motivo" in gioco qui sarebbe "interfaccia". Finché tutti gli oggetti nella raccolta myshapes implementano il metodo printSurface(), tutto andrà bene.

Poiché Javascript è un linguaggio digitato in modo dinamico, non è necessario che gli oggetti di una raccolta siano correlati.

+1

sì suona un campanello, pitone come? – Peter

+1

Anche lo stesso esempio che ho usato è mostrato qui: http://knol.google.com/k/programming-to-the-interface-in-javascript-yes-it-can-be-done-er-i- mean-faked # – Peter

+1

Non capisco perché questa risposta non sia più ottimizzata. Indica la semplice verità su JavaScript e il suo polimorfismo è solo la denominazione delle proprietà allo stesso modo. Non ci sono vincoli di tipo di cui preoccuparsi. – hayavuk

7

so che questo può essere fatto con prototipi ma io non sono un maestro di usarlo. i prefer the object literal approach (più facile da visualizzare e ha una portata "privato")

//shape class 
var shape = function() { 
    //return an object which "shape" represents 
    return { 
     printSurface: function() { 
      alert('blank'); 
     }, 
     toInherit: function() { 
      alert('inherited from shape'); 
     } 
    } 
}; 

//rect class 
var rect = function() { 
    //inherit shape 
    var rectObj = shape(); 

    //private variable 
    var imPrivate = 'Arrr, i have been found by getter!'; 

    //override shape's function 
    rectObj.printSurface = function(msg) { 
     alert('surface of rect is ' + msg); 
    } 

    //you can add functions too 
    rectObj.newfunction = function() { 
     alert('i was added in rect'); 
    } 

    //getters and setters for private stuff work too 
    rectObj.newGetter = function(){ 
     return imPrivate; 
    } 

    //return the object which represents your rectangle 
    return rectObj; 
} 



//new rectangle 
var myrect = rect(); 

//this is the overridden function 
myrect.printSurface('foo'); 

//the appended function 
myrect.newfunction(); 

//inherited function 
myrect.toInherit(); 

//testing the getter 
alert(myrect.newGetter()); 

+0

Devo vederlo più tardi, ma sempre +1 per chi fa lo sforzo di fornire esempi di codice! – Peter

+1

@Weston: non è abbastanza il soggetto in questione da essere una risposta separata? I quadri possono essere discussi lì di. Per ora: la penalità delle prestazioni è sempre accettabile? – Peter

4

Come dice Weston, se non si ha la necessità di eredità allora la natura anatra tipizzato di un linguaggio dinamico come come Javascript ti dà il polimorfismo poiché non c'è alcun requisito nel linguaggio stesso per una classe base o un'interfaccia fortemente tipizzata.

Se si desidera utilizzare l'ereditarietà e fare cose come chiamare un'implementazione di una superclasse facilmente, questo può essere ottenuto con prototipi o letterali di oggetti come mostrato da Joeseph.

Un'altra cosa sarebbe quella di guardare Coffescript poiché questo compila verso il basso in Javascript dandovi tutta la bontà OO in una semplice sintassi. Scriverà tutto il materiale per la creazione di prototipi di bollettini per te. Lo svantaggio è che aggiunge questo passo aggiuntivo alla compilazione. Detto questo, scrivere una semplice gerarchia di classi come il tuo esempio sopra e vedere poi che cosa javascript viene visualizzato come risultato aiuta a mostrare come tutto può essere fatto.

+0

Coffeescript Lo so, ma il passo della compilazione è uno di troppo per me .. – Peter

4

Su un'altra nota. Se vuoi programmare Javascript in uno stile OO usando le classi, puoi guardare i molti "sistemi di classe" per Javascript. Un esempio è Joose (http://joose.it).

Molti framework lato client implementano il proprio sistema di classe. Un esempio di questo è ExtJS.

+2

provarlo ora, è davvero molto bello – Peter

+0

O semplicemente dimenticare le lezioni e abbracciare gli oggetti. – hayavuk