[译]你不知道的NodeJS

标签: 知道 nodejs | 发表时间:2020-01-28 11:34 | 作者:风过留情李寻欢
出处:https://juejin.im/welcome/frontend

你不知道的NodeJS

更新:这篇文章现在是我的书《Node.js进阶》的一部分。 在 jscomplete.com/node-beyond…中阅读此内容的更新版本以及有关Node.js的更多信息。

在今年的Forward.js会议(关于JavaScript的会议)上,我分享了题为“你不知道的NodeJS”的演讲。 在那次演讲中,我向观众提出了一系列有关Nodejs运行时的问题,大多数有技术背景的观众无法回答其中大多数问题。

我没有真正去统计这个数据但确实能在会议室里感觉到。演讲后一些有勇气的人走近我并且承认了这个事实。

这就是让我发表演讲的原因。 我认为我们没有以正确的方式教授Node.js! 关于Node.js的大多数学习内容都聚焦于Node包上,而不是它的运行时上。 大多数Node包将模块封装在自身的Node运行时中(例如http或stream)。 当你遇到问题时,这些问题可能是在自身运行时发生,并且如果你不了解Node运行时,就会遇到麻烦。

关于Node.js的大多数学习内容都聚焦于Node包上,而不是它的运行时上。

我为这篇文章精选一些问题和回答。列如下的标题中,可以尝试先闹中回答它们。(如果你在这里发现了错误或者有歧义的回答,请让我知道)

问题 #1: 什么是调用堆栈?它是V8的一部分吗?

调用肯定是V8的一部分。它是V8用于保存函数调用轨迹的一种数据结构。每次我们运行一个函数,V8都会将该函数的引用放入调用堆栈,并对该函数中嵌套的其他函数进行相同的操作。这也包括递归调用的函数。

当嵌套的函数运行结束,V8将一次弹出一个函数并用它的返回值替换它的位置。

为什么这对于Node很重要? 因为每个 Node进程仅获得一个调用堆栈。 如果使该调用堆栈处于繁忙状态,则整个 Node进程都处于繁忙状态。记住这一点。

问题 #2: 什么是事件轮询? 它是V8的一部分吗?

你认为下图中的事件轮询在哪里?

事件轮询由 libuv模块提供,它不是V8的一部分。

事件轮询是处理外部事件并将它们转换回调函数运行的一种机制。这种轮询会循环的从事件队列中选择事件执行,并将它们的回调函数推入调用堆栈中。

如果这是你第一次听到事件循环,则这些定义不会有太大帮助。 事件循环只是更大架构下中的一部分:

你需要理解事件轮询背后更大的架构、V8所扮演的角色、 Node.js的APIs以及知道这些事情是如何推入队列并被V8执行的。

Node.jsAPIs是像 setTimeoutfs.readFile这样的函数。这些并不是 JavaScript的一部分,而由 Node.js提供的函数。

事件循环位于这张照片的中间(实际上是它的一个更复杂的版本),并且像一个组织者。 当V8调用堆栈为空时,事件循环可以决定下一步执行什么。

问题 #3: 当调用堆栈和事件轮询队列全部为空时, Node.js会做什么?

它简单的退出.

当你启动一个 Node.js进程时,Node将自动启动事件轮询。当事件轮询处于空闲状态并且无其他事件去处理时,程序将退出。

To keep a Node process running, you need to place something somewhere in event queues. For example, when you start a timer or an HTTP server you are basically telling the event loop to keep running and checking on these events. 为了保持Node进程运行,你需要向事件队列中放入一些内容。比如,当你可以启动一个定时器或者一个 HTTP服务时,你就相当于告诉事件轮询保持运行,同时去监听一些事件。

问题 #4: 除了V8和Libuv,Node还具有其他哪些外部依赖项?

