Update question/index format

This commit is contained in:
Lellansin 2017-02-22 15:15:50 +08:00
parent 334a4daf72
commit aa2a40b104
6 changed files with 71 additions and 74 deletions

View File

@ -101,13 +101,13 @@ Hi, 欢迎来到 ElemeFE, 如标题所示本教程的目的是教你如何通过
### 常见问题
* Buffer 一般用于处理什么数据? 其长度能否动态变化?
* Buffer 一般用于处理什么数据? 其长度能否动态变化? [more](https://github.com/ElemeFE/node-interview/blob/master/sections/io.md#buffer)
* Stream 的 highWaterMark 与 drain 事件是什么? 二者之间的关系是? [more](https://github.com/ElemeFE/node-interview/blob/master/sections/io.md#缓冲区)
* Stream 的 pipe 的作用是? 在 pipe 的过程中数据是引用传递还是拷贝传递? [more](https://github.com/ElemeFE/node-interview/blob/master/sections/io.md#pipe)
* 什么是文件描述符? 输入流/输出流/错误流是什么?
* console.log 是同步还是异步? 如何实现一个 console.log? [more](https://github.com/ElemeFE/node-interview/blob/master/sections/io.md#console)
* 如何同步的获取用户的输入?
* Readline 是如何实现的? (有思路即可) [more](https://github.com/ElemeFE/node-interview/blob/master/sections/io.md#Readline)
* Readline 是如何实现的? (有思路即可) [more](https://github.com/ElemeFE/node-interview/blob/master/sections/io.md#readline)
<font color="blue">[阅读更多](https://github.com/ElemeFE/node-interview/blob/master/sections/io.md)</font>

View File

@ -1,46 +1,10 @@
# 事件/异步
* `[Basic]` Promise
* [`[Doc]` Events (事件)](https://nodejs.org/dist/latest-v6.x/docs/api/events.html)
* [`[Doc]` Timers (定时器)](https://nodejs.org/dist/latest-v6.x/docs/api/timers.html)
* `[Point]` 并行
* `[Point]` 阻塞/异步
> <a name="q-1"></a> Promise 中 .then 的第二参数与 .catch 有什么区别?
参见 [We have a problem with promises](https://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html)
> <a name="q-2"></a> Eventemitter 的 emit 是同步还是异步?
同步.
> <a name="q-3"></a> 如何判断接口是否异步? 是否只要有回调函数就是异步?
开放性问题, 每个写 node 的人都有一套自己的判断方式.
* 看文档
* console.log 打印看看
* 看是否有IO操作
回调函数并没有异步, IO 操作才可能会异步, 除此之外还有使用 setTimeout 等方式实现异步.
> <a name="q-4"></a> nextTick, setTimeout 以及 setImmediate 三者有什么区别?
参见[该帖](https://cnodejs.org/topic/5556efce7cabb7b45ee6bcac)
> <a name="q-5"></a> 如何实现一个 sleep 函数? ①
```javascript
function sleep(ms) {
var start = Date.now(), expire = start + ms;
while (Date.now() < expire) ;
return;
}
```
> <a name="q-6"></a> 如何实现一个异步的 reduce? (注:不是异步完了之后同步 reduce)
需要了解 reduce 的情况, 是第 n 个与 n+1 的结果异步处理完之后, 在用新的结果与第 n+2 个元素继续依次异步下去. 不贴答案, 期待诸君的版本.
* [`[Basic]` Promise](https://github.com/ElemeFE/node-interview/blob/master/sections/event-async.md#promise)
* [`[Doc]` Events (事件)](https://github.com/ElemeFE/node-interview/blob/master/sections/event-async.md#events)
* [`[Doc]` Timers (定时器)](https://github.com/ElemeFE/node-interview/blob/master/sections/event-async.md#timers)
* [`[Point]` 阻塞/异步](https://github.com/ElemeFE/node-interview/blob/master/sections/event-async.md#阻塞异步)
* [`[Point]` 并行/并发](https://github.com/ElemeFE/node-interview/blob/master/sections/event-async.md#并行并发)
## 简述
@ -52,9 +16,13 @@ function sleep(ms) {
相信很多同学在面试的时候都碰到过这样一个问题, `如何处理 Callback Hell`. 在早些年的时候, 大家会看到有很多的解决方案例如 [Q](https://www.npmjs.com/package/q), [async](https://www.npmjs.com/package/async), [EventProxy](https://www.npmjs.com/package/eventproxy) 等等. 最后从流行程度来看 `Promise` 当之无愧的独领风骚, 并且是在 ES6 的 Javascript 标准上赢得了支持.
关于它的基础知识/概念推荐看阮一峰的 [Promise 对象](http://javascript.ruanyifeng.com/advanced/promise.html#toc9) 这里就不多不赘述, 直接来看问题.
关于它的基础知识/概念推荐看阮一峰的 [Promise 对象](http://javascript.ruanyifeng.com/advanced/promise.html#toc9) 这里就不多不赘述.
一下是一个很简单的 Promise 的使用例子:
> <a name="q-1"></a> Promise 中 .then 的第二参数与 .catch 有什么区别?
参见 [We have a problem with promises](https://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html)
另外关于同步与异步, 有个问题希望大家看一下, 这是很简单的 Promise 的使用例子:
```javascript
let doSth = new Promise((resolve, reject) => {
@ -99,6 +67,8 @@ setTimeout(() => {
通过继承 EventEmitter 来使得一个类具有 node 提供的基本的 event 方法, 这样的对象可以称作 emitter, 而触发(emit)事件的 cb 则称作 listener. 与前端 DOM 树上的事件并不相同, emitter 的触发不存在冒泡, 逐层捕获等事件行为, 也没有处理事件传递的方法.
> <a name="q-2"></a> Eventemitter 的 emit 是同步还是异步?
Node.js 中 Eventemitter 的 emit 是同步的. 在官方文档中有说明:
> The EventListener calls all listeners synchronously in the order in which they were registered. This is important to ensure the proper sequencing of events and to avoid race conditions or logic errors.
@ -157,21 +127,45 @@ emitter.emit('myEvent');
## 阻塞/异步
> <a name="q-3"></a> 如何判断接口是否异步? 是否只要有回调函数就是异步?
开放性问题, 每个写 node 的人都有一套自己的判断方式.
* 看文档
* console.log 打印看看
* 看是否有 IO 操作
单纯使用回调函数并不会异步, IO 操作才可能会异步, 除此之外还有使用 setTimeout 等方式实现异步.
> 有这样一个场景, 你在线上使用 koa 搭建了一个网站, 这个网站项目中有一个你同事写的接口 A, 而 A 接口中在特殊情况下会变成死循环. 那么首先问题是, 如果触发了这个死循环, 会对网站造成什么影响?
Node.js 中执行 js 代码的过程是单线程的. 只有当前代码都执行完, 才会切入事件循环, 然后从事件队列中 pop 出下一个回调函数开始执行代码. 所以 ① 实现一个 sleep 函数, 只要通过一个死循环就可以阻塞整个 js 的执行流程. (关于如何避免坑爹的同事写出死循环, 在后面的测试环节有写到.)
> <a name="q-5"></a> 如何实现一个 sleep 函数? ①
```javascript
function sleep(ms) {
var start = Date.now(), expire = start + ms;
while (Date.now() < expire) ;
return;
}
```
而异步, 是使用 libuv 来实现的 (C/C++的同学可以参见 libev 和 libevent) 另一个线程里的事件队列.
如果在线上的网站中出现了死循环的逻辑被触发, 整个进程就会一直卡在死循环中, 如果没有多进程部署的话, 之后的网站请求全部会超时, js 代码没有结束那么事件队列就会停下等待不会执行异步, 整个网站无法响应.
> <a name="q-6"></a> 如何实现一个异步的 reduce? (注:不是异步完了之后同步 reduce)
需要了解 reduce 的情况, 是第 n 个与 n+1 的结果异步处理完之后, 在用新的结果与第 n+2 个元素继续依次异步下去. 不贴答案, 期待诸君的版本.
## Timers
在笔者这里将 Node.js 中的异步简单的划分为两种, 硬异步和软异步.
硬异步是指由于 IO 操作或者外部调用走 libuv 而需要异步的情况. 当然, 也存在 readFileSync, execSync 等例外情况, 不过 node 由于是单线程的, 所以如果常规业务在普通时段执行可能比较耗时同步的 IO 操作会使得其执行过程中其他的所有操作都不能响应, 有点作死的感觉. 不过在启动/初始化以及一些工具脚本的应用场景下是完全没问题的. 而一般的场景下 IO 操作都是需要异步的.
软异步是指, 通过 setTimeout 等方式来实现的异步. 关于 nextTick, setTimeout 以及 setImmediate 三者的区别参见[该帖](https://cnodejs.org/topic/5556efce7cabb7b45ee6bcac)
软异步是指, 通过 setTimeout 等方式来实现的异步. <a name="q-4"></a> 关于 nextTick, setTimeout 以及 setImmediate 三者的区别参见[该帖](https://cnodejs.org/topic/5556efce7cabb7b45ee6bcac)
**Event loop 示例**

View File

@ -1,12 +1,12 @@
# IO
* `[Doc]` Stream (流)
* `[Doc]` Buffer
* `[Doc]` String Decoder (字符串解码)
* `[Doc]` Console (控制台)
* `[Doc]` File System (文件系统)
* `[Doc]` Readline
* `[Doc]` REPL
* [`[Doc]` Buffer](https://github.com/ElemeFE/node-interview/blob/master/sections/io.md#buffer)
* [`[Doc]` String Decoder (字符串解码)](https://github.com/ElemeFE/node-interview/blob/master/sections/io.md#string-decoder)
* [`[Doc]` Stream (流)](https://github.com/ElemeFE/node-interview/blob/master/sections/io.md#stream)
* [`[Doc]` Console (控制台)](https://github.com/ElemeFE/node-interview/blob/master/sections/io.md#console)
* [`[Doc]` File System (文件系统)](https://github.com/ElemeFE/node-interview/blob/master/sections/io.md#file)
* [`[Doc]` Readline](https://github.com/ElemeFE/node-interview/blob/master/sections/io.md#readline)
* [`[Doc]` REPL](https://github.com/ElemeFE/node-interview/blob/master/sections/io.md#repl)
# 简述
@ -14,7 +14,7 @@ Node.js 是以 IO 密集型业务著称. 那么问题来了, 你真的了解什
## Buffer
Buffer 是 Node.js 中用于处理二进制数据的类, 其中与 IO 相关的操作 (网络/文件等) 均基于 Buffer. Buffer 类的实例非常类似整数数组, 但其大小是固定不变的, 并且其内存在 V8 堆栈外分配原始内存空间. Buffer 类的实例创建之后, 其所占用的内存大小就不能再进行调整.
Buffer 是 Node.js 中用于处理二进制数据的类, 其中与 IO 相关的操作 (网络/文件等) 均基于 Buffer. Buffer 类的实例非常类似整数数组, ***但其大小是固定不变的***, 并且其内存在 V8 堆栈外分配原始内存空间. Buffer 类的实例创建之后, 其所占用的内存大小就不能再进行调整.
在 Node.js v6.x 之后 `new Buffer()` 接口开始被废弃, 理由是参数类型不同会返回不同类型的 Buffer 对象, 所以当开发者没有正确校验参数或没有正确初始化 Buffer 对象的内容时, 以及不了解的情况下初始化 就会在不经意间向代码中引入安全性和可靠性问题.

View File

@ -1,10 +1,10 @@
# Javascript 基础问题
* `[Basic]` 类型判断
* `[Basic]` 作用域
* `[Basic]` 引用传递
* `[Basic]` 内存释放
* `[Basic]` ES6 新特性
* [`[Basic]` 类型判断](https://github.com/ElemeFE/node-interview/blob/master/sections/js-basic.md#类型判断)
* [`[Basic]` 作用域](https://github.com/ElemeFE/node-interview/blob/master/sections/js-basic.md#作用域)
* [`[Basic]` 引用传递](https://github.com/ElemeFE/node-interview/blob/master/sections/js-basic.md#引用传递)
* [`[Basic]` 内存释放](https://github.com/ElemeFE/node-interview/blob/master/sections/js-basic.md#内存释放)
* [`[Basic]` ES6 新特性](https://github.com/ElemeFE/node-interview/blob/master/sections/js-basic.md#es6-新特性)
> <a name="q-value"></a> js 中什么类型是引用传递, 什么类型是值传递? 如何将值类型的变量以引用的方式传递?

View File

@ -3,6 +3,9 @@
* [`[Doc]` Modules (模块)](https://nodejs.org/dist/latest-v6.x/docs/api/modules.html)
* [`[Doc]` Globals (全局变量)](https://nodejs.org/dist/latest-v6.x/docs/api/globals.html)
* [`[Doc]` VM (虚拟机)](https://nodejs.org/dist/latest-v6.x/docs/api/vm.html)
* [`[Basic]` 模块机制](https://github.com/ElemeFE/node-interview/blob/master/sections/node-basic.md#模块机制)
* [`[Basic]` 热更新](https://github.com/ElemeFE/node-interview/blob/master/sections/node-basic.md#热更新)
* [`[Basic]` 上下文](https://github.com/ElemeFE/node-interview/blob/master/sections/node-basic.md#上下文)
## 常见问题

View File

@ -1,19 +1,10 @@
# 进程
* `[Doc]` Process (进程)
* `[Doc]` Child Processes (子进程)
* `[Doc]` Cluster (集群)
* `[Basic]` 进程间通信
* `[Basic]` 守护进程
> <a name="q-cwd"></a> 进程的当前工作目录是什么? 有什么作用?
当前进程启动的目录, 通过 process.cwd() 获取, 通常是命令行启动的时候所在的目录 (也可以在启动时指定), 文件操作等使用相对路径的时候会相对当前工作目录来获取文件.
> <a name="q-child"></a> 父进程或子进程的死亡是否会影响对方? 什么是僵死进程?
子进程死亡不会影响父进程, 不过 node 中父进程会收到子进程死亡的信号. 反之父进程死亡, 子进程也会跟着死亡, 如果子进程没有随之终止而继续存在的状态, 被称作僵死进程.
* [`[Doc]` Process (进程)](https://github.com/ElemeFE/node-interview/blob/master/sections/process.md#process)
* [`[Doc]` Child Processes (子进程)](https://github.com/ElemeFE/node-interview/blob/master/sections/process.md#child-process)
* [`[Doc]` Cluster (集群)](https://github.com/ElemeFE/node-interview/blob/master/sections/process.md#cluster)
* [`[Basic]` 进程间通信](https://github.com/ElemeFE/node-interview/blob/master/sections/process.md#进程间通信)
* [`[Basic]` 守护进程](https://github.com/ElemeFE/node-interview/blob/master/sections/process.md#守护进程)
## 简述
@ -89,7 +80,11 @@ function test() {
你可以通过[设置环境变量](http://cn.bing.com/search?q=linux+%E8%AE%BE%E7%BD%AE%E7%8E%AF%E5%A2%83%E5%8F%98%E9%87%8F)来指定配置, 然后通过 `process.env` 来获取配置项. 另外也可以通过读取定义好的配置文件来获取, 在这方面有很多不错的库例如 `dotenv`, `node-config` 等, 而在使用这些库来加载配置文件的时候, 通常都会碰到一个当前工作目录的问题.
通过 `process.cwd()` 获取当前目录 (current working directory), 一些获取配置的第三方模块就是通过你的当前目录来找配置文件的. 所以如果你错误的目录启动脚本, 可能没法得到正确的结果. 在程序中可以通过 `process.chdir()` 来改变当前的工作目录.
> <a name="q-cwd"></a> 进程的当前工作目录是什么? 有什么作用?
当前进程启动的目录, 通过 process.cwd() 获取当前工作目录 (current working directory), 通常是命令行启动的时候所在的目录 (也可以在启动时指定), 文件操作等使用相对路径的时候会相对当前工作目录来获取文件.
一些获取配置的第三方模块就是通过你的当前目录来找配置文件的. 所以如果你错误的目录启动脚本, 可能没法得到正确的结果. 在程序中可以通过 `process.chdir()` 来改变当前的工作目录.
### 标准流
@ -105,6 +100,8 @@ function test() {
子进程 (Child Process) 是进程中一个重要的概念. 你可以通过 Node.js 的 `child_process` 模块来执行可执行文件, 调用命令行命令, 比如其他语言的程序等. 也可以通过该模块来将 .js 代码以子进程的方式启动. 比较有名的网易的分布式架构 [pomelo](https://github.com/NetEase/pomelo) 就是基于该模块 (而不是 `cluster`) 来实现多进程分布式架构的.
> <a name="q-fork"></a> child_process.fork 与 POSIX 的 fork 有什么区别?
Node.js 的 `child_process.fork()` 不像 POSIX [fork(2)](http://man7.org/linux/man-pages/man2/fork.2.html) 系统调用, 不会拷贝当前父进程. 这里对于其他语言转过的同学可能比较误导, 可以作为一个比较偏的面试题.
* spawn() 启动一个子进程来执行命令
@ -123,6 +120,9 @@ Node.js 的 `child_process.fork()` 不像 POSIX [fork(2)](http://man7.org/linux/
常见会问的面试题, 如 `child.kill``child.send` 的区别. 二者一个是基于信号系统, 一个是基于 IPC.
> <a name="q-child"></a> 父进程或子进程的死亡是否会影响对方? 什么是僵死进程?
子进程死亡不会影响父进程, 不过 node 中父进程会收到子进程死亡的信号. 反之父进程死亡, 子进程也会跟着死亡, 如果子进程没有随之终止而继续存在的状态, 被称作僵死进程.
## Cluster