V8 内部机制 - 匿名函数的处理方式

11

想了解完整的故事,请查看我的另一个问题

基本上,我曾经问过,在以下代码中,使用命名函数作为套接字处理程序是否更有效:

var app = require('express').createServer()
var io = require('socket.io').listen(app);

app.listen(8080);

// Some unrelated stuff

io.sockets.on('connection', function (socket) {
    socket.on('action1', function (data) {
        // logic for action1
    });

    socket.on('action2', function (data) {
        // logic for action2
    });

    socket.on('disconnect', function(){
        // logic for disconnect
    });
});

总的来说,答案是肯定的(请参见上面的链接以获取更多细节),但以下评论由ThiefMaster发布:

我不熟悉V8的内部结构,但它可能足够聪明,编译函数一次并每次重用它,只是附加了不同的作用域。

所以现在我的问题是:在通常情况下匿名函数会导致创建多个函数实例的情况下,V8是否足够聪明,可以编译一次匿名函数并在不同的作用域中重用它们?例如,在上面的代码中,我希望连接事件的处理程序只被创建一次,而对于每个连接,action1action2disconnect的处理程序都要被创建。在另一个问题中,这是通过使用命名函数来解决的,但我更感兴趣的是在V8中是否必须这样做,或者它是否会进行一些优化。


1
请注意,这与函数的命名或匿名无关,而是与您是否在每个连接上创建新函数有关。 (有关详细信息,请参见我的答案。) - T.J. Crowder
1个回答

9

是的。我曾在V8邮件列表上问过一个非常类似的问题(与我的情况相关,涉及从构造函数内创建函数)。 我收到的回复是函数的代码通常是“...被重用的...”,即使每次都有一个单独的函数对象(根据规范要求)。


请注意,您的问题与函数是命名还是匿名无关。在您的示例中,该函数可以有一个名称:
io.sockets.on('connection', function handleConnection(socket) {
    socket.on('action1', function (data) {
        // logic for action1
    });

    socket.on('action2', function (data) {
        // logic for action2
    });

    socket.on('disconnect', function(){
        // logic for disconnect
    });
});

这使用了一个命名函数表达式,在V8中是完全有效且被正确处理的。(不幸的是,IE8和更早版本不能正确处理,会在完全不同的时间创建两个完全不同的函数。但由于您正在使用V8,您不必担心这个问题。)


为什么函数是命名的还是匿名的并不重要呢?虽然这很有可能不重要,但我们是否确定V8对这两种类型的函数处理情况是一致的呢?尽管超出了原始问题的范围,但也有可能V8有一致的实现,但其他JavaScript运行时没有? - anishpatel
1
@anishpatel - 关于你的第一个问题,对我来说,怀疑函数是否有名称会影响这种优化是不可笑的。关于你的第二个问题,这个问题特别涉及V8,但是回答我邮件列表的V8贡献者Kevin Millikin说:“...我不确定其他JS引擎是否会为源文本中创建的不同函数对象复制代码,但如果他们这样做,我会感到非常惊讶。”那是在2011年,当时其他引擎的优化程度远不如现在。 :-) - T.J. Crowder

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