以下是一个Node进程所有可以使用的独立库:

  • http-parser
  • c-ares
  • OpenSSL
  • zlib 它们所有都独立于Node,它们都有拥有自己独立的源码以及证书。Node仅仅是使用它们。所以你需要记住这些,如果你想知道你的程序运行在什么地方。如果你正在处理数据压缩相关的事情,你可能会在 zlib库底层堆栈遇到遇到麻烦,那么你将面对一个 zilb相关的错误,而不是归责于Node。

问题 #5: Node能否不依赖于V8运行?

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

问题 #6: module.exportsexports有什么不同 ?

你可以一直使用 module.exports去导出模块的API。除一种情况外,你也可以使用 export:

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

exports.g = ...         // Ok

module.exports = ...    // Ok

exports = ...           // Not Ok
复制代码

为什么?

export仅仅是 module.export的一个别名或引用。 更改导出时,你将更改该引用,而不再更改官方API(即module.exports)。 你只需要在模块作用域中获取局部变量即可。

问题 #7: 为什么顶级变量不是全局变量?

如果你在模块 module1中定义了一个顶级变量 g:

  // module1.js

var g = 42;
复制代码

同时你有一个模块 module2引用了模块 module1,并且尝试访问变量 g,你将得到 g is not defined.的错误。

为什么?

如果你在浏览器端做相同的事情,你可以在该定义该顶级变量脚本之后的所有脚本里访问该顶级变量。 每个Node文件在后台都有其自己的IIFE(函数调用表达式)。 在Node文件中声明的所有变量都作用于该IIFE。

相关问题: 下面这个仅仅含有一行代码的Node文件将会输出什么?

  // script.js

console.log(arguments);
复制代码

你将会看一些参数!

为什么?

因为Node执行的是一个函数。Node将你的代码包装到一个函数中。该函数中明确定义了上图中你所见的5个参数。

问题 #8: 这些对象: exportrequiremodule都是全局可用的,然而在它们在每个文件又有所不同,为什么?

当你需要使用 require对象,你就是像一个全局变量那样直接使用它。然而,如果你在两个不同的文件中检查 require,你将看到两个不同的对象。为什么? 因为 由于具有相同的IIFE魔法:

如你所见, “这IIFE魔法”向你的代码中传递以下五个参数: exports, require, module, __filename, 和 __dirname。当你在Node中使用这5个参数时,它们看起来像全局变量,但实际上它们仅仅是函数参数。

问题 #9: 什么是Node中的循环依赖?

如果你定义一个模块 module1引用了模块 module2,同时模块 module2内部又引用了模块 module1。将发生什么?报错?

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

// module2
require('./module1');
复制代码

你不会得到报错。因为Node允许那种情况。 所以模块 module1引用模块 module2,但是因为模块 module2依赖模块 module1且模块 module1没有加载完成,模块 module1将仅仅获取到模块 module2的一个部分版本。程序将提示警告。

问题 #10: 什么时候适合使用文件系统的同步方法(如 readFileSync)?

Node模块 fs中的每一个方法都有一个同步版本。为什么你会使用一个同步方法代替一个异步方法?

有时候,使用同步方法会更好。比如服务器仍在加载时,可以在任何初始化步骤中使用它。 通常情况下,初始化步骤之后执行的所有操作都取决于在那里获取的数据。 只要你使用同步方法是一次性的,就可以使用同步方法来避免引入回调狱。

但是,如果您在处理程序(例如HTTP服务器请求回调)中使用同步方法,那简直就是100%错误。 不要那样做。

我希望你能够回答以上部分或者全部的问题。

感谢阅读。

相关 [知道 nodejs] 推荐:

[译]你不知道的NodeJS

- - 掘金前端
更新:这篇文章现在是我的书《Node.js进阶》的一部分. 在 jscomplete.com/node-beyond…中阅读此内容的更新版本以及有关Node.js的更多信息. 在今年的Forward.js会议(关于JavaScript的会议)上,我分享了题为“你不知道的NodeJS”的演讲. 在那次演讲中,我向观众提出了一系列有关Nodejs运行时的问题,大多数有技术背景的观众无法回答其中大多数问题.

