Skip to content

Commit 0dee69d

Browse files
committed
Add stdio dsrp
1 parent aa2a40b commit 0dee69d

3 files changed

Lines changed: 137 additions & 48 deletions

File tree

README.md

Lines changed: 35 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,19 @@
22

33
# 如何通过饿了么 Node.js 面试
44

5-
Hi, 欢迎来到 ElemeFE, 如标题所示本教程的目的是教你如何通过饿了么大前端的面试, 职位是 2~3 年经验的 Node.js 服务端程序员, 如果你对这个职位感兴趣或者想了解一下相关情况,那么欢迎阅读.
5+
Hi, 欢迎来到 ElemeFE, 如标题所示本教程的目的是教你如何通过饿了么大前端的面试, 职位是 2~3 年经验的 Node.js 服务端程序员, 如果你对这个职位感兴趣或者学习 Node.js 一些进阶的内容, 那么欢迎阅读.
66

7-
需要注意的是, 本文针对的并不是零基础的同学, 你需要有一定的 JavaScript/Node 基础, 并且有一定的工作经验.
7+
需要注意的是, 本文针对的并不是零基础的同学, 你需要有一定的 JavaScript/Node.js 基础, 并且有一定的工作经验. 另外本教程的重点更准确的说是服务端基础中 Node.js 程序员需要了解的部分.
88

9-
如果你觉得大多不了解, 就不用投简历了 <del>(这样两边都节约了时间)</del>, 如果你觉得大都有了解或者<font color="blue">光看大纲都都觉得很简单那么欢迎投递简历至 ElemeFe (fe.job@ele.me)</font>.
9+
如果你觉得大多不了解, 就不用投简历了 <del>(这样两边都节约了时间)</del>, 如果你觉得大都有了解或者***光看大纲都都觉得很简单那么欢迎投递简历至 ElemeFe (fe.job@ele.me)***.
1010

1111
### 导读
1212

13-
虽然说目的是要通过面试, 但是本教程并不是简单的把所有面试题列出来, 而<font color="red">主要是将面试中需要确认你是否懂的点列举出来</font>, 并进行一定程度的讨论. 本文将一些常见的问题划分归类, 每类标明涵盖的一些`覆盖点`, 并且列举几个`常见问题`, 通常这些问题都是 2~3 年工作经验需要了解或者面对的. 如果你对某类问题感兴趣, 或者想知道其中列举问题的答案, 可以通过该类下方的 `阅读更多` 查看更多的内容.
13+
虽然说目的是要通过面试, 但是本教程并不是简单的把所有面试题列出来, 而<font color="red">主要是将面试中需要确认你是否懂的点列举出来</font>, 并进行一定程度的讨论.
1414

15-
大纲列举的并不是很全面, 细节上覆盖率不高, 很多讨论只是点到即止, 就是希望大家带着问题去思考.
15+
本文将一些常见的问题划分归类, 每类标明涵盖的一些`覆盖点`, 并且列举几个`常见问题`, 通常这些问题都是 2~3 年工作经验需要了解或者面对的. 如果你对某类问题感兴趣, 或者想知道其中列举问题的答案, 可以通过该类下方的 `阅读更多` 查看更多的内容.
16+
17+
整体上大纲列举的并不是很全面, 细节上覆盖率不高, 很多讨论只是点到即止, 希望大家带着问题去思考.
1618

1719
## [Js 基础问题](https://github.com/ElemeFE/node-interview/blob/master/sections/js-basic.md)
1820

@@ -26,12 +28,12 @@ Hi, 欢迎来到 ElemeFE, 如标题所示本教程的目的是教你如何通过
2628

2729
### 常见问题
2830

