2012-04-04 17 views
10

Ho creato una classe basata su prototipo Person che apre una connessione WebSocket e definisce le funzioni di callback come metodi prototipo.javascript: prototipi con callback e 'this'

Poiché all'interno del callback this si fa riferimento all'oggetto WebSocket, ho utilizzato un'altra variabile per conservare lo thisPerson. Tuttavia, quando gestisco più istanze, la variabile viene sovrascritta.

Ecco una piccola tagliò che mostra il problema:

function Person(name){ 
    self = this 
    self.name = name 
} 

Person.prototype = { 
    getName : function(){ 
     return self.name 
    }, 

    openConnection : function(host, port){ 
     self.pointCount = 0 
     self.ws = new WebSocket("ws://" + host + ":" + port) 
     self.ws.onopen = self.onOpenConnection 
    }, 

    onOpenConnection : function() { 
     console.log(this) // prints the websocket 
     console.log(self) // prints the person 
     self.ws.send(self.name) // works only if one person exists 
    } 
} 

var p1 = new Person("Jonh") 
var p2 = new Person("Adam") 

console.log(p1.getName()) // Prints Adam 
console.log(p2.getName()) // Prints Adam 

p1.openConnection("localhost", 7000) // opens connection for p1 
p2.openConnection("localhost", 7000) // opens another connection for p1  

Se più di un Person è creato, poi quando si cerca di inviare un messaggio tramite la presa ottengo il seguente errore:

Uncaught Error: INVALID_STATE_ERR: DOM Exception 11

Quindi sembra che lo self sia definito globalmente e il mio tentativo di ottenere un handle per lo thisthis all'interno del callback non riesce. Qualche suggerimento su come ottenerlo?

+0

È necessario dichiarare di essere un 'var' prima, tra le altre questioni – qwertymk

+0

@qwertymk' var' rende locali al costruttore, che è un altro problema – unexplored

risposta

10

Quando si esegue:

self = this 

Si sta implicitamente creando una variabile globale, che (dal momento che è globale) sarà avere lo stesso valore per tutte le istanze. Le variabili locali, devono avere var di fronte a loro in questo modo:

var self = this; 

Ma, questa non è la soluzione qui. Devi invece usare this. E, se hai intenzione di fornire un callback per il websocket e vuoi che la persona sia associata a questo, ti suggerirei di mettere un riferimento all'oggetto Person sul websocket in modo da poterlo recuperare da lì. E cosa c'è di tutto il punto e virgola mancante per terminare ogni affermazione? Ad ogni modo, ecco qualche codice sistemato:

function Person(name){ 
    this.name = name; 
} 

Person.prototype = { 
    getName : function(){ 
     return this.name; 
    }, 

    openConnection : function(host, port){ 
     this.pointCount = 0; 
     this.ws = new WebSocket("ws://" + host + ":" + port); 
     // save person reference on the web socket 
     // so we have access to the person from web socket callbacks 
     this.ws.person = this; 
     this.ws.onopen = this.onOpenConnection; 
    }, 

    onOpenConnection : function() { 
     // "this" will be the websocket 
     // "this.person" is the person object 
     console.log(this); // prints the websocket 
     console.log(this.person); // prints the person 
     this.send(this.person.name); // works only if one person exists 
    } 
} 
+0

+1 Accidenti, così semplice e stavo cercando non so dove trovare soluzioni strane. BTW mi dispiace per ';'. È il mio primo progetto javascript e non ho capito la loro importanza fino a quando non l'hai menzionato e mi hanno fatto leggere su di loro :) – unexplored

+0

'cosa c'è di tutto il punto e virgola mancante per terminare ogni affermazione? Stai cercando di imporre i tuoi stili di codifica. JavaSCRIPT è un linguaggio di scripting e non dovresti preoccuparti di quegli stupidi punti e virgola. Forse sei stato traumatizzato da linguaggi compilati come Java o C, ma la maggior parte degli altri linguaggi di scripting non usa il punto e virgola (o molto raramente). Tutti dovrebbero essere liberi di decidere se usare o meno il punto e virgola, ma essere coerenti con le scelte (e l'OP era). –

+2

@ CyrilDuchon-Doris - Non ho alcun interesse a discutere con te. Sono interessato a codificare ciò che considero buoni stili di codifica solidi e sicuri anche se la lingua non lo richiede e lo consiglierò nelle mie risposte. Se vuoi programmare il codice in modo diverso o scrivere in modo diverso le risposte andare avanti, non ti fermerò, ma ti consiglierò quello che penso sia un buon stile. – jfriend00

0

self = this

vostra creazione di una variabile globale, è per questo che il codice è rotto.

cercando anche di fare riferimento self all'interno del prototipo non funziona, utilizzare this

function Person(name){ 
    this.name = name 
} 

Person.prototype = { 
    openConnection : function(host, port){ 
     this.pointCount = 0 
     this.ws = new WebSocket("ws://" + host + ":" + port) 
     this.ws.onopen = this.onOpenConnection.bind(this) 
    }, 
    constructor: Person,  
    onOpenConnection : function() { 
     console.log(this) // prints the person 
     this.ws.send(this.name) // works only if one person exists 
    } 
} 
+0

Sono consapevole che è globale , L'ho menzionato nella mia domanda. Solo cercando i modi corretti per farlo – unexplored

5

Quando dichiarate variabili in Javascript, se non si mette un var di fronte, sarà trattata come una variabile globale, che provoca qualche problema nel tuo caso .

Mentre il costruttore si comporta come previsto, si consiglia di effettuare le seguenti operazioni, invece, in modo name viene salvato all'istanza di persona che si sta creando:

// Constructor 
function Person(name){ 
    // You don't need to reference "self" here. It's already implied. 
    this.name = name; 
} 

Inoltre, in WebSocket.onopen, ' questo 'cambia dall'istanza di una Persona all'istanza di un WebSocket. Dovrai conservare la "Persona" per fare riferimento a WebSocket.onopen.

// Prototype 
Person.prototype = { 
    getName : function(){ 
     // 'this' in this case refers to an instance of Person. 
     // So, when creating John, this.name will be John. 
     return this.name; 
    }, 

    openConnection : function(host, port) { 
     // Similar to getName(...), this refers to an instance of Person. 
     // In your example, this.pointCount is NOT shared between John and Adam 
     this.pointCount = 0; 
     this.ws = new WebSocket("ws://" + host + (port ? ':' + port : '')); 

     // In WebSocket.onopen below, you're working with a new scope, so you 
     // won't have access to 'this' as the Person anymore. You need to save 
     // 'this' somewhere, so you can reference it in the new scope. 
     // ***** 
     var self = this; 

     this.ws.onopen = function() { 
      // In this function, a new scope has been created. 'this' no 
      // longer refers to John/Adam (The instance of Person), but to 
      // WebSocket instead. 

      console.log(this); // 'this' references the WebSocket instance 
      console.log(self); // 'self' references the 'self' in the outer 
           // scope. See ***** 

      // Since this = WebSocket in this scope, all we need to do 
      // is this.send(...). If you'd like to obtain the refer 
      // to the instance of the Person you worked with, you can 
      // use the 'self' variable 
      this.send(self.name); 
     }; 
    } 
}; 

Spero che questo aiuti!Ecco un JSFiddle di andare con lui: http://jsfiddle.net/WFdbe/