nodejs快速入门

- AreYouOK? - 淘宝数据平台与产品部官方博客 tbdata.org
主要介绍了一下node.js的发展, 现状, 安装, 使用.

NodeJS学习笔记

- - Web前端 - ITeye博客
今天开始学习NodeJS,在这里做个笔记,记录一下我的学习历程,也方便以后参考. Node.js® 是一个基于  Chrome V8 引擎 的 JavaScript 运行时. 简单的说 Node.js 就是运行在服务端的 JavaScript. Node.js 是一个基于Chrome JavaScript 运行时建立的一个平台.

NodeJS与Mysql的交互

- - CSDN博客推荐文章
把Mysql Module装到 NodeJS中.   JS脚本 mysqlTest.js. //加载mysql Module  .   //要创建的数据库名  .     //要创建的表名  . 作者:qxs965266509 发表于2013-8-17 9:47:35 原文链接. 阅读:0 评论:0 查看评论.

nodejs web开发入门: Simple-TODO Nodejs 实现版

- Aleafs - CNode社区
看到simple todo的各种python版本实现, 我也来凑凑热闹…. 既然已经有这么多python版本了, 我就对比实现了一个Simple-TODO的nodejs版本: Node TODO, 模版和樣式全部copy自web.py版本.. 源代码: https://github.com/fengmk2/todo.

無痛安裝 NodeJS 和 Node Framework Express

- Hming - 小惡魔 - 電腦技術 - 工作筆記 - AppleBOY
直接到官網下載 Stable 的版本吧,目前是 node-v0.4.10.tar.gz,也可以先看看 API Document. 安裝 Ububtu 相關套件. 下面會使用最原始的編譯方式,所以必須安裝 g++ 套件,否則下 ./configure 的時候,會吐出來沒有安裝過的套件. 兩種方法:1.用 apt-get install nodejs 2.

nodejs-post文件上传原理详解

- never-online - CNode社区
浅谈HTTP中Get与Post的区别. 其中请求报文中的开始行和首部行包含了常见的各种信息,比如http协议版本,方法(GET/POST),accept-language,cookie等等. 而’实体主体’一般在post中使用,比如我们用表单上传文件,文件数据就是在这个’实体主体’当中. 写这篇教程的起因是因为在学习nodejs的过程中,想要自己实现一些文件上传的功能,于是不得不去研究POST.

eclipse配置nodejs开发环境

- - CSDN博客云计算推荐文章
首先说明一下本人的开发环境,个人兴趣爱好问题,这边使用的很多环境都是基于Linux系统下做的,这次也不例外. 前提条件:NodeJs已经在系统中正确安装. 1、下载安装eclipse,地址: http://www.eclipse.org/downloads/. Eclipse Standard 版本即可.

nodejs上HTML分析利器node-jquery

- - 博客园_首页
      首先描述产生这篇随笔的场景:我需要获取项目在jenkins构建的最新Javascript Coverage显示在供管理层次查看的项目情况Report上,但是由于jenkins没有直接的API取得数据所需数据,所以我们只能从自建的容器发布Javascript Coverage数据API,供Report项目使用.

简单高效的nodejs爬虫模型

- - 蓝猫的博客 - 分享前端开发经验和教程
这篇文章讲解一下 yunshare项目的爬虫模型. 使用nodejs开发爬虫很简单,不需要类似python的scrapy这样的爬虫框架,只需要用request或者superagent这样的http库就能完成大部分的爬虫工作了. 使用nodejs开发爬虫半年左右了,爬虫可以很简单,也可以很复杂. 简单的爬虫定向爬取一个网站,可能有个几万或者几十万的页面请求,复杂的爬虫类似google bot这样搜索引擎的蜘蛛爬虫,要每时每刻爬取互联网上最新的内容.