将一个函数绑定到多个套接字

Binding a function to multiple sockets

io.on('connection', function(socket) {

    socket.join('myRoom');
    if(condition){
        bindFunctions();
    }

});

假设第 4 个套接字连接时 condition 为真。下面的代码成功地将 .on 处理程序绑定到 myRoom.

中的所有套接字
bindFunctions(){
    for(var socketId in io.sockets.adapter.rooms["myRoom"]){

        var socket = io.sockets.connected[socketId];

        socket.on('my event',function(submit){
            console.log(socket.myVariable);
            //This is always the same property, no matter who calls this function.      

        }); 
    }
}

然而,当 'my event' 触发时,无论哪个客户端触发它,socket 对象始终是连接的第 4 个套接字。它始终是调用 bindFunctions 的套接字。本来以为handler socket应该和函数内部的socket是一样的

所以我有两个问题:

为什么 'my event' 函数中的套接字是调用 bindFunctions 的套接字而不是触发事件的套接字?

我该怎么做才能在函数内部访问调用 'my event' 的套接字的属性?

发生这种情况是因为尽管您在循环内执行 var socket = io.sockets.connected[socketId];,但 in Javascript, the var keyword is function scoped 而不是块范围。

胆量之下,真正发生的事情是:

bindFunctions(){
    var socket;
    for(var socketId in io.sockets.adapter.rooms["myRoom"]){

        socket = io.sockets.connected[socketId];

        socket.on('my event',function(submit){
            console.log(socket.myVariable);
            //This is always the same property, no matter who calls this function.      

        }); 
    }
}

因此,由于您的事件将在稍后发生,因此您的 socket 变量始终是最后一组。

要防止此行为,如果使用 Node.js 版本 > 4.0.0,则可以使用 const instead of var, because it's block scoped。但是在 Node.js 中,为了能够在块范围内使用 constlet 关键字,您必须将 'use strict'; 放在函数的开头,例如:

bindFunctions(){
    'use strict';
    for(var socketId in io.sockets.adapter.rooms["myRoom"]){

        const socket = io.sockets.connected[socketId];

        socket.on('my event',function(submit){
            console.log(socket.myVariable);
            //This is always the same property, no matter who calls this function.      

        }); 
    }
}

另一种解决问题的方法是创建一个 IIFE,因此您总是将当前变量传递给事件,例如:

bindFunctions(){
    for(var socketId in io.sockets.adapter.rooms["myRoom"]){

        var socket = io.sockets.connected[socketId];

        (function(socket) {
            socket.on('my event',function(submit){
                console.log(socket.myVariable);
            });
        })(socket);
    }
}

这样做,IIFE 中的 socket 变量将始终引用作为参数传递的对象,您将获得所需的行为。


您还可以使用 Object.keys 结合 Array.prototype.forEach 而不是 for...in 循环:

bindFunctions(){
    var room = io.sockets.adapter.rooms["myRoom"];
    Object.keys(room).forEach(function(key) {
        var socketId = room[key];

        var socket = io.sockets.connected[socketId];

        socket.on('my event',function(submit){
            console.log(socket.myVariable);    
        }); 
    });
}

这样做,您将在每次迭代中创建一个新的上下文,因为它是一个被调用的回调函数,并且您不会遇到函数作用域的问题。