此代码存在一些问题,但总体思路是为每个传入请求在 req 对象 req.emitToUser() 上定义一个方法,这将允许链中稍后的一些其他路由处理程序使用该方法发出给提出请求的用户。这是希望将当前连接的 socket.io 连接连接到发出 http 请求的用户的常见愿望。
让我们看看这里的每一行:
redis.get(req.user.email, function(err, id) {
在 redis 数据库中查找 req.user.email 以获取与该电子邮件相关联的 socket.io id,该电子邮件之前已保存在该 redis 数据库中。
if (err) next(err);
如果在 redis 中没有找到,让这个请求失败并报错。
req.emitToUser = function() {
为当前的req 对象分配一个新方法,以便链中后面的其他路由处理程序可以使用该方法。
var soc = id && io.to(id);
在 socket.io 中查找 id 值以获取该 id 的套接字。从技术上讲,io.to() 不会返回套接字,但它会返回一个您可以调用 emit() 的对象,该对象将发送到该套接字。
soc.emit.apply(soc, arguments);
soc.emit.apply(soc, arguments);的作用是这样的:
- 执行
soc.emit()方法
- 在对
soc 对象执行该方法时设置this 值。
- 在执行该方法时将参数设置为调用时传递给
req.emitToUser(x, y, z) 的任何参数。
这是一个更具体的例子:
function fn(a, b, c) {
console.log(a, b, c);
}
fn.apply(null, [1, 2, 3]);
使用fn.apply(null, [1, 2, 3]); 将等同于:
fn(1, 2, 3);
现在,当参数已知时,您可能永远不会以这种确切的方式使用.apply()。使用它的情况是当您有一些任意数组传递给您时(您不知道其中有什么),并且您希望将这些参数以与提供给您的顺序完全相同的顺序传递给其他函数.这就是soc.emit.apply(soc, arguments); 正在做的事情。它采用arguments 对象(这是一个类似数组的结构,表示传递给父函数req.emitToUser() 的参数并将这些确切的参数传递给它sock.emit()。如果您确切知道会有多少参数,那么您可以硬编码与此相同的代码:
app.use(function(req,res,next) {
redis.get(req.user.email, function(err, id) {
if (err) next(err);
req.emitToUser = function(msg, data) {
var soc = id && io.to(id);
soc.emit(msg, data);
}
});
});
但是,.apply() 创建了一个更通用的解决方案,无论将多少参数传递给req.emitToUser(),它都可以正常工作,因为它只会将所有参数传递给soc.emit()。
这行代码有点可疑:
var soc = id && io.to(id);
它似乎试图防止之前没有从 redis 返回正确的 id。但是,如果没有 id,那么 soc 将不是一个有效的对象,下面类似的代码:
soc.emit.apply(soc, arguments);
会抛出。所以,id && io.to(id) 并没有真正提供适当的保护。看起来这更有可能是:
app.use(function(req,res,next) {
redis.get(req.user.email, function(err, id) {
if (err) next(err);
req.emitToUser = function() {
if (id) {
var soc = io.to(id);
soc.emit.apply(soc, arguments);
} else {
// not sure what you want here, perhaps return an error
// or throw a more meaningful exception
}
}
});
});