javascript: прототипы с обратными вызовами и "это"
Я создал прототип класса Person
который открывает соединение WebSocket и определяет функции обратного вызова как прототипы.
Потому что внутри обратного вызова this
будет ссылаться на объект WebSocket Я использовал другую переменную, держаться за Person
this
. Однако, когда я имею дело с несколькими экземплярами, переменная перезаписывается.
Вот небольшой фрагмент, который показывает проблему:
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
Если создано несколько Person
, то при попытке отправить сообщение через сокет я получаю следующую ошибку:
Ошибка при сбое: INVALID_STATE_ERR: исключение DOM 11
Таким образом, кажется, что self
определяется глобально, и моя попытка получить обращение к Person
this
внутри обратного вызова не удается. Любые предложения о том, как этого достичь?
Ответы
Ответ 1
Когда вы выполните:
self = this
Вы неявно создаете глобальную переменную, которая (поскольку она глобальна) будет иметь одинаковое значение для всех экземпляров. Локальные переменные должны иметь var
, let
или const
перед ними, как один из них:
var self = this;
const self = this;
let self = this;
Но это не ваше решение. Вы должны использовать this
вместо этого. И, если вы собираетесь предоставить обратный вызов для websocket, и вы хотите, чтобы связанный с ним человек, я бы предложил вам просто поместить ссылку на объект Person на websocket, чтобы потом вы могли получить его оттуда. И, что со всеми недостающими точками с запятой заканчивается каждое утверждение? Во всяком случае, вот какой-то исправленный код:
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
}
}
Ответ 2
При объявлении переменных в Javascript, если вы не ставите переднюю переменную var
, она будет рассматриваться как глобальная переменная, что может вызвать некоторые проблемы в вашем случае.
В то время как конструктор ведет себя так, как ожидалось, вы можете сделать следующее вместо этого, поэтому name
сохраняется в экземпляре Person, который вы создаете:
// Constructor
function Person(name){
// You don't need to reference "self" here. It already implied.
this.name = name;
}
Кроме того, в WebSocket.onopen "это" изменяется от экземпляра Person к экземпляру WebSocket. Вам нужно будет сохранить "Личность", чтобы ссылаться на нее в 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);
};
}
};
Надеюсь это поможет! Здесь JSFiddle, чтобы пойти с ним: http://jsfiddle.net/WFdbe/
Ответ 3
self = this
Создавая глобальную переменную, почему ваш код сломан.
Кроме того, попытка ссылки на self
внутри прототипа не работает, используйте 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
}
}