摘自:http://www.admin10000.com/document/6715.html
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 语法到底好在哪里?一图胜千言
3、如何用Node监听80端口
这题有陷阱!在类Unix系统中你不应该尝试去监听80端口,因为这需要超级用户权限。 因此不推荐让你的应用直接监听这个端口。
目前,如果你一定要让你的应用监听80端口的话,你可以有通过在Node应用的前方再增加一层反向代理 (例如nginx)来实现,如下图所示。否则,建议你直接监听大于1024的端口。
方向代理指的是以代理服务器来接收Internet上的连接请求,然后将请求转发给内部网络上的服务器, 并且将服务器返回的结果发送给客户端。
关于反向代理的更多内容,建议你阅读这篇文章。
解释:这个问题用于检查被面试者是否有实际运行Node应用的经验。
4.什么是事件循环
Node采用的是单线程的处理机制(所有的I/O请求都采用非阻塞的工作方式),至少从Node.js开发者的角度是这样的。 而在底层,Node.js借助libuv来作为抽象封装层, 从而屏蔽不同操作系统的差异,Node可以借助livuv来来实现多线程。下图表示了Node和libuv的关系。
Libuv库负责Node API的执行。它将不同的任务分配给不同的线程,形成一个事件循环, 以异步的方式将任务的执行结果返回给V8引擎。可以简单用下面这张图来表示。
每一个I/O都需要一个回调函数——一旦执行完便推到事件循环上用于执行。 如果你需要更多详细的解释,可以参考这个视频。 你也可以参考这篇文章。
解释:这用于检查Node.js的底层知识,例如什么是libuv,它的作用是什么。
Event Loop: http://www.ruanyifeng.com/blog/2014/10/event-loop.html
5、哪些工具可以用来保证一致性的代码风格
你可以选择如下的工具:
在团队开发中,这些工具对于编写代码非常的有帮助,能够帮助团队开发者强制执行规定的风格指南, 还能够通过静态分析捕获常见的错误。
解析:用于检查被面试者是否有大型项目开发经验。
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、什么是测试金字塔?
测试金字塔指的是: 当我们在编写测试用例时,底层的单元测试应该远比上层的端到端测试要多。
当我们谈到HTTP API时,我们可能会涉及到:
有很多针对模型的底层单元测试
但你需要测试模型间如何交互时,需要减少集成测试
解析:本文主要考察被面试者的在测试方面的经验。
9、你最喜欢的HTTP框架以及原因
这题没有唯一的答案。本题主要考察被面试者对于他所使用的Node框架的理解程度, 考察他是否能够给出选择该框架的理由,优缺点等。常用的HTTP框架你可以参考这个网站。
Statement
原文地址:https://blog.risingstack.com/node-js-interview-questions
References
http://zyzhang.github.io/blog/2013/04/28/test-pyramid/
http://www.ruanyifeng.com/blog/2014/10/event-loop.html
http://segmentfault.com/a/1190000002921481