JavaScript:带有回调函数和“this”的原型

11

我创建了一个原型基类Person,它打开一个WebSocket连接并将回调函数定义为原型方法。

因为在回调函数内部this将指向WebSocket对象,所以我使用另一个变量来保存Personthis。然而,当我处理多个实例时,该变量会被覆盖。

这里是一个显示问题的小片段:

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,然后尝试通过socket发送消息时出现以下错误:

Uncaught Error: INVALID_STATE_ERR: DOM Exception 11

因此看起来self在全局范围内定义,并且我尝试在回调函数中获取对Personthis的引用失败。有什么建议吗?

你需要首先将它声明为 var,还有其他问题。 - qwertymk
@qwertymk var 使其局限于构造函数,这是另一个问题。 - unexplored
3个回答

13

当你执行以下操作时:

self = this

你隐式地创建了一个全局变量(因为它是全局的),将具有所有实例相同的值。局部变量必须在变量名前加上varletconst中的一个,例如:

var self = this;
const self = this;
let self = this;

但这并不是您的解决方案。您需要使用this。如果您要为WebSocket提供回调并且想要与之关联的人员,我建议您在WebSocket上放置对Person对象的引用,以便稍后可以从那里检索它。至于所有缺失的分号来结束每个语句,是怎么回事呢?无论如何,这是一些修复后的代码:

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
    }
}

+1 真是太简单了,我一直在寻找奇怪的解决方案。顺便说一下,对于 ; 我很抱歉。这是我的第一个 JavaScript 项目,直到你提到它并让我阅读相关资料,我才真正理解它们的重要性 :) - unexplored
“为什么每个语句都缺少分号?”你试图强加自己的编码风格。JavaScript是一种脚本语言,你不必担心那些愚蠢的分号。也许你被像Java或C这样的编译语言所创伤,但大多数其他脚本语言不使用分号(或很少使用)。每个人都应该自由决定是否使用分号,但选择后要保持一致性(而OP就是这样做的)。 - Cyril Duchon-Doris
2
@CyrilDuchon-Doris - 我对与你争论此事没有兴趣。我感兴趣的是编写我认为良好、稳健和安全的编码风格,即使语言不要求,我也会在我的答案中推荐这种风格。如果你想以不同的方式编写代码或撰写答案,请随意,我不会阻止你,但我会推荐我认为是一个好的风格。 - jfriend00

5
在JavaScript中声明变量时,如果没有在前面加上var,它将被视为全局变量,在你的情况下会导致一些问题。
虽然构造函数的行为符合预期,但你可能希望改为以下方式,以便将name保存到你正在创建的Person实例中:
// Constructor
function Person(name){
    // You don't need to reference "self" here. It's already implied.
    this.name = name;
}

此外,在WebSocket.onopen中,'this'从Person实例变为WebSocket实例。为了在WebSocket.onopen内引用它,您需要保留'Person'。
// 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/

1

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
    }
}

我知道这是全局的,我在我的问题中已经提到了。只是在寻找正确的做法。 - unexplored

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接