29-
* js 中什么类型是引用传递, 什么类型是值传递? 如何将值类型的变量以引用的方式传递? [more](https://github.com/ElemeFE/node-interview/blob/master/sections/js-basic.md#q-value)
31+
* js 中什么类型是引用传递, 什么类型是值传递? 如何将值类型的变量以引用的方式传递? [[more]](https://github.com/ElemeFE/node-interview/blob/master/sections/js-basic.md#q-value)
3032
* js 中, 0.1 + 0.2 === 0.3 是否为 true ? 在不知道浮点数位数时应该怎样判断两个浮点数之和与第三数是否相等?
31-
* const 定义的 Array 中间元素能否被修改? 如果可以, 那 const 的意义是? [more](https://github.com/ElemeFE/node-interview/blob/master/sections/js-basic.md#q-const)
32-
* Javascript 中不同类型以及不同环境下变量的内存都是何时释放? [more](https://github.com/ElemeFE/node-interview/blob/master/sections/js-basic.md#q-mem)
33+
* const 定义的 Array 中间元素能否被修改? 如果可以, 那 const 的意义是? [[more]](https://github.com/ElemeFE/node-interview/blob/master/sections/js-basic.md#q-const)
34+
* Javascript 中不同类型以及不同环境下变量的内存都是何时释放? [[more]](https://github.com/ElemeFE/node-interview/blob/master/sections/js-basic.md#q-mem)
3335

34-
<font color="blue">[阅读更多](https://github.com/ElemeFE/node-interview/blob/master/sections/js-basic.md)</font>
36+
[阅读更多](https://github.com/ElemeFE/node-interview/blob/master/sections/js-basic.md)
3537

3638
## [node 基础问题](https://github.com/ElemeFE/node-interview/blob/master/sections/node-basic.md)
3739

@@ -46,11 +48,11 @@ Hi, 欢迎来到 ElemeFE, 如标题所示本教程的目的是教你如何通过
4648

4749
### 常见问题
4850

49-
* a.js 和 b.js 两个文件互相 require 是否会死循环? 双方是否能导出变量? 如何从设计上避免这种问题? [more](https://github.com/ElemeFE/node-interview/blob/master/sections/node-basic.md#q-loop)
50-
* 如果 a.js require 了 b.js, 那么在 b 中定义全局变量 `t = 111` 能否在 a 中直接打印出来? [more](https://github.com/ElemeFE/node-interview/blob/master/sections/node-basic.md#q-global)
51-
* 如何在不重启 node 进程的情况下热更新一个 js/json 文件? 这个问题本身是否有问题? [more](https://github.com/ElemeFE/node-interview/blob/master/sections/node-basic.md#q-hot)
51+
* a.js 和 b.js 两个文件互相 require 是否会死循环? 双方是否能导出变量? 如何从设计上避免这种问题? [[more]](https://github.com/ElemeFE/node-interview/blob/master/sections/node-basic.md#q-loop)
52+
* 如果 a.js require 了 b.js, 那么在 b 中定义全局变量 `t = 111` 能否在 a 中直接打印出来? [[more]](https://github.com/ElemeFE/node-interview/blob/master/sections/node-basic.md#q-global)
53+
* 如何在不重启 node 进程的情况下热更新一个 js/json 文件? 这个问题本身是否有问题? [[more]](https://github.com/ElemeFE/node-interview/blob/master/sections/node-basic.md#q-hot)
5254

53-
<font color="blue">[阅读更多](https://github.com/ElemeFE/node-interview/blob/master/sections/node-basic.md)</font>
55+
[阅读更多](https://github.com/ElemeFE/node-interview/blob/master/sections/node-basic.md)
5456

5557
## [事件/异步](https://github.com/ElemeFE/node-interview/blob/master/sections/event-async.md)
5658

@@ -62,14 +64,14 @@ Hi, 欢迎来到 ElemeFE, 如标题所示本教程的目的是教你如何通过
6264

6365
### 常见问题
6466

65-
* Promise 中 .then 的第二参数与 .catch 有什么区别? [more](https://github.com/ElemeFE/node-interview/blob/master/sections/event-async.md#q-1)
66-
* Eventemitter 的 emit 是同步还是异步? [more](https://github.com/ElemeFE/node-interview/blob/master/sections/event-async.md#q-2)
67-
* 如何判断接口是否异步? 是否只要有回调函数就是异步? [more](https://github.com/ElemeFE/node-interview/blob/master/sections/event-async.md#q-3)
68-
* nextTick, setTimeout 以及 setImmediate 三者有什么区别? [more](https://github.com/ElemeFE/node-interview/blob/master/sections/event-async.md#q-4)
69-
* 如何实现一个 sleep 函数? [more](https://github.com/ElemeFE/node-interview/blob/master/sections/event-async.md#q-5)
70-
* 如何实现一个异步的 reduce? (注:不是异步完了之后同步 reduce) [more](https://github.com/ElemeFE/node-interview/blob/master/sections/event-async.md#q-6)
67+
* Promise 中 .then 的第二参数与 .catch 有什么区别? [[more]](https://github.com/ElemeFE/node-interview/blob/master/sections/event-async.md#q-1)
68+
* Eventemitter 的 emit 是同步还是异步? [[more]](https://github.com/ElemeFE/node-interview/blob/master/sections/event-async.md#q-2)
69+
* 如何判断接口是否异步? 是否只要有回调函数就是异步? [[more]](https://github.com/ElemeFE/node-interview/blob/master/sections/event-async.md#q-3)
70+
* nextTick, setTimeout 以及 setImmediate 三者有什么区别? [[more]](https://github.com/ElemeFE/node-interview/blob/master/sections/event-async.md#q-4)
71+
* 如何实现一个 sleep 函数? [[more]](https://github.com/ElemeFE/node-interview/blob/master/sections/event-async.md#q-5)
72+
* 如何实现一个异步的 reduce? (注:不是异步完了之后同步 reduce) [[more]](https://github.com/ElemeFE/node-interview/blob/master/sections/event-async.md#q-6)
7173

72-
<font color="blue">[阅读更多](https://github.com/ElemeFE/node-interview/blob/master/sections/event-async.md)</font>
74+
[阅读更多](https://github.com/ElemeFE/node-interview/blob/master/sections/event-async.md)
7375

7476
## [进程](https://github.com/ElemeFE/node-interview/blob/master/sections/process.md)
7577

@@ -81,12 +83,12 @@ Hi, 欢迎来到 ElemeFE, 如标题所示本教程的目的是教你如何通过
8183

8284
### 常见问题
8385

84-
* 进程的当前工作目录是什么? 有什么作用? [more](https://github.com/ElemeFE/node-interview/blob/master/sections/process.md#q-cwd)
85-
* child_process.fork 与 POSIX 的 fork 有什么区别? [more](https://github.com/ElemeFE/node-interview/blob/master/sections/process.md#q-fork)
86-
* 父进程或子进程的死亡是否会影响对方? 什么是僵死进程? [more](https://github.com/ElemeFE/node-interview/blob/master/sections/process.md#q-child)
87-
* 什么是守护进程? 如何实现守护进程? [more](https://github.com/ElemeFE/node-interview/blob/master/sections/process.md#守护进程)
86+
* 进程的当前工作目录是什么? 有什么作用? [[more]](https://github.com/ElemeFE/node-interview/blob/master/sections/process.md#q-cwd)
87+
* child_process.fork 与 POSIX 的 fork 有什么区别? [[more]](https://github.com/ElemeFE/node-interview/blob/master/sections/process.md#q-fork)
88+
* 父进程或子进程的死亡是否会影响对方? 什么是僵死进程? [[more]](https://github.com/ElemeFE/node-interview/blob/master/sections/process.md#q-child)
89+
* 什么是守护进程? 如何实现守护进程? [[more]](https://github.com/ElemeFE/node-interview/blob/master/sections/process.md#守护进程)
8890

89-
<font color="blue">[阅读更多](https://github.com/ElemeFE/node-interview/blob/master/sections/process.md)</font>
91+
[阅读更多](https://github.com/ElemeFE/node-interview/blob/master/sections/process.md)
9092

9193

9294
## [IO](https://github.com/ElemeFE/node-interview/blob/master/sections/io.md)
@@ -101,15 +103,15 @@ Hi, 欢迎来到 ElemeFE, 如标题所示本教程的目的是教你如何通过
101103

102104
### 常见问题
103105

104-
* Buffer 一般用于处理什么数据? 其长度能否动态变化? [more](https://github.com/ElemeFE/node-interview/blob/master/sections/io.md#buffer)
105-
* Stream 的 highWaterMark 与 drain 事件是什么? 二者之间的关系是? [more](https://github.com/ElemeFE/node-interview/blob/master/sections/io.md#缓冲区)
106-
* Stream 的 pipe 的作用是? 在 pipe 的过程中数据是引用传递还是拷贝传递? [more](https://github.com/ElemeFE/node-interview/blob/master/sections/io.md#pipe)
107-
* 什么是文件描述符? 输入流/输出流/错误流是什么?
108-
* console.log 是同步还是异步? 如何实现一个 console.log? [more](https://github.com/ElemeFE/node-interview/blob/master/sections/io.md#console)
109-
* 如何同步的获取用户的输入?
110-
* Readline 是如何实现的? (有思路即可) [more](https://github.com/ElemeFE/node-interview/blob/master/sections/io.md#readline)
106+
* Buffer 一般用于处理什么数据? 其长度能否动态变化? [[more]](https://github.com/ElemeFE/node-interview/blob/master/sections/io.md#buffer)
107+
* Stream 的 highWaterMark 与 drain 事件是什么? 二者之间的关系是? [[more]](https://github.com/ElemeFE/node-interview/blob/master/sections/io.md#缓冲区)
108+
* Stream 的 pipe 的作用是? 在 pipe 的过程中数据是引用传递还是拷贝传递? [[more]](https://github.com/ElemeFE/node-interview/blob/master/sections/io.md#pipe)
109+
* 什么是文件描述符? 输入流/输出流/错误流是什么? [[more]](https://github.com/ElemeFE/node-interview/blob/master/sections/io.md#file)
110+
* console.log 是同步还是异步? 如何实现一个 console.log? [[more]](https://github.com/ElemeFE/node-interview/blob/master/sections/io.md#console)
111+
* 如何同步的获取用户的输入? [[more]](https://github.com/ElemeFE/node-interview/blob/master/sections/io.md#如何同步的获取用户的输入)
112+
* Readline 是如何实现的? (有思路即可) [[more]](https://github.com/ElemeFE/node-interview/blob/master/sections/io.md#readline)
111113

112-
<font color="blue">[阅读更多](https://github.com/ElemeFE/node-interview/blob/master/sections/io.md)</font>
114+
[阅读更多](https://github.com/ElemeFE/node-interview/blob/master/sections/io.md)
113115

114116
## Network
115117

sections/io.md

Lines changed: 90 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ Buffer.allocUnsafe()|创建一个未初始化的 Buffer 对象
2828

2929
Node.js 的 Buffer 在 ES6 增加了 TypedArray 类型之后, 修改了原来的 Buffer 的实现, 选择基于 TypedArray 中 Uint8Array 来实现, 从而提升了一波性能.
3030

31-
使用上, 你需要了解如下例子:
31+
使用上, 你需要了解如下情况:
3232

3333
```javascript
3434
const arr = new Uint16Array(2);
@@ -147,7 +147,7 @@ int copy(const char *src, const char *dest)
147147
148148
### 缓冲区
149149
150-
Node.js 中 stream 的缓冲区, 以开头的 C语言 拷贝文件的代码为模板讨论, (抛开异步的区别看) 则是从 <font color="blue">src</font> 中读出数据到 <font color="blue">buf</font> 中后, 并没有直接写入 <font color="blue">dest</font> 中, 而是先放在一个比较大的缓冲区中, 等待写入(消费) <font color="blue">dest</font> 中. 即, 在缓冲区的帮助下可以使读与写的过程分离.
150+
Node.js 中 stream 的缓冲区, 以开头的 C语言 拷贝文件的代码为模板讨论, (抛开异步的区别看) 则是从 `src` 中读出数据到 `buf` 中后, 并没有直接写入 `dest` 中, 而是先放在一个比较大的缓冲区中, 等待写入(消费) `dest` 中. 即, 在缓冲区的帮助下可以使读与写的过程分离.
151151
152152
Readable 和 Writable 流都会将数据储存在内部的缓冲区中. 缓冲区可以分别通过 `writable._writableState.getBuffer()` 和 `readable._readableState.buffer` 来访问. 缓冲区的大小, 由构造 stream 时候的 `highWaterMark` 标志指定可容纳的 byte 大小, 对于 `objectMode` 的 stream, 该标志表示可以容纳的对象个数.
153153
@@ -244,11 +244,95 @@ Node.js 封装了标准 POSIX 文件 I/O 操作的集合. 通过 require('fs')
244244

245245
### stdio
246246

247-
标准的 IO 流, 即输入流 (stdin), 输出流 (stdout), 错误流 (stderr).
247+
stdio (standard input output) 标准的输入输出流, 即输入流 (stdin), 输出流 (stdout), 错误流 (stderr) 三者. 在 Node.js 中分别对应 `process.stdin` (Readable), `process.stdout` (Writable) 以及 `process.stderr` (Writable) 三个 stream.
248248

249+
输出函数是每个人在学习任何一门编程语言时所需要学到的第一个函数. 例如 C语言的 `printf("hello, world!");` python/ruby 的 `print 'hello, world!'` 以及 Javascript 中的 `console.log('hello, world!');`
249250

251+
以 C语言的伪代码来看的话, 这类输出函数的实现思路如下:
250252

251-
整理中
253+
```c
254+
int printf(FILE *stream, 要打印的内容)
255+
{
256+
// ...
257+
258+
// 1. 申请一个临时内存空间
259+
char *s = malloc(4096);
260+
261+
// 2. 处理好要打印的的内容, 其值存储在 s 中
262+
// ...
263+
264+
// 3. 将 s 上的内容写入到 stream 中
265+
fwrite(s, stream);
266+
267+
// 4. 释放临时空间
268+
free(s);
269+
270+
// ...
271+
}
272+
```
273+
274+
我们需要了解的是第 3 步, 其中的 stream 则是指 stdout (输出流). 实际上在 shell 上运行一个应用程序的时候, shell 做的第一个操作是 fork 当前 shell 的进程 (所以, 如果你通过 ps 去查看你从 shell 上启动的进程, 其父进程 pid 就是当前 shell 的 pid), 在这个过程中也把 shell 的 stdio 继承给了你当前的应用进程, 所以你在当前进程里面将数据写入到 stdout, 也就是写入到了 shell 的 stdout, 即在当前 shell 上显示了.
275+
276+
输入也是同理, 当前进程继承了 shell 的 stdin, 所以当你从 stdin 中读取数据时, 其实就获取到你在 shell 上输入的数据. (PS: shell 可以是 windows 下的 cmd, powershell, 也可以是 linux 下 bash 或者 zsh 等)
277+
278+
当你使用 ssh 在远程服务器上运行一个命令的时候, 在服务器上的命令输出虽然也是写入到服务器上 shell 的 stdout, 但是这个远程的 shell 是从 sshd 服务上 fork 出来的, 其 stdout 是继承自 sshd 的一个 fd, 这个 fd 其实是个 socket, 所以最终其实是写入到了一个 socket 中, 通过这个 socket 传输你本地的计算机上的 shell 的 stdout.
279+
280+
另外 `console.error` 则是将数据写入到错误流中 (`process.stderr`).
281+
282+
### 如何同步的获取用户的输入?
283+
284+
如果你理解了上述的内容, 那么放到 Node.js 中来看, 获取用户的输入其实就是读取 Node.js 进程中的输入流 (即 process.stdin 这个 stream) 的数据.
285+
286+
而要同步读取, 则是不用异步的 read 接口, 而是用同步的 readSync 接口去读取 stdin 的数据即可实现. 以下来自万能的 stackoverflow:
287+
288+
```javascript
289+
/*
290+
* http://stackoverflow.com/questions/3430939/node-js-readsync-from-stdin
291+
* @mklement0
292+
*/
293+
var fs = require('fs');
294+
295+
var BUFSIZE = 256;
296+
var buf = new Buffer(BUFSIZE);
297+
var bytesRead;
298+
299+
module.exports = function() {
300+
var fd = ('win32' === process.platform) ? process.stdin.fd : fs.openSync('/dev/stdin', 'rs');
301+
bytesRead = 0;
302+
303+
try {
304+
bytesRead = fs.readSync(fd, buf, 0, BUFSIZE);
305+
} catch (e) {
306+
if (e.code === 'EAGAIN') { // 'resource temporarily unavailable'
307+
// Happens on OS X 10.8.3 (not Windows 7!), if there's no
308+
// stdin input - typically when invoking a script without any
309+
// input (for interactive stdin input).
310+
// If you were to just continue, you'd create a tight loop.
311+
console.error('ERROR: interactive stdin input not supported.');
312+
process.exit(1);
313+
} else if (e.code === 'EOF') {
314+
// Happens on Windows 7, but not OS X 10.8.3:
315+
// simply signals the end of *piped* stdin input.
316+
return '';
317+
}
318+
throw e; // unexpected exception
319+
}
320+
321+
if (bytesRead === 0) {
322+
// No more stdin input available.
323+
// OS X 10.8.3: regardless of input method, this is how the end
324+
// of input is signaled.
325+
// Windows 7: this is how the end of input is signaled for
326+
// *interactive* stdin input.
327+
return '';
328+
}
329+
// Process the chunk read.
330+
331+
var content = buf.toString(null, 0, bytesRead - 1);
332+
333+
return content;
334+
};
335+
```
252336

253337
## Readline
254338

@@ -269,6 +353,8 @@ rl.on('line', (line) => {
269353

270354
实现上, realine 在读取 TTY 的数据时, 是通过 `input.on('keypress', onkeypress)` 时发现用户按下了回车键来判断是新的 line 的, 而读取一般的 stream 时, 则是通过缓存数据然后用正则 .test 来判断是否为 new line 的.
271355

356+
PS: 打个广告, 如果在编写脚本时, 不习惯这样异步获取输入, 想要同步获取同步的用户输入可以看一看这个 Node.js 版本类 C语言使用的 [scanf](https://github.com/Lellansin/node-scanf/) 模块 (支持 ts).
357+
272358
## REPL
273359

274360
Read-Eval-Print-Loop (REPL)

0 commit comments

Comments
 (0)