Node.js 十问

720 阅读4分钟
原文链接: zhuanlan.zhihu.com
简评:作者在一次技术分享中提了 10 个问题,结果在场的很多开发者不能准确的说出答案,看看这 10 个问题你都了解吗?

什么是调用堆栈,他是 V8 的一部分吗?

调用栈是 V8 的一部分。 调用栈在 V8 中是一个数据结构,用于追踪方法的调用。 每次调用一个函数时,V8 都会在调用栈中引用该函数。并且每次调用其他函数都会如此(包括递归调用的函数)。

当函数调用结束时 V8 将 Pop 该函数并且在函数调用处返回他的值。

了解这个很重要,因为每一个 Node 进程只会有一个调用栈,如果你的调用栈处于繁忙,那么你的这个 Node 进程便处于繁忙。

什么是事件循环?他是 V8 的一部分吗?

事件循环处在下图中的哪个模块?

事件循环是由 libuv 库提供的,它不是 V8 的一部分。

event loop 用于处理外部事件并且将其转化为回调,它会从事件队列中选择事件并将其事件回调 push 到调用栈中。

事件循环位于这张照片的中间,并且像一个组织者一样。当V8调用堆栈为空时,事件循环可以决定下一步执行什么。

当调用栈和 event loop 为空的时候,Node 会做些什么?

它只会退出。

当您运行 Node 程序时,Node 将自动启动事件循环,当该事件循环处于空闲状态并且没有任何其他操作时,该进程将退出。为了保持 Node 进程的运行,您需要在某个事件队列中放置某些东西。例如,当您启动计时器或 HTTP 服务器时,相当于告诉事件循环继续运行并检查这些事件。

除了 V8 和 Libuv,Node 还有哪些外部依赖?

以下是 Node 进程用到的依赖库:

  • http-parser
  • c-ares
  • OpenSSL
  • zlib

他们都是 Node 所用到的依赖库,他们有自己的源码,协议。 Node 只是使用了他们。所以如果你正在进行数据压缩并且报错了,不要怪 Node。

如果没有 V8 可以运行 Node吗?

这可能是一个棘手的问题。您需要一个 VM 来运行 Node 进程,但 V8 不是唯一可以使用的VM。你还可以使用 Chakra。

module.exports 和 exports 有什么区别?

你可以使用 module.exports 导出模块的 API,当然你也可以使用 exports。

module.exports.g = ...  // Ok
exports.g = ...         // Ok
module.exports = ...    // Ok
exports = ...           // Not Ok

export 只是 module.exports 的引用或者别名。真正起作用的是 module.exports ,如果给 exports 重新赋值,那么 exports 和 module.exports 将不再有关系。

为什么定义的顶级变量不是全局的?

如果你有 module1 定义了顶级变量 g:

// module1.js
var g = 42;

如果在 module2 require module1 并尝试访问变量 g,你会得到 g is not defined 的错误。

但是如果你在浏览器中执行相同的操作,则可以访问所有 scirpt 的顶级变量(已经定义过的),

每个 Node 文件都有一个自己的IIFE(即时调用函数表达式)。在 Node 文件中声明的所有变量都被限定在 IIFE 中。

相关问题:运行如下 Node 文件输出的是什么?

// script.js
console.log(arguments);

你会看到一些参数:

因为 Node 执行的是一个函数。Node 用一个函数包装你的代码,该函数显式地定义了如上五个参数。

exports,require 和 module 这些对象都可以在每个文件中全局可用,但在每个文件中都是不同的。这是如何做到的?

当您需要使用 require 对象时,您只需直接使用它,就像它是一个全局变量一样。但是,如果您在两个不同的文件中检查他们,您将看到两个不同的对象。怎么会这样?

同样是因为 IIFE:

如上所示,IIFF 将传递给您的代码以下五个参数:exports,require,module,__filename 和__dirname。当您在 Node 中使用这些变量时,这五个变量似乎是全局变量,但它们实际上只是函数参数。

什么是模块循环依赖:

如果你有一个 module1,module1 require module2 和 module2 反过来require module1,会发生什么?会报错吗?

// module1
require('./module2');
// module2
require('./module1');

这样并不会报错。Node 允许这种操作。

所以 module1 require module2,但是由于module2 require module1 (此时 module1 还没有完成),module1 只会得到 module2 的部分版本。

这样会被警告。

什么时候可以使用文件系统 *Sync(如readFileSync)

Node 中的每个 fs 方法都具有同步版本。为什么要使用同步方法而不是异步?

有时使用同步方法是个很好的选择。例如,它可以在服务器仍在加载的初始化中使用。使用同步方法是可以接受的,只要你使用同步方法是一次性的事情(像 Http 请求这种多次回调的肯定是错误的)。

原文:You don’t know Node

推荐阅读: 新 V8 即将推出,和 Node.js 性能变化