精华内容
下载资源
问答
  • nodejs 面试题

    千次阅读 2017-10-31 23:33:55
    nodejs面试题
     

    1、什么是错误优先的回调函数?

    错误优先的回调函数用于传递错误和数据。第一个参数始终应该是一个错误对象, 用于检查程序是否发生了错误。其余的参数用于传递数据。

    s.readFile(filePath, function(err, data) {  
        if (err) {
            //handle the error
        }
        // use the data object
    });

    解析:这个题目的主要作用在于检查被面试者对于Node中异步操作的一些基本知识的掌握。

    2、如何避免回调地狱

    引用:http://www.cnblogs.com/greatluoluo/p/6288931.html

    为了解决这个阻塞问题,JavaScript严重依赖于回调,这是在长时间运行的进程(IO,定时器等)完成后运行的函数,因此允许代码执行经过长时间运行的任务。

    downloadFile('example.com/weather.json', function(err, data) {  
        console.log('Got weather data:', data);
    });

    但是,问题来了,回调地狱

    虽然回调的概念在理论上是巨大的,但它可能导致一些真正令人困惑和难以阅读的代码。 想象一下,如果你需要在回调后进行回调

    这种层层嵌套的代码给开发带来了很多问题,主要体现在:
    
    1.代码可能性变差
    2.调试困难
    3.出现异常后难以排查
    • Use modules

    在几乎每种编程语言中,降低复杂性的最好方法之一是模块化。 JavaScript也不例外。 每当你编写代码时,花一些时间来回顾一下你是否经常遇到一个常见的模式。

    你在不同的地方多次写相同的代码吗? 你的代码的不同部分是否遵循一个共同的主题? 如果是这样,你有机会清理东西,抽象和重用代码。

    有数千个模块,你可以看看供参考,但这里有几个要考虑。 它们处理常见的,但非常具体的任务,否则会扰乱你的代码并降低可读性:Pluralize,csv,qs,clone。

    Here is a new file called formuploader.js that contains our two functions from before:

    module.exports.submit = formSubmit
    
    function formSubmit (submitEvent) {
      var name = document.querySelector('input').value
      request({
        uri: "http://example.com/upload",
        body: name,
        method: "POST"
      }, postResponse)
    }
    
    function postResponse (err, response, body) {
      var statusMessage = document.querySelector('.status')
      if (err) return statusMessage.value = err
      statusMessage.value = body
    }

    Now that we have formuploader.js (and it is loaded in the page as a script tag after being browserified) we just need to require it and use it! Here is how our application specific code looks now:

    var formUploader = require('formuploader')
    document.querySelector('form').onsubmit = formUploader.submit
    • Promises

    虽然Promises可以花费一些时间来掌握,但在我看来,它们是您可以在JavaScript中学习的更重要的概念之一。 它不仅大大减少了代码行数,而且使代码的逻辑流程更容易遵循。

    这里是一个使用非常快,非常受欢迎的Promise库,Bluebird的例子:

    var Promise = require('bluebird');  
    var fs = require('fs');  
    Promise.promisifyAll(fs);
    
    var myFile = '/tmp/test';  
    fs.readFileAsync(myFile, 'utf8').then(function(txt) {  
        txt = txt + '\nAppended something!';
        fs.writeFile(myFile, txt);
    }).then(function() {
        console.log('Appended text!');
    }).catch(function(err) {
        console.log(err);
    });

    请注意,这个解决方案不仅比以前的解决方案更短,而且更容易阅读(尽管,诚然,Promise风格的代码可能需要一些习惯)。 花时间学习和理解承诺,这将是值得你的时间。 但是,Promise绝对不是解决我们在异步编程中的所有问题,所以不要假设通过使用它们,你会有一个快速,干净,无bug的应用程序。 关键是知道什么时候对你有用。

    一些Promise库你应该检查是Q,bluebird,或内置Promises如果你使用ES6的话。

    • Async/Await

    注意:这是一个ES7功能,目前不支持Node或io.js。 但是,你现在可以使用它像Babel一样的转换器。

    清除代码的另一个选项是我最近喜欢的(当它有更广泛的支持时),它使用异步函数。 这将允许你编写看起来更像同步代码,但仍然是异步的代码。

    async function getUser(id) {  
        if (id) {
            return await db.user.byId(id);
        } else {
            throw 'Invalid ID!';
        }
    }
    
    try {  
        let user = await getUser(123);
    } catch(err) {
        console.error(err);
    }

    The db.user.byId(id) call returns a Promise , which we'd normally have to use with .then() , but with await we can return the resolved value directly. Notice that the function containing the await call is prefixed with async , which tells us that it contains asynchronous code and must also be called with await. Another big advantage to this method is we can now use try/catch, for, and while with our asynchronous functions,which is much more intuitive than chaining promises together.Aside from using transpilers like Babel and Traceur, you can also get functionality like this in Node with the asyncawait package.

    async/await 语法到底好在哪里?一图胜千言


    1.png

    2.png

    3.png

    3、如何用Node监听80端口

    这题有陷阱!在类Unix系统中你不应该尝试去监听80端口,因为这需要超级用户权限。 因此不推荐让你的应用直接监听这个端口。
      目前,如果你一定要让你的应用监听80端口的话,你可以有通过在Node应用的前方再增加一层反向代理 (例如nginx)来实现,如下图所示。否则,建议你直接监听大于1024的端口。


    4.png

    方向代理指的是以代理服务器来接收Internet上的连接请求,然后将请求转发给内部网络上的服务器, 并且将服务器返回的结果发送给客户端。
    关于反向代理的更多内容,建议你阅读这篇文章

    解释:这个问题用于检查被面试者是否有实际运行Node应用的经验。

    4.什么是事件循环

    Node采用的是单线程的处理机制(所有的I/O请求都采用非阻塞的工作方式),至少从Node.js开发者的角度是这样的。 而在底层,Node.js借助libuv来作为抽象封装层, 从而屏蔽不同操作系统的差异,Node可以借助livuv来来实现多线程。下图表示了Node和libuv的关系。


    5.png


    Libuv库负责Node API的执行。它将不同的任务分配给不同的线程,形成一个事件循环, 以异步的方式将任务的执行结果返回给V8引擎。可以简单用下面这张图来表示。


    6.png

    每一个I/O都需要一个回调函数——一旦执行完便推到事件循环上用于执行。 如果你需要更多详细的解释,可以参考这个视频。 你也可以参考这篇文章
      解释:这用于检查Node.js的底层知识,例如什么是libuv,它的作用是什么。

    Event Loop:  http://www.ruanyifeng.com/blog/2014/10/event-loop.html

    5、哪些工具可以用来保证一致性的代码风格

    你可以选择如下的工具:

    JSLint
    JSHint
    ESLint
    JSCS - 推荐

      在团队开发中,这些工具对于编写代码非常的有帮助,能够帮助团队开发者强制执行规定的风格指南, 还能够通过静态分析捕获常见的错误。
      解析:用于检查被面试者是否有大型项目开发经验。

    6、运算错误与程序员错误的区别

    运算错误并不是bug,这是和系统相关的问题,例如请求超时或者硬件故障。而程序员错误就是所谓的bug。
    解析:这个题目和Node关系并不大,用于考察面试者的基础知识。

    7、使用NPM有哪些好处?

    通过NPM,你可以安装和管理项目的依赖,并且能够指明依赖项的具体版本号。 对于Node应用开发而言,你可以通过package.json文件来管理项目信息,配置脚本, 以及指明项目依赖的具体版本。
      关于NPM的更多信息,你可以参考官方文档
      解析:它能考察面试者使用npm命令的基础知识和Node.js开发的实际经验。

    8、什么是Stub?举个使用场景

    Stub是用于模拟一个组件或模块的函数或程序。在测试用例中, 简单的说,你可以用Stub去模拟一个方法,从而避免调用真实的方法, 使用Stub你还可以返回虚构的结果。你可以配合断言使用Stub。

      举个例子,在一个读取文件的场景中,当你不想读取一个真正的文件时:

    var fs = require('fs');
    
    var readFileStub = sinon.stub(fs, 'readFile', function (path, cb) {  
        return cb(null, 'filecontent');
    });
    
    expect(readFileStub).to.be.called;  
    readFileStub.restore();

    在单元测试中:Stub是完全模拟一个外部依赖,而Mock常用来判断测试通过还是失败。
    有关Node.js的单元测试小结,你可以参考这个链接。  

    解析:用于测试被面试者是否有测试的经验。如果被面试者知道什么是Stub, 那么可以继续问他是如何做单元测试的。

    8、什么是测试金字塔?

    测试金字塔指的是: 当我们在编写测试用例时,底层的单元测试应该远比上层的端到端测试要多。


    7.png

    当我们谈到HTTP API时,我们可能会涉及到:

    有很多针对模型的底层单元测试
    但你需要测试模型间如何交互时,需要减少集成测试  

    解析:本文主要考察被面试者的在测试方面的经验。

    9、你最喜欢的HTTP框架以及原因

      这题没有唯一的答案。本题主要考察被面试者对于他所使用的Node框架的理解程度, 考察他是否能够给出选择该框架的理由,优缺点等。常用的HTTP框架你可以参考这个网站

    Statement
    原文地址:https://blog.risingstack.com/node-js-interview-questions



    展开全文
  • nodejs面试题

    2021-05-09 11:03:58
    整理一小部分nodejs面试题1. nodejs是什么?2. nodejs和前端js的区别?3. nodejs如何进行调试?4. 当前文件和目录的路径如何获取?5. commonjs和es6 import的区别?6. path.resolve和path.join的区别?7. 事件循环...


    1. nodejs是什么?

    1. Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时
    2. nodejs出现之前,js只能在游览器运行
    3. nodejs出现之后,js可以在任何安装nodejs的环境运行

    2. nodejs和前端js的区别?

    在这里插入图片描述

    1. 语法不同
      nodejs和js有共同的es语法,但是API不一样
    2. 应用不同
      2.1 前端js用于网页,在游览器运行
      2.2 nodejs可用于服务端,如开发web server
      2.3 nodejs 也可以用于本机,如webpack等本机工具

    3. nodejs如何进行调试?

    在这里插入图片描述

    1. 启动nodejs 服务时,使用inspect
    2. 代码中使用debugger断点
    3. 使用Chrome调试,在游览器输入: chrome://inspect

    4. 当前文件和目录的路径如何获取?

    1. __filename 获取当前文件
    2. __dirname 获取当前路径

    5. commonjs和es6 module的区别?

    1. 语法不同
    2. commonjs是动态引入,执行时引入
    3. ES6 module 是静态引入,编译时引入,一般放在顶部
      在这里插入图片描述

    6. path.resolve和path.join的区别?

    1. 两者都是拼接文件路径
    2. path.resolve 获取绝对路径
    3. path.join 获取在相对路径
      在这里插入图片描述

    7. 事件循环(event loop)在nodejs和游览器中的区别?

    1. 游览器事件循环引用阮一峰老师的博客:JavaScript 运行机制详解:再谈Event Loop
    2. nodejs 参考其他博主的博客,如: NodeJs 的 Event loop 事件循环机制详解

    8. session如何实现登录?

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    9. 请描述koa2和express的中间件机制

    参考博客: Koa2 和 Express 中间件对比

    10. async和await执行顺序的考察

    参考博客:async 函数的含义和用法

    11. 描述koa2洋葱圈模型

    参考博客:Koa2 中间件简易洋葱圈模型实现

    12. 如何逐行读取一个1G大小的日志文件?

    在这里插入图片描述
    在这里插入图片描述

    13. nodejs线上环境为何要开启多进程?

    1. 高效使用多核CPU
    2. 充分利用服务器内存
    3. 最终: “压榨”服务器,不浪费资源
    4. 可以使用 PM2 进行管理
    展开全文
  • 面试整理|50个NodeJS面试题初级NodeJS面试题中级NodeJS面试题高级NodeJS面试题《Nodejs仿微博网站》 Node.js是一种服务器端脚本工具,使用它可以轻松构建从简单的命令行程序到复杂的企业级 Web 应用程序的所有内容...


    在这里插入图片描述

    Node.js是一种服务器端脚本工具,使用它可以轻松构建从简单的命令行程序到复杂的企业级 Web 应用程序的所有内容。
    本篇文章,将为大家分享50个和NodeJS相关的面试题及其解析,难度分为:初级、中级、高级三个部分,由易到难,循序渐进。

    初级NodeJS面试题

    Q1、如何区分 JavaScript 和 Node.js?

    特征JavaScriptNodeJS
    类型编程语言JavaScript 的解释器和环境
    公用事业用于 Web 应用程序的任何客户端活动用于访问或执行任何操作系统的任何非阻塞操作
    运行引擎蜘蛛猴 (FireFox)、JavaScript Core (Safari)、V8 (Google Chrome) 等V8 (Google Chrome)

    Q2、什么是NodeJS?
    Node.js 是在Chrome 的 V8 JavaScript 引擎上开发的极其强大的框架 ,可将 JavaScript 直接编译为原生机器码。它是一个轻量级框架,用于创建服务器端 Web 应用程序并扩展 JavaScript API 以提供常用的服务器端功能。它通常用于大型应用程序开发,特别是视频流站点、单页应用程序和其他 Web 应用程序。

    Q3、列出NodeJS的优势?

    特征描述
    快速Node.js 建立在 Google Chrome 的 V8 JavaScript 引擎之上,这使得它的库在代码执行方面非常快
    异步基于 Node.js 的服务器从不等待 API 返回数据,从而使其异步
    可扩展它是高度可扩展的,因为它的事件机制可以帮助服务器以非阻塞方式响应
    开源Node.js 拥有一个广泛的开源社区,该社区贡献了一些优秀的模块来为 Node.js 应用程序添加附加功能
    无缓冲Node.js 应用程序只是以块的形式输出数据,从不缓冲任何数据

    Q4、Angular 和 Node.js 有什么区别

    AngularNodeJS
    它是一个开源的Web应用程序开发框架它是应用程序的跨平台运行时环境
    它是用TypeScript编写的它是用C、C++ 和 JavaScript 语言编写的
    用于构建单页面客户端web应用用于构建快速且可扩展的服务器端网络应用程序
    Angular本身就是一个web 应用框架Node.js 有许多不同的框架, 如 Sails.js、Partial.js 和 Express.js 等
    非常适合创建高度活跃和交互式的网络应用程序非常适合开发小型项目
    有助于将应用程序拆分为MVC组件有助于生成数据库查询
    适合开发实时应用适用于需要更快和更具可扩展性的情况

    Q5、NodeJS的单线程模型?

    Node.js 使用单线程模型来支持异步处理。通过异步处理,应用程序可以在 Web 负载下性能更好并且更具可扩展性。因此,Node.js 使用单线程模型方法而不是典型的基于线程的实现。

    Q6、NodeJS是如何工作的?
    Node.js 是一个使用 JavaScript 作为脚本语言并在 v8 环境中运行的虚拟机。它在单线程事件循环和非阻塞 I/O 上提供高速率工作,因为它可以处理更多并发请求。此外,通过使用“HTTP”模块,Node.js 可以在任何独立的 Web 服务器上运行。

    Q7、NodeJS的应用领域?
    1、实时网络应用程序
    2、网络应用
    3、分布式系统
    4、通用应用

    Q8、NodeJS中有多少种API函数?
    有两种类型API函数

    • 异步非阻塞函数
    • 同步、阻塞函数

    Q9、异步和非阻塞有什么区别?

    • 异步
    • 非阻塞

    异步意味着不同步。使用这些我们可以发出不等待服务器响应的异步 HTTP 请求。这些函数继续响应它已经收到服务器响应的请求。 非阻塞函数用于 I/O 操作。他们立即响应任何可用数据,并根据请求继续运行。如果无法检索到任何答案,则 API 会立即返回并显示错误。

    Q10、package.json是什么文件?
    Node.js 中的 package.json文件是整个应用程序的核心。它是包含项目元数据的清单文件,我们在其中定义了包的属性。

    Q11、你对事件驱动编程的理解是什么?
    事件驱动编程是一种大量使用事件来触发各种函数的方法。事件可以是鼠标单击、按键等任何事件。当事件发生时,将执行已向元素注册的回调函数。这种方法主要遵循发布订阅模式。由于事件驱动编程的特点,使得Node.js 与其他技术相比速度更快。

    Q12、NodeJS中事件循环是什么,如何工作?
    Node.js 中的事件循环处理应用程序中的所有异步回调。它是 Node.js 最重要的特征之一,也是 Node.js 具有非阻塞 I/O 的原因。由于 Node.js 是一种事件驱动的语言,你可以轻松地将侦听器附加到事件,然后当事件发生时回调将由特定的侦听器执行。每当调用 setTimeout、http.get 和 fs.readFile 等函数时,Node.js 都会执行事件循环,然后继续执行进一步的代码,而无需等待输出。整个操作完成后,Node.js 接收输出,然后执行回调函数。这就是为什么所有的回调函数都被放置在一个循环的队列中。一旦收到响应,它们就会被一一执行。
    在这里插入图片描述

    Q13、在NodeJS上下文中解释REPL

    REPL在Node.js的代表:R-Read,E-Eval,P-Print,L-Loop。它代表一个计算机环境,例如窗口控制台或 Unix/Linux shell,可以在其中输入任何命令,然后系统可以输出响应。默认情况下,Node.js 与 REPL 环境捆绑在一起。REPL 可以执行以下列出的任务:

    • Read:读取用户的输入,将其解析为 JavaScript 数据结构,然后将其存储在内存中。
    • Eval:接收并评估数据结构。
    • Print:打印最终结果。
    • Loop:循环提供的命令,直到按两次CTRL+C。

    Q14、列出应该使用事件循环异步完成的任务

    1. 输入/输出操作
    2. 繁重的计算
    3. 任何需要阻塞的东西

    Q15、列出使用“控制流”控制 Node.js 中函数调用的步骤

    1. List item
    2. 控制执行顺序
    3. 收集数据
    4. 限制并发
    5. 调用程序的下一步

    中级NodeJS面试题

    Q16、你对测试金字塔的理解是什么?
    测试金字塔基本上是一个图表,它描述了为了项目的成功开发需要编写多少单元测试、集成测试和端到端测试的比例。
    图片

    Q17、Node.js 中的错误优先回调是什么?
    Node.js 中的错误优先回调用于传递错误和数据。你需要传递给这些函数的第一个参数必须是一个错误对象,而其他参数代表相关数据。因此,你可以传递错误对象以检查是否有任何错误并进行处理。如果没有问题,可以继续使用后续参数。

    var myPost = new Post({title: 'edureka'});
    myPost.save(function(err,myInstance){
    if(err){
    //处理错误并返回
    }
    //继续 `myInstance`
    });
    

    Q18、解释module.exports的用途?
    Node.js 中的一个模块用于将所有相关代码封装到一个代码单元中,该代码单元可以通过将所有相关功能转移到单个文件中来进行解释。例如,假设您有一个名为 greet.js 的文件,其中包含如下所示的两个函数:

    module.exports = {
    greetInHindi: function(){
    return "NAMASTE";
    },
    greetInKorean: function(){
    return "ANNYEONGHASEYO";
    }};
    

    如你所见,module.exports 提供了两个函数,可以使用以下代码将它们导入到另一个文件中:

    var eduGreets = require ("./greet.js");
    eduGreets.greetInHindi() //NAMASTE
    eduGreets.greetInKorean() //ANNYEONGHASEYO
    

    Q19、你对Node.js中的Reactor Pattern有什么理解?
    Node.js 中的Reactor Pattern基本上是一个非阻塞 I/O 操作的概念。该模式提供了一个与每个 I/O 操作相关联的处理程序,一旦生成 I/O 请求,它就会提交给多 路分解器。该解复用器是一个通知接口,能够在非阻塞 I/O 模式下处理并发。它还有助于以事件的形式收集每个请求,然后将每个事件放入队列中。从而导致事件队列的产生。同时,我们有事件循环,它迭代事件队列中存在的事件。

    Q20、“前端”和“后端”开发有什么区别?

    前端后端
    使用 HTML、CSS、JavaScript等标记和网络语言使用Python、Ruby、Perl等编程和脚本语言。
    基于异步请求和AJAX基于服务器架构
    更好的可访问性增强的安全性|
    用于搜索引擎优化用于备份

    Q21. Node.js 的 LTS 版本是什么?

    LTS代表Long Term Support版本的Node.js,接收安全更新和性能改进以及所有关键bug修复。主要关注稳定性和安全性。对 LTS 版本所做的修改仅限于错误修复、安全升级、npm 和文档更新、性能改进等。

    Q22. 列出 Node.js 中的主要安全实现?

    • 身份验证
    • 错误处理

    Q23. 你怎么理解回调地狱?

    回调地狱也被称为末日金字塔。这是一种由密集嵌套的回调引起的模式,这些回调不可读且笨拙。它通常包含多个嵌套的回调函数,从而使代码难以阅读和调试。它是由异步逻辑的不当实现引起的。

    async_A(function(){
    async_B(function(){
    async_C(function(){
    async_D(function(){
    ....
    });
    });
    });
    });
    

    Q24. 解释libuv?

    Libuv 是Node.js的多平台支持库,主要用于异步 I/O。它主要是为 Node.js 开发的,随着时间的推移,它被广泛用于其他系统,如 Luvit、pyuv、Julia 等。Libuv 基本上是对依赖于平台的 libev/IOCP 的抽象,为用户提供基于 libev 的 API。libuv 的一些重要特性是:

    • 支持全功能事件循环
    • 文件系统事件
    • 异步文件和文件系统操作
    • 异步 TCP 和 UDP 套接字
    • 子进程

    Q25. 解释NodeJS中间件概念
    一般来说,中间件是一个接收请求和响应对象的函数。换句话说,在应用程序的请求-响应循环中,这些函数可以访问各种请求和响应对象以及循环的下一个函数。中间件的 next 功能是借助一个变量来表示的,通常命名为 next。中间件功能最常执行的任务是:

    • 执行任何类型的代码
    • 更新或修改请求和响应对象
    • 完成请求-响应循环
    • 调用堆栈中的下一个中间件

    Q26.解释URL模块的概念?

    Node.js的 URL 模块提供了各种用于URL 解析和解析的实用程序 。它是一个内置模块,有助于将网址拆分为可读格式:

    var url = require('url');
    

    例如:

    var url = require('url');
    var adrs = 'http://localhost:8082/default.htm?year=2021&month=July';
    var q = url.parse(adr, true);
    console.log(q.host); //returns 'localhost:8082'
    console.log(q.pathname); //returns '/default.htm'
    console.log(q.search); //returns '?year=2021 and month=July'
    var qdata = q.query; //returns an object: { year: 2021, month: 'July' }
    console.log(qdata.month); //returns 'July'
    

    Q27.你对ESLint的理解是什么?

    ESLint 是一个开源项目,最初由 Nicholas C. Zakas 于 2013 年开发,旨在通过插件为 JavaScript 提供 linting 实用程序。Node.js 中的 Linters 是搜索某些 bug 类的好工具,尤其是那些与变量作用域相关的 bug。

    Q28.对于 Node.js,为什么 Google 使用 V8 引擎?

    Google 使用 V8,因为它是一个 Chrome 运行时引擎,可以将JavaScript 代码转换为本地机器代码。这反过来又加快了应用程序的执行和响应过程,并为您提供了一个快速运行的应用程序。

    Q29.解释控制流的工作?
    在 Node.js 中,控制流函数基本上是在异步函数调用之间执行的代码。以下是执行它必须遵循的步骤:
    首先,必须控制执行顺序。
    然后,需要收集所需的数据。
    其次,必须限制并发。
    完成后,必须调用程序的下一步。

    Q30.列出 async.queue 作为输入的两个参数?
    下面是 async.queue 作为输入的两个参数:

    1. 任务功能
    2. 并发值

    Q31.Node.js 中 spawn() 和 fork() 方法的区别?

    在 Node.js 中, spawn() 用于使用提供的命令集启动新进程。此方法不会创建新的 V8 实例,并且只有一个节点模块副本在处理器上处于活动状态。当你的子进程向 Node 返回大量数据时,可以调用此方法。
    语法:

    child_process.spawn(command[, args][, options])
    

    而 Node.js 中的 fork() 是 spawn() 的一个特殊实例,它执行 V8 引擎的一个新实例。这种方法只是意味着多个工作程序在单个 Node 代码库上运行以执行各种任务。
    语法:

    child_process.fork(modulePath[, args][, options])
    

    Q32、Node.js中的全局对象是怎么理解的?
    在 Node.js 中,全局对象本质上是全局的,并且在应用程序的所有模块中都可用。您可以直接在应用程序中使用这些对象,而不必显式包含它们。全局对象可以是模块、函数、字符串、对象等。此外,这些对象中的一些可以在模块范围内而不是在全局范围内。

    Q33、解释Node中存根的概念?
    在 Node.js 中,存根基本上是用于刺激模块或组件行为的程序或函数。在任何测试用例中,存根都会提供函数的预设答案。

    Q34、断言在NodeJS中如何工作?
    在 Node.js 中,断言用于编写测试。它仅在任何正在运行的测试用例失败时才提供反馈。该模块为您提供了一组断言测试,然后用于测试不变量。它基本上由 Node.js 内部使用,但使用 require(‘assert’) 代码,它也可以在其他应用程序中使用。

    Q35、定义测试金字塔的概念。从 HTTP API 的角度解释实现它们的过程
    测试金字塔基本上是由 Mike Cohn 开发的一个概念。根据这一点,你应该有一个更高一些低级别的单元测试相比,高层次的终端到终端的测试,通过GUI运行。
    就 HTTP API 而言,它可以定义为:

    • 每个模型的低级单元测试数量更多
    • 较少的集成测试来测试模型交互
    • 用于测试实际 HTTP 端点的较少验收测试

    Q36、解释一下ExpressJS包的用途?
    Express.js 是一个构建在 Node.js 之上的框架,它有助于管理服务器端应用程序中服务器和路由之间的数据流。它是一个轻量级且灵活的框架,可提供 Web 和移动应用程序开发所需的广泛功能。Express.js开发的中间件的Node.js的模块被称为 连接。connect 模块进一步利用 http 模块与 Node.js 通信。因此,如果您正在使用任何基于连接的中间件模块,那么您可以轻松地与 Express.js 集成。

    Q37、process.nextTick() 和 setImmediate() 的区别?
      在 Node.js 中,process.nextTick() 和 setImmediate() 都是 Timers 模块的函数,它们有助于在预定义的时间段后执行代码。但是这些功能在执行上有所不同。process.nextTick 函数等待动作的执行,直到事件循环中的下一次传递,或者一旦事件循环完成,它就会调用回调函数。另一方面, setImmediate() 用于在事件循环的下一个周期执行回调方法,最终将其返回到事件循环以执行 I/O 操作。

    Q38、解释Node.js中buffer类的用法?
    Node.js 中的 Buffer 类用于以类似于整数数组的方式存储原始数据。但它对应于位于 V8 堆之外的原始内存分配。它是一个易于访问的全局类,无需导入缓冲模块即可在应用程序中访问。使用 Buffer 类是因为纯 JavaScript 与二进制数据不兼容。因此,在处理 TCP 流或文件系统时,有必要处理八位字节流。

    Q39、NodeJS如何处理子线程?
     一般来说,Node.js 是一个单线程进程,不暴露子线程或线程管理方法。但是您仍然可以使用 spawn() 将子线程用于某些特定的异步 I/O 任务,这些任务在后台执行并且通常不执行任何 JS 代码或阻碍应用程序中的主事件循环。如果您仍然想在您的应用程序中使用线程概念,您必须明确地包含一个名为 ChildProcess 的模块。

    Q40、相对,固定,绝对和静态定位的元素有什么区别?
    Node.js 中的流是类似于数组和字符串的数据集合。它们是对象,您可以使用它们以连续方式从源读取数据或将数据写入目标。它可能无法立即使用,也不必放入内存中。这些流对于读取和处理大量数据特别有用。在 Node.js 中,流有四种基本类型:

    1. 可读: 用于从源读取大量数据。
    2. 可写:用于将大块数据写入目的地。
    3. 双工:用于两种功能;读和写。
    4. Transform:它是一种用于修改数据的双工流。

    高级NodeJS面试题

    Q41、NODE_ENV有什么用?
    如果项目处于生产阶段,Node.js 提倡使用 NODE_ENV 变量来标记它的约定。这有助于在项目开发过程中做出更好的判断。此外,当您将 NODE_ENV 设置为生产时,您的应用程序的执行速度往往会快 3 倍。

    Q42、Node.js 中 readFile 和 createReadStream 的区别?
    Node.js 提供了两种读取和执行文件的方式,分别是使用 readFile 和 CreateStream。readFile() 是一个完全缓冲的进程,只有当完整的文件被推入缓冲区并被读取时才返回响应。这是一个内存密集型过程,在大文件的情况下,处理速度可能非常慢。而 createReadStream 是部分缓冲的,它将整个过程视为一个事件系列。整个文件被分成块,然后被处理并作为响应一一发回。完成后,它们最终会从缓冲区中删除。与 readFile 不同,createReadStream 对于大文件的处理非常有效。

    Q43、列出 Node.js 的各种计时功能?
    Node.js 提供了一个 Timers 模块,其中包含在指定时间段后执行代码的各种功能。下面我列出了这个模块提供的各种功能:

    1. setTimeout/clearTimeout – 用于在指定的毫秒数后安排代码执行
    2. setInterval/clearInterval – 用于多次执行一个代码块
    3. setImmediate/clearImmediate – 用于在当前事件循环周期结束时执行代码
    4. process.nextTick – 用于调度需要在事件循环的下一次迭代中调用的回调函数

    Q44、解释Node.js中Punycode的概念
    在 Node.js 中,Punycode 是一种编码语法,用于将 Unicode (UTF-8) 字符串转换为基本的 ASCII 字符串。这很重要,因为主机名只能理解 ASCII 字符。因此,从 Node.js 0.6.2 版本开始,它与默认的 Node 包捆绑在一起。如果您想将它与任何以前的版本一起使用,您可以使用以下代码轻松实现:
    语法:

    punycode = require('punycode');
    

    Q45、Node.js 和 Ajax 的区别?
    Node.js 和 Ajax 之间最基本的区别在于,Node.js 是服务器端 JavaScript,而 Ajax 是客户端技术。简单来说,Ajax 主要用于更新或修改网页内容,而无需刷新它。另一方面,Node.js 需要开发通常由服务器而不是 Web 浏览器执行的服务器软件。

    Q46、Node.js 是否提供任何调试器?
    Node.js 确实提供了一个简单的基于 TCP 的协议和内置的调试客户端。为了调试您的 JavaScript 文件,您可以使用下面的调试参数,后跟要调试的js文件名。
    语法:

    node debug [script.js | -e "script" | <host> : <port> ]
    

    Q47、描述 Node.js 的退出代码?
    在 Node.js 中,退出代码是一组用于完成特定进程的特定代码。这些进程也可以包括全局对象。下面是 Node.js 中使用的一些退出代码:

    • 未捕获的致命异常
    • 没用过
    • 致命错误
    • 内部异常处理程序运行时失败
    • 内部 JavaScript 评估失败

    Q48、Node.js 中的 Event Emitter 是怎么理解的?
    EventEmitter 是一个 Node.js 类,包含所有能够发出事件的对象。这些对象包含一个 eventEmitter.on() 函数,通过该函数可以将多个函数附加到对象发出的命名事件。每当 EventEmitter 对象抛出一个事件时,所有附加到该特定事件的函数都会被同步调用。下面的代码显示了如何在您的应用程序中使用 EventEmitter:

    const EventEmitter = require('events');
    class MyEmitter extends EventEmitter { }
    const myEmitter = new MyEmitter();
    myEmitter.on('event', () => {
    console.log('an event occurred!');
    });
    myEmitter.emit('event');
    

    Q49、解释为什么 Express ‘app’ 和 ‘server’ 必须分开的原因?
    Express ‘app’ 和 ‘server’ 必须保持分开,因为这样做,您将 API 声明与网络相关配置分开,这有利于以下列出的方式:
    它允许在进程内测试 API,而无需执行网络调用
    更快的测试执行
    获取更广泛的代码覆盖指标
    允许在灵活的不同网络条件下部署相同的 API
    更好地分离关注点和更清晰的代码
    API 声明应驻留在 app.js 中:

    var app = express();
    app.use(bodyParser.json());
    app.use("/api/events", events.API);
    app.use("/api/forms", forms);
    

    服务器网络声明应位于 /bin/www 中:

    var app = require('../app');
    var http = require('http');
    //Get port from environment and store in Express
    var port = normalizePort(process.env.PORT || '8000');
    app.set('port', port);
    //Create HTTP server.
    var server = http.createServer(app);
    

    Q50、Node.js 支持密码学吗?
    是的,Node.js 确实通过名为 Crypto 的模块支持加密。该模块提供各种加密功能,如密码、解密、签名和验证功能,一组用于开放 SSL 的哈希 HMAC 的包装器等。例如:
    语法:

    const crypto = require'crypto');
    const secret = 'akerude';
    const hash = crypto.createHmac('swaEdu', secret).update('Welcome to Edureka').digest('hex');
    console.log(hash);
    

    关于NodeJS面试题就先简单给大家分享到这里!
    掌握了这些面试题型后,还需要结合真实项目才能够融会贯通 !
    下面课程将带你进入真实项目的应用,带大家掌握NodeJS!

    《Nodejs仿微博网站》

    在这里插入图片描述
    在这里插入图片描述

    前端学习扫码咨询
    在这里插入图片描述

    展开全文
  • Nodejs面试题

    2021-02-18 11:00:43
    1.2 请介绍一下require的模块加载机制 这道基本上就可以了解到面试者对Node模块机制的了解程度基本上面试提到 1、先计算模块路径 2、如果模块在缓存里面,取出缓存 3、加载模块 4、的输出模块的exports属性即可 1...

    1、Node模块机制

    1.1 请介绍一下node里的模块是什么

    Node中,每个文件模块都是一个对象,它的定义如下:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    function Module(id, parent) {

     this.id = id;

     this.exports = {};

     this.parent = parent;

     this.filename = null;

     this.loaded = false;

     this.children = [];

    }

     

    module.exports = Module;

     

    var module = new Module(filename, parent);

    所有的模块都是 Module 的实例。可以看到,当前模块(module.js)也是 Module 的一个实例。

    1.2 请介绍一下require的模块加载机制

    这道题基本上就可以了解到面试者对Node模块机制的了解程度基本上面试提到

    1、先计算模块路径

    2、如果模块在缓存里面,取出缓存

    3、加载模块

    4、的输出模块的exports属性即可

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    // require 其实内部调用 Module._load 方法

    Module._load = function(request, parent, isMain) {

     // 计算绝对路径

     var filename = Module._resolveFilename(request, parent);

     

     // 第一步:如果有缓存,取出缓存

     var cachedModule = Module._cache[filename];

     if (cachedModule) {

     return cachedModule.exports;

     

     // 第二步:是否为内置模块

     if (NativeModule.exists(filename)) {

     return NativeModule.require(filename);

     }

      

     /********************************这里注意了**************************/

     // 第三步:生成模块实例,存入缓存

     // 这里的Module就是我们上面的1.1定义的Module

     var module = new Module(filename, parent);

     Module._cache[filename] = module;

     

     /********************************这里注意了**************************/

     // 第四步:加载模块

     // 下面的module.load实际上是Module原型上有一个方法叫Module.prototype.load

     try {

     module.load(filename);

     hadException = false;

     } finally {

     if (hadException) {

      delete Module._cache[filename];

     }

     }

     

     // 第五步:输出模块的exports属性

     return module.exports;

    };

    接着上一题继续发问

    1.3 加载模块时,为什么每个模块都有__dirname,__filename属性呢,new Module的时候我们看到1.1部分没有这两个属性的,那么这两个属性是从哪里来的

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    // 上面(1.2部分)的第四步module.load(filename)

    // 这一步,module模块相当于被包装了,包装形式如下

    // 加载js模块,相当于下面的代码(加载node模块和json模块逻辑不一样)

    (function (exports, require, module, __filename, __dirname) {

     // 模块源码

     // 假如模块代码如下

     var math = require('math');

     exports.area = function(radius){

      return Math.PI * radius * radius

     }

    });

    也就是说,每个module里面都会传入__filename, __dirname参数,这两个参数并不是module本身就有的,是外界传入的

    1.4 我们知道node导出模块有两种方式,一种是exports.xxx=xxx和Module.exports={}有什么区别吗

    • exports其实就是module.exports
    • 其实1.3问题的代码已经说明问题了,接着我引用廖雪峰大神的讲解,希望能讲的更清楚

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

    38

    39

    40

    41

    42

    43

    44

    45

    46

    47

    48

    49

    50

    51

    52

    53

    54

    55

    56

    57

    58

    59

    60

    61

    62

    63

    64

    65

    66

    67

    68

    69

    70

    71

    72

    73

    74

    75

    76

    77

    78

    79

    80

    81

    82

    83

    84

    85

    86

    87

    88

    89

    90

    91

    module.exports vs exports

    很多时候,你会看到,在Node环境中,有两种方法可以在一个模块中输出变量:

     

    方法一:对module.exports赋值:

     

    // hello.js

     

    function hello() {

     console.log('Hello, world!');

    }

     

    function greet(name) {

     console.log('Hello, ' + name + '!');

    }

     

    module.exports = {

     hello: hello,

     greet: greet

    };

    方法二:直接使用exports:

     

    // hello.js

     

    function hello() {

     console.log('Hello, world!');

    }

     

    function greet(name) {

     console.log('Hello, ' + name + '!');

    }

     

    function hello() {

     console.log('Hello, world!');

    }

     

    exports.hello = hello;

    exports.greet = greet;

    但是你不可以直接对exports赋值:

     

    // 代码可以执行,但是模块并没有输出任何变量:

    exports = {

     hello: hello,

     greet: greet

    };

    如果你对上面的写法感到十分困惑,不要着急,我们来分析Node的加载机制:

     

    首先,Node会把整个待加载的hello.js文件放入一个包装函数load中执行。在执行这个load()函数前,Node准备好了module变量:

     

    var module = {

     id: 'hello',

     exports: {}

    };

    load()函数最终返回module.exports:

     

    var load = function (exports, module) {

     // hello.js的文件内容

     ...

     // load函数返回:

     return module.exports;

    };

     

    var exported = load(module.exports, module);

    也就是说,默认情况下,Node准备的exports变量和module.exports变量实际上是同一个变量,并且初始化为空对象{},于是,我们可以写:

     

    exports.foo = function () { return 'foo'; };

    exports.bar = function () { return 'bar'; };

    也可以写:

     

    module.exports.foo = function () { return 'foo'; };

    module.exports.bar = function () { return 'bar'; };

    换句话说,Node默认给你准备了一个空对象{},这样你可以直接往里面加东西。

     

    但是,如果我们要输出的是一个函数或数组,那么,只能给module.exports赋值:

     

    module.exports = function () { return 'foo'; };

    给exports赋值是无效的,因为赋值后,module.exports仍然是空对象{}。

     

    结论

    如果要输出一个键值对象{},可以利用exports这个已存在的空对象{},并继续在上面添加新的键值;

     

    如果要输出一个函数或数组,必须直接对module.exports对象赋值。

     

    所以我们可以得出结论:直接对module.exports赋值,可以应对任何情况:

     

    module.exports = {

     foo: function () { return 'foo'; }

    };

    或者:

     

    module.exports = function () { return 'foo'; };

    最终,我们强烈建议使用module.exports = xxx的方式来输出模块变量,这样,你只需要记忆一种方法。

    2、Node的异步I/O

    本章的答题思路大多借鉴于朴灵大神的《深入浅出的NodeJS》

    2.1 请介绍一下Node事件循环的流程

    • 在进程启动时,Node便会创建一个类似于while(true)的循环,每执行一次循环体的过程我们成为Tick。
    • 每个Tick的过程就是查看是否有事件待处理。如果有就取出事件及其相关的回调函数。然后进入下一个循环,如果不再有事件处理,就退出进程。

    2.2 在每个tick的过程中,如何判断是否有事件需要处理呢?

    1. 每个事件循环中有一个或者多个观察者,而判断是否有事件需要处理的过程就是向这些观察者询问是否有要处理的事件。
    2. 在Node中,事件主要来源于网络请求、文件的I/O等,这些事件对应的观察者有文件I/O观察者,网络I/O的观察者。
    3. 事件循环是一个典型的生产者/消费者模型。异步I/O,网络请求等则是事件的生产者,源源不断为Node提供不同类型的事件,这些事件被传递到对应的观察者那里,事件循环则从观察者那里取出事件并处理。
    4. 在windows下,这个循环基于IOCP创建,在*nix下则基于多线程创建

    2.3 请描述一下整个异步I/O的流程

    3、V8的垃圾回收机制

    3.1 如何查看V8的内存使用情况

    使用process.memoryUsage(),返回如下

    1

    2

    3

    4

    5

    6

    {

     rss: 4935680,

     heapTotal: 1826816,

     heapUsed: 650472,

     external: 49879

    }

    heapTotal和heapUsed代表V8的内存使用情况。external代表V8管理的,绑定到Javascript的C++对象的内存使用情况。rss, 驻留集大小, 是给这个进程分配了多少物理内存(占总分配内存的一部分) 这些物理内存中包含堆,栈,和代码段。

    3.2 V8的内存限制是多少,为什么V8这样设计

    64位系统下是1.4GB, 32位系统下是0.7GB。因为1.5GB的垃圾回收堆内存,V8需要花费50毫秒以上,做一次非增量式的垃圾回收甚至要1秒以上。这是垃圾回收中引起Javascript线程暂停执行的事件,在这样的花销下,应用的性能和影响力都会直线下降。

    3.3 V8的内存分代和回收算法请简单讲一讲

    在V8中,主要将内存分为新生代和老生代两代。新生代中的对象存活时间较短的对象,老生代中的对象存活时间较长,或常驻内存的对象。

    3.3.1 新生代

    新生代中的对象主要通过Scavenge算法进行垃圾回收。这是一种采用复制的方式实现的垃圾回收算法。它将堆内存一份为二,每一部分空间成为semispace。在这两个semispace空间中,只有一个处于使用中,另一个处于闲置状态。处于使用状态的semispace空间称为From空间,处于闲置状态的空间称为To空间。

    • 当开始垃圾回收的时候,会检查From空间中的存活对象,这些存活对象将被复制到To空间中,而非存活对象占用的空间将会被释放。完成复制后,From空间和To空间发生角色对换。
    • 应为新生代中对象的生命周期比较短,就比较适合这个算法。
    • 当一个对象经过多次复制依然存活,它将会被认为是生命周期较长的对象。这种新生代中生命周期较长的对象随后会被移到老生代中。

    3.3.2 老生代

    老生代主要采取的是标记清除的垃圾回收算法。与Scavenge复制活着的对象不同,标记清除算法在标记阶段遍历堆中的所有对象,并标记活着的对象,只清理死亡对象。活对象在新生代中只占叫小部分,死对象在老生代中只占较小部分,这是为什么采用标记清除算法的原因。

    3.3.3 标记清楚算法的问题

    主要问题是每一次进行标记清除回收后,内存空间会出现不连续的状态

    • 这种内存碎片会对后续内存分配造成问题,很可能出现需要分配一个大对象的情况,这时所有的碎片空间都无法完成此次分配,就会提前触发垃圾回收,而这次回收是不必要的。
    • 为了解决碎片问题,标记整理被提出来。就是在对象被标记死亡后,在整理的过程中,将活着的对象往一端移动,移动完成后,直接清理掉边界外的内存。

    3.3.4 哪些情况会造成V8无法立即回收内存

    闭包和全局变量

    3.3.5 请谈一下内存泄漏是什么,以及常见内存泄漏的原因,和排查的方法

    什么是内存泄漏

    • 内存泄漏(Memory Leak)指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况。
    • 如果内存泄漏的位置比较关键,那么随着处理的进行可能持有越来越多的无用内存,这些无用的内存变多会引起服务器响应速度变慢。
    • 严重的情况下导致内存达到某个极限(可能是进程的上限,如 v8 的上限;也可能是系统可提供的内存上限)会使得应用程序崩溃。常见内存泄漏的原因内存泄漏的几种情况:

    一、全局变量

    1

    2

    3

    4

    5

    a = 10;

    //未声明对象。

    global.b = 11;

    //全局变量引用

    这种比较简单的原因,全局变量直接挂在 root 对象上,不会被清除掉。

    二、闭包

    1

    2

    3

    4

    5

    6

    function out() {

     const bigData = new Buffer(100);

     inner = function () {

       

     }

    }

    闭包会引用到父级函数中的变量,如果闭包未释放,就会导致内存泄漏。上面例子是 inner 直接挂在了 root 上,那么每次执行 out 函数所产生的 bigData 都不会释放,从而导致内存泄漏。

    需要注意的是,这里举得例子只是简单的将引用挂在全局对象上,实际的业务情况可能是挂在某个可以从 root 追溯到的对象上导致的。

    三、事件监听

    Node.js 的事件监听也可能出现的内存泄漏。例如对同一个事件重复监听,忘记移除(removeListener),将造成内存泄漏。这种情况很容易在复用对象上添加事件时出现,所以事件重复监听可能收到如下警告:

    1

    emitter.setMaxListeners() to increase limit

    例如,Node.js 中 Agent 的 keepAlive 为 true 时,可能造成的内存泄漏。当 Agent keepAlive 为 true 的时候,将会复用之前使用过的 socket,如果在 socket 上添加事件监听,忘记清除的话,因为 socket 的复用,将导致事件重复监听从而产生内存泄漏。

    原理上与前一个添加事件监听的时候忘了清除是一样的。在使用 Node.js 的 http 模块时,不通过 keepAlive 复用是没有问题的,复用了以后就会可能产生内存泄漏。所以,你需要了解添加事件监听的对象的生命周期,并注意自行移除。

    排查方法

    • 想要定位内存泄漏,通常会有两种情况:
    • 对于只要正常使用就可以重现的内存泄漏,这是很简单的情况只要在测试环境模拟就可以排查了。
    • 对于偶然的内存泄漏,一般会与特殊的输入有关系。想稳定重现这种输入是很耗时的过程。如果不能通过代码的日志定位到这个特殊的输入,那么推荐去生产环境打印内存快照了。
    • 需要注意的是,打印内存快照是很耗 CPU 的操作,可能会对线上业务造成影响。快照工具推荐使用 heapdump 用来保存内存快照,使用 devtool 来查看内存快照。
    • 使用 heapdump 保存内存快照时,只会有 Node.js 环境中的对象,不会受到干扰(如果使用 node-inspector 的话,快照中会有前端的变量干扰)。
    • PS:安装 heapdump 在某些 Node.js 版本上可能出错,建议使用 npm install heapdump -target=Node.js 版本来安装。

    4、Buffer模块

    4.1 新建Buffer会占用V8分配的内存吗

    不会,Buffer属于堆外内存,不是V8分配的。

    4.2 Buffer.alloc和Buffer.allocUnsafe的区别

    Buffer.allocUnsafe创建的 Buffer 实例的底层内存是未初始化的。 新创建的 Buffer 的内容是未知的,可能包含敏感数据。 使用 Buffer.alloc() 可以创建以零初始化的 Buffer 实例。

    4.3 Buffer的内存分配机制

    为了高效的使用申请来的内存,Node采用了slab分配机制。slab是一种动态的内存管理机制。Node以8kb为界限来来区分Buffer为大对象还是小对象,如果是小于8kb就是小Buffer,大于8kb就是大Buffer。

    例如第一次分配一个1024字节的Buffer,Buffer.alloc(1024),那么这次分配就会用到一个slab,接着如果继续Buffer.alloc(1024),那么上一次用的slab的空间还没有用完,因为总共是8kb,1024+1024 = 2048个字节,没有8kb,所以就继续用这个slab给Buffer分配空间。

    如果超过8bk,那么直接用C++底层地宫的SlowBuffer来给Buffer对象提供空间。

    4.4 Buffer乱码问题

    例如一个份文件test.md里的内容如下:

    床前明月光,疑是地上霜,举头望明月,低头思故乡

    我们这样读取就会出现乱码:

    1

    2

    var rs = require('fs').createReadStream('test.md', {highWaterMark: 11});

    // 床前明???光,疑???地上霜,举头???明月,???头思故乡

    一般情况下,只需要设置rs.setEncoding('utf8')即可解决乱码问题

    5、webSocket

    5.1 webSocket与传统的http有什么优势

    • 客户端与服务器只需要一个TCP连接,比http长轮询使用更少的连接
    • webSocket服务端可以推送数据到客户端
    • 更轻量的协议头,减少数据传输量

    5.2 webSocket协议升级时什么,能简述一下吗?

    首先,WebSocket连接必须由浏览器发起,因为请求协议是一个标准的HTTP请求,格式如下:

    1

    2

    3

    4

    5

    6

    7

    GET ws://localhost:3000/ws/chat HTTP/1.1

    Host: localhost

    Upgrade: websocket

    Connection: Upgrade

    Origin: http://localhost:3000

    Sec-WebSocket-Key: client-random-string

    Sec-WebSocket-Version: 13

    该请求和普通的HTTP请求有几点不同:

    • GET请求的地址不是类似/path/,而是以ws://开头的地址;
    • 请求头Upgrade: websocket和Connection: Upgrade表示这个连接将要被转换为WebSocket连接;
    • Sec-WebSocket-Key是用于标识这个连接,并非用于加密数据;
    • Sec-WebSocket-Version指定了WebSocket的协议版本。

    随后,服务器如果接受该请求,就会返回如下响应:

    1

    2

    3

    4

    HTTP/1.1 101 Switching Protocols

    Upgrade: websocket

    Connection: Upgrade

    Sec-WebSocket-Accept: server-random-string

    该响应代码101表示本次连接的HTTP协议即将被更改,更改后的协议就是Upgrade: websocket指定的WebSocket协议。

    6、https

    6.1 https用哪些端口进行通信,这些端口分别有什么用

    • 443端口用来验证服务器端和客户端的身份,比如验证证书的合法性
    • 80端口用来传输数据(在验证身份合法的情况下,用来数据传输)

    6.2 身份验证过程中会涉及到密钥, 对称加密,非对称加密,摘要的概念,请解释一下

    • 密钥:密钥是一种参数,它是在明文转换为密文或将密文转换为明文的算法中输入的参数。密钥分为对称密钥与非对称密钥,分别应用在对称加密和非对称加密上。
    • 对称加密:对称加密又叫做私钥加密,即信息的发送方和接收方使用同一个密钥去加密和解密数据。对称加密的特点是算法公开、加密和解密速度快,适合于对大数据量进行加密,常见的对称加密算法有DES、3DES、TDEA、Blowfish、RC5和IDEA。
    • 非对称加密:非对称加密也叫做公钥加密。非对称加密与对称加密相比,其安全性更好。对称加密的通信双方使用相同的密钥,如果一方的密钥遭泄露,那么整个通信就会被破解。而非对称加密使用一对密钥,即公钥和私钥,且二者成对出现。私钥被自己保存,不能对外泄露。公钥指的是公共的密钥,任何人都可以获得该密钥。用公钥或私钥中的任何一个进行加密,用另一个进行解密。
    • 摘要: 摘要算法又称哈希/散列算法。它通过一个函数,把任意长度的数据转换为一个长度固定的数据串(通常用16进制的字符串表示)。算法不可逆。

    6.3 为什么需要CA机构对证书签名

    如果不签名会存在中间人攻击的风险,签名之后保证了证书里的信息,比如公钥、服务器信息、企业信息等不被篡改,能够验证客户端和服务器端的“合法性”。

    6.4 https验证身份也就是TSL/SSL身份验证的过程

    简要图解如下

    7、进程通信

    7.1 请简述一下node的多进程架构

    面对node单线程对多核CPU使用不足的情况,Node提供了child_process模块,来实现进程的复制,node的多进程架构是主从模式,如下所示:

    1

    2

    3

    4

    5

    var fork = require('child_process').fork;

    var cpus = require('os').cpus();

    for(var i = 0; i < cpus.length; i++){

     fork('./worker.js');

    }

    在linux中,我们通过ps aux | grep worker.js查看进程

    这就是著名的主从模式,Master-Worker

    7.2 请问创建子进程的方法有哪些,简单说一下它们的区别

    创建子进程的方法大致有:

    • spawn(): 启动一个子进程来执行命令
    • exec(): 启动一个子进程来执行命令,与spawn()不同的是其接口不同,它有一个回调函数获知子进程的状况
    • execFlie(): 启动一个子进程来执行可执行文件
    • fork(): 与spawn()类似,不同电在于它创建Node子进程需要执行js文件
    • spawn()与exec()、execFile()不同的是,后两者创建时可以指定timeout属性设置超时时间,一旦创建的进程超过设定的时间就会被杀死
    • exec()与execFile()不同的是,exec()适合执行已有命令,execFile()适合执行文件。

    7.3 请问你知道spawn在创建子进程的时候,第三个参数有一个stdio选项吗,这个选项的作用是什么,默认的值是什么。

    • 选项用于配置在父进程和子进程之间建立的管道。
    • 默认情况下,子进程的 stdin、 stdout 和 stderr 会被重定向到 ChildProcess 对象上相应的 subprocess.stdin、subprocess.stdout 和 subprocess.stderr 流。
    • 这相当于将 options.stdio 设置为 ['pipe', 'pipe', 'pipe']。

    7.4 请问实现一个node子进程被杀死,然后自动重启代码的思路

    在创建子进程的时候就让子进程监听exit事件,如果被杀死就重新fork一下

    1

    2

    3

    4

    5

    6

    7

    8

    var createWorker = function(){

     var worker = fork(__dirname + 'worker.js')

     worker.on('exit', function(){

      console.log('Worker' + worker.pid + 'exited');

      // 如果退出就创建新的worker

      createWorker()

     })

    }

    7.5 在7.4的基础上,实现限量重启,比如我最多让其在1分钟内重启5次,超过了就报警给运维

    • 思路大概是在创建worker的时候,就判断创建的这个worker是否在1分钟内重启次数超过5次
    • 所以每一次创建worker的时候都要记录这个worker 创建时间,放入一个数组队列里面,每次创建worker都去取队列里前5条记录
    • 如果这5条记录的时间间隔小于1分钟,就说明到了报警的时候了

    7.6 如何实现进程间的状态共享,或者数据共享

    我自己没用过Kafka这类消息队列工具,问了java,可以用类似工具来实现进程间通信,更好的方法欢迎留言

    8、中间件

    8.1 如果使用过koa、egg这两个Node框架,请简述其中的中间件原理,最好用代码表示一下

    上面是在网上找的一个示意图,就是说中间件执行就像洋葱一样,最早use的中间件,就放在最外层。处理顺序从左到右,左边接收一个request,右边输出返回response

    一般的中间件都会执行两次,调用next之前为第一次,调用next时把控制传递给下游的下一个中间件。当下游不再有中间件或者没有执行next函数时,就将依次恢复上游中间件的行为,让上游中间件执行next之后的代码

    例如下面这段代码

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    const Koa = require('koa')

    const app = new Koa()

    app.use((ctx, next) => {

     console.log(1)

     next()

     console.log(3)

    })

    app.use((ctx) => {

     console.log(2)

    })

    app.listen(3001)

    执行结果是1=>2=>3

    koa中间件实现源码大致思路如下:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

    38

    39

    40

    41

    // 注意其中的compose函数,这个函数是实现中间件洋葱模型的关键

    // 场景模拟

    // 异步 promise 模拟

    const delay = async () => {

     return new Promise((resolve, reject) => {

     setTimeout(() => {

      resolve();

     }, 2000);

     });

    }

    // 中间间模拟

    const fn1 = async (ctx, next) => {

     console.log(1);

     await next();

     console.log(2);

    }

    const fn2 = async (ctx, next) => {

     console.log(3);

     await delay();

     await next();

     console.log(4);

    }

    const fn3 = async (ctx, next) => {

     console.log(5);

    }

     

    const middlewares = [fn1, fn2, fn3];

     

    // compose 实现洋葱模型

    const compose = (middlewares, ctx) => {

     const dispatch = (i) => {

     let fn = middlewares[i];

     if(!fn){ return Promise.resolve() }

     return Promise.resolve(fn(ctx, () => {

      return dispatch(i+1);

     }));

     }

     return dispatch(0);

    }

     

    compose(middlewares, 1);

    9、其它

    现在在重新过一遍node 12版本的主要API,有很多新发现,比如说

    • fs.watch这个模块,事件的回调函数有一个参数是触发的事件名称,但是呢,无论我增删改,都是触发rename事件(如果更改是update事件,删除delete事件,重命名是rename事件,这样语义明晰该多好)。后来网上找到一个node-watch模块,此模块增删改都有对应的事件, 并且还高效的支持递归watch 文件。
    • util模块有个promisify方法,可以让一个遵循异常优先的回调风格的函数,即 (err, value) => ... 回调函数是最后一个参数,返回一个返回值是一个 promise 版本的函数。

    1

    2

    3

    4

    5

    6

    7

    8

    9

    const util = require('util');

    const fs = require('fs');

     

    const stat = util.promisify(fs.stat);

    stat('.').then((stats) => {

     // 处理 `stats`。

    }).catch((error) => {

     // 处理错误。

    });

    9.1 杂想

    • crypto模块,可以考察基础的加密学知识,比如摘要算法有哪些(md5, sha1, sha256,加盐的md5,sha256等等),接着可以问如何用md5自己模拟一个加盐的md5算法, 接着可以问加密算法(crypto.createCiphe)中的aes,eds算法的区别,分组加密模式有哪些(比如ECB,CBC,为什么ECB不推荐),node里的分组加密模式是哪种(CMM),这些加密算法里的填充和向量是什么意思,接着可以问数字签名和https的流程(为什么需要CA,为什么要对称加密来加密公钥等等)
    • tcp/ip,可以问很多基础问题,比如链路层通过什么协议根据IP地址获取物理地址(arp),网关是什么,ip里的ICMP协议有什么用,tcp的三次握手,四次分手的过程是什么,tcp如何控制重发,网络堵塞TCP会怎么办等等,udp和tcp的区别,udp里的广播和组播是什么,组播在node里通过什么模块实现。
    • os,操作系统相关基础,io的流程是什么(从硬盘里读取数据到内核的内存中,然后内核的内存将数据传入到调用io的应用程序的进程内存中),冯诺依曼体系是什么,进程和线程的区别等等(我最近在看马哥linux教程,因为自己不是科班出身,听了很多基础的计算机知识,受益匪浅,建议去bilibili看)
    • linux相关操作知识(node涉及到后台,虽然是做中台,不涉及数据库,但是基本的linux操作还是要会的)
    • node性能监控(自己也正在学习中)
    • 测试,因为用的egg框架,有很完善的学习单元测试的文档,省略这部分
    • 数据库可以问一些比如事务的等级有哪些,mysql默认的事务等级是什么,会产生什么问题,然后考一些mysql查询的笔试题。。。和常用优化技巧,node的mysql的orm工具使用过没有。。。(比如我自己是看的尚硅谷mysql初级+高级视频,书是看的mysql必知必会,我自己出于爱好学习一下。。。没有实战过)

     

     

     

    10道有难度的NodeJS面试题(附答案)

    06-30 17:14830浏览

    举报 T字号

    面试虽然不是传统意义上学生时代的考试,但也是毕业之后进入社会上的必经步骤,也算是一种考试了。既然是考试,就一定会分出三六九等,根据应聘的岗位不同,面试的难度也不相同。对于java的面试准备来说,少不了的一项就是面试题的收集整理。如果应聘的岗位是初级java程序员的话,面试题就会偏简单基础一些,而对于高级java工程师和java架构师来说,面试题的难度肯定加大了。所以今天为应聘岗位高的朋友准备了10道Nodejs面试题,希望能够帮助到有需要的朋友们。

    1、用Nodejs的优点是什么?有哪些缺点?

    答:优点:(1)事件驱动,通过闭包很容易实现客户端的生命活期;(2)不用担心多线程,锁,并行计算的问题;(3)V8引擎速度非常快;(4)对于游戏来说,写一遍游戏逻辑代码,前端后端通用。

    缺点:(1)Nodejs更新很快,可能会出现版本兼容;(2)Nodejs还不算成熟,还没有大制作;(3)Nodejs不像其他的服务器,对于不同的链接,不支持进程和线程操作。

    2、为什么Nodejs是单线程的?

    答:Nodejs使用的是单线程没错,但是通过异步处理的方式,可以处理大量的数据吞吐量,从而有更好的性能和扩可扩展性。

    3、Nodejs和ajax的区别是什么?

    答:Nodejs和ajax也就是asynchronous JavaScript and xml,都是通过JavaScript来表现的,但是他们的目的截然不同。Ajax是设计用来动态的更新页面的某个区域,从而不需要更新整个页面。Nodejs是用来开发客户服务器类型应用的。

    4、 Nodejs是如何支持多处理器平台的?

    答:Cluster模块是用来支持这方面的。它可以允许多个nodejs工作进程运行在相同的端口上。

    5、如何避免回调地狱?

    答:(1)模块化:将回调函数转换为独立的函数;(2)使用流程控制库,例如[aync];(3)使用Promise;(4)使用aync/await。

    6、什么是Promise?

    答:Promise可以帮助我们更好地处理异步操作。下面的实例中,100ms后会打印result字符串。catch用于错误处理。多个Promise可以链接起来。

    new Promise((resolve, reject) =>
          {
              setTimeout(() =>
              {
                  resolve('result');
              }, 100)
          })
          .then(console.log)
          .catch(console.error);

    7、退出代码是什么?有哪些退出代码?

    答:退出代码是指中断nodejs运行时返回的代码。有这么几种unused, uncaught fatal exception, fatal error, non function internal exception handler, internal exception handler run time failure,internal JavaScript evaluation failure。

    8、解释Node.js Web应用程序架构?

    答:Web应用程序区分为4层:客户端层:客户端层包含可以向Web服务器发出HTTP请求的Web浏览器,移动浏览器或应用程序。服务器层:服务器层包含Web服务器,它可以拦截客户端发出的请求并将响应传递给它们。业务层:业务层包含Web服务器用于执行所需处理的应用程序服务器。 该层通过数据库或一些外部程序与数据层交互。数据层:数据层包含数据库或任何数据源。

    9、介绍一下Node事件循环的流程?

    答:在进程启动时,Node便会创建一个类似于while(true)的循环,每执行一次循环体的过程我们成为Tick。

    每个Tick的过程就是查看是否有事件待处理。如果有就取出事件及其相关的回调函数。然后进入下一个循环,如果不再有事件处理,就退出进程。

    10、 在每个tick的过程中,如何判断是否有事件需要处理呢?

    答:(1)每个事件循环中有一个或者多个观察者,而判断是否有事件需要处理的过程就是向这些观察者询问是否有要处理的事件。

    (2)在Node中,事件主要来源于网络请求、文件的I/O等,这些事件对应的观察者有文件I/O观察者,网络I/O的观察者。

    (3)事件循环是一个典型的生产者/消费者模型。异步I/O,网络请求等则是事件的生产者,源源不断为Node提供不同类型的事件,这些事件被传递到对应的观察者那里,事件循环则从观察者那里取出事件并处理。

    (4)在windows下,这个循环基于IOCP创建,在*nix下则基于多线程创建。

    上面就是今天为大家整理的NodeJS面试题,一共有10道,数量上不多,但是每一道题都是重点,希望大家都可以学会这10道NodeJS面试题的题目,会在Java面试中用到的。最后想系统的学习NodeJS的话,可以观看“带你快速掌握NodeJS”这门课程,在这个学习java的教程里,可以一步一步地让大家快速地掌握NodeJS这样一个前端核心框架,以适应公司的开发需要。

     

     

     

     

     

     

    nodejs面试总结

    置顶 蛋蛋wqt 2019-03-26 10:38:48  7760  收藏 23

    版权

    一:Node 好处: 处理高并发 事件驱动 轻量  要用于搭建高性能的web服务器,

        1. 它是一个Javascript运行环境

      2. 依赖于Chrome V8引擎进行代码解释

      3. 事件驱动

      4. 非阻塞I/O

      5. 轻量、可伸缩,适于实时数据交互应用

      6. 单进程,单线程

     

    二:Express 和 koa的区别?     

    异步 摆脱回调地域

    对response 和request进行了封装 content

    Express主要基于Connect中间件框架,功能丰富,随取随用,并且框架自身封装了大量便利的功能,比如路由、视图处理等等。而koa主要基于co中间件框架,框架自身并没集成太多功能,大部分功能需要用户自行require中间件去解决,但是由于其基于ES6 generator特性的中间件机制,解决了长期诟病的“callback hell”和麻烦的错误处理的问题,大受开发者欢迎。

    三:事件驱动模型和事件循环:

    事件驱动模型:当服务端收到请求时,就把它关闭 然后处理下一个请求 当第一个请求处理完毕后 就放回处理队列 当达到队列开头 将结果返回给用户  好处:高效 扩展性强 因为服务端一直接受请求 不等待任何读写操作

    事件循环:查看队列里面是否有队列里面有待处理的 如果有 交给主线程执行

    四:Redis:

    使用场景:支持string、list、set、zset和hash类型数据。

    1. 配合关系型数据库做高速缓存
    • 缓存高频次访问的数据,降低数据库io
    • 分布式架构,做session共享
    1. 可以持久化特定数据。
    • 利用zset类型可以存储排行榜
    • 利用list的自然时间排序存储最新n个数据

    五:mysql 和mongodb的区别 

    mysql 关系型数据库 mongodb是非关系数据库(主要)

    六:MySQL索引
    七:闭包应该注意的地方
    八:进程和线程

    进程是操作系统资源分配的基本单位,而线程是任务调度和执行的基本单位     

    进程是线程的容器

    十:mysql存储引擎 和区别

    InnoDB存储引擎:事务型数据库首选,支持事务安全表(ACID),支持行锁定和外键  是mysql 5.5之后的默认引擎

    MyISAM 存储引擎:不支持事务和外键,访问速度较快,是mysql5.5 之前的默认引擎

    MEMORY: 保存在内存中的数据表 ,每个memory表对应一个磁盘文件。格式是.frm  访问速度很快 缺点是:mysql服务关闭,数据丢失,另外对数据表大小有限制。
    十一:如何判断一个字符串是另一个字符串的子串

     indexof       es6:include    startWith   endWith
    十二:单点登录
    十三:oauth2.0
    十四:type of 和instance of 区别 
    十五:pm2 restart 和reload的区别(配置文件的重载 重启)
    十六:MySQL 读写分离
    十七:pm2如何查看指定三个项目的日志
    十八:深拷贝 浅拷贝
    十九:路由机制
    二十:MySQL 批量更新
    二十一:登录流程
    二十二:cookie 和session
    二十三:基本数据类型 引用数据类型  区别
    二十四:防止sql 注入

    1.使用escape() 对传入参数进行编码

    2.使用connection.query ()的查询参数占位符

    3.使用escapeId()编码SQL查询标识符

    4.使用mysql.format()转义参数:

    二十五:require()模块加载机制

    先判断是否存在文件缓存区中,存在直接导入,没有的话,在判断是否是原生模块,

    如果是原生模块,再看是否在原生模块缓存区中,如果有直接导入,没有的话加载原生模块,缓存原生模块,在导入

    如果不是原生模块,先查找文件模块,根据扩展名载入文件模块,缓存文件模块,在导入

    展开全文
  • nodeJs面试题

    千次阅读 2019-11-30 01:53:28
    简介:nodejs是后端和Web应用程序中最流行和新兴的技术。您可以使用nodejs使用模板创建前端,并为服务器信息创建微服务。在这里,我汇总了在访谈问题中可能会问到的nodejs流行问题– 什么是Node.js Node.js是一...
  • nodejs面试题笔记

    2021-03-12 13:41:45
    一、nodejs是什么 nodejs 是基于 Chome V8 引擎的 Javascript运行时 nodejs出现以前,js只能再浏览器运行 nodejs出现之后,js可以再如何安装nodejs的环境运行 二、nodejs和前端js的区别 (1)语法层面 都使用ES...
  • nodejs面试题集锦

    2021-02-01 09:01:37
    1、为什么用Nodejs,它有哪些缺点? 事件驱动,通过闭包很容易实现客户端的生命活期。 不用担心多线程,锁,并行计算的问题 V8引擎速度非常快 对于游戏来说,写一遍游戏逻辑代码,前端后端通用 当然Nodejs也有...
  • 关于nodejs面试题详解

    千次阅读 2021-03-19 09:04:32
    nodejs的一个经典面试题 this.d = 4; exports.c = 3; module.exports = { a: 1, b: 2 } //module.exports.a = 1 //module.exports.b = 2 const a = require('./a.js') console.log(a); 这里执行的结果是: 。...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 5,586
精华内容 2,234
关键字:

nodejs面试题