├── .gitignore ├── README.md ├── buffer ├── assets │ └── typedArray.png ├── buffer.js └── buffer.md ├── daemon ├── b.js ├── daemon.js └── daemon.md ├── debug ├── debug.js ├── node-inspector.js └── repl.js ├── events └── events.js ├── http ├── http.md ├── incoming.js ├── listener.js └── response.js ├── package.json ├── process └── process.js ├── stream ├── stream.js └── stream.md ├── v8 ├── v8.js └── v8.md └── view-engine ├── ejs.js ├── ejs.md ├── index.js └── test.ejs /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | tmp* 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # full-stack-practice 2 | 3 | # http对象相关 4 | 5 | - [http IncomingMessage](./http/incoming.js) 6 | 7 | - [http ServerResponse](./http/response.js) 8 | 9 | - [response/request数据结构](./http/http.md) 10 | 11 | # stream对象相关 12 | 13 | - [stream知识体系和数据结构](./stream/stream.md) 14 | 15 | # Buffer数据类型相关 16 | 17 | - [Buffer基本知识](./buffer/buffer.md) 18 | 19 | # 创建守护进程 20 | 21 | - [daemon](./daemon/daemon.js) 22 | 23 | # 模板引擎 24 | 25 | - [view-engine](./view-engine/ejs.js) 26 | 27 | ```bash 28 | npm run v-engine 29 | ``` 30 | 31 | 32 | # 四种debug主流debug模式 33 | 34 | 1. V8内置的inspect 35 | 36 | ```bash 37 | node --inspect `yourcode` 38 | ``` 39 | 40 | 2. [node-inspector配合nodemon](./debug/node-inspector.js) 41 | 42 | ```bash 43 | npm run inspector 44 | ``` 45 | 46 | 3. [原生repl](./debug/repl.js) 47 | 48 | ```bash 49 | npm run repl 50 | ``` 51 | 52 | 4. [debug模块优化日志](./debug/debug.js) 53 | 54 | ```bash 55 | npm run debug 56 | ``` 57 | -------------------------------------------------------------------------------- /buffer/assets/typedArray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashhuang/full-stack-practice/0e1a6b08573d4e79aa43b3f7d2a215ad258cc9a8/buffer/assets/typedArray.png -------------------------------------------------------------------------------- /buffer/buffer.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 关于buffer 3 | * @Author slashhuang 4 | */ 5 | // Creates a zero-filled Buffer of length 10. 6 | const buf1 = Buffer.alloc(10); 7 | // 8 | console.log(buf1); 9 | 10 | // Creates a Buffer of length 10, filled with 0x1. 11 | const buf2 = Buffer.alloc(10, 1); 12 | // 13 | console.log(buf2) 14 | 15 | // Creates a Buffer containing [0x1, 0x2, 0x3]. 16 | const buf4 = Buffer.from([1, 2, 3]); 17 | // 18 | console.log(buf4) 19 | 20 | 21 | /*----------- Encoding ----------*/ 22 | const buf5 = Buffer.from('test'); 23 | //test 24 | console.log(buf5.toString('utf8')) 25 | 26 | // encoding互换 27 | const bufEnc1 = Buffer.from('hello world', 'ascii'); 28 | console.log(bufEnc1.toString('ascii')) 29 | 30 | /*----------- Iteration ----------*/ 31 | const bufArr = Buffer.from([1,2,3]); 32 | // print 1,2,3 33 | for(const b of bufArr){ 34 | console.log(b) 35 | } 36 | 37 | /*----------- Method ----------*/ 38 | const buf_1 = Buffer.from('1234'); 39 | const buf_2 = Buffer.from('0123'); 40 | const arr = [buf_1, buf_2]; 41 | // 1 42 | console.log(Buffer.compare(buf_1,buf_2)) 43 | // Prints: [ , ] 44 | console.log(arr.sort(Buffer.compare)); 45 | 46 | const totalLength = buf_2.length + buf_1.length; 47 | const buf_cat = Buffer.concat([buf_2,buf_1], totalLength); 48 | // 49 | console.log(buf_cat) 50 | 51 | 52 | const buf_from = Buffer.from(buf_1); 53 | buf_from[0] = 0x61; 54 | // Prints: 1234 55 | console.log(buf_1.toString()); 56 | // Prints: a1234 57 | console.log(buf_from.toString()); 58 | //[0,97] [1,50]等 59 | for(const pair of buf_from.entries()){ 60 | console.log(pair) 61 | } 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /buffer/buffer.md: -------------------------------------------------------------------------------- 1 | 2 | # buffer学习 3 | 4 | [Node.jsbuffer](https://github.com/nodejs/node/blob/master/doc/api/buffer.md) 5 | 6 | ## 基础 7 | 8 | > ![typedArray说明](./assets/typedArray.png) 9 | 10 | ES6引入TypedArray,而buffer类实现了Unit8Array,适用Node.js的使用场景。 11 | 12 | Buffer实例和整数数组类似,但是大小固定,并且属于V8的堆外内存。 13 | 14 | Buffer是Node.js的全局类,因此不需要require('buffer')来引入。 15 | 16 | > 数据存储采用16进制标示,每一位最大为255 17 | 18 | > 以255为unsigned integar范围。【这点有待查文献考证】 19 | 20 | ## Buffer方法演进 21 | 22 | > Node版本6之前创建Buffer需要通过Buffer构造函数 23 | 24 | ```javaScript 25 | new Buffer([number|| string || array || ArrayBuffer]) 26 | ``` 27 | > 不同的参数类型将导致return的结果不同。 28 | 29 | > 所以为了让Buffer实例更可靠,以前的构造函数形式被以下的形式代替。 30 | 31 | ```javaScript 32 | Buffer.from(),Buffer.alloc(),Buffer.allocUnsafe() 33 | ``` 34 | 35 | * [`Buffer.from(array)`]=> 36 | 37 | a new `Buffer` containing a *copy* of the provided octets. 38 | 39 | * [`Buffer.from(arrayBuffer[, byteOffset [, length]])`]=> 40 | 41 | returns a new `Buffer` that *shares* the same allocated memory as the given [`ArrayBuffer`]. 42 | 43 | * [`Buffer.from(buffer)`] => 44 | 45 | returns a new `Buffer` containing a *copy* of the contents of the given `Buffer`. 46 | 47 | * [`Buffer.from(string[, encoding])`][`Buffer.from(string)`] => 48 | 49 | returns a new `Buffer` containing a *copy* of the provided string. 50 | 51 | * [`Buffer.alloc(size[, fill[, encoding]])`][`Buffer.alloc()`] => 52 | 53 | returns a "filled" `Buffer` instance of the specified size. 54 | 55 | This method can be significantly slower than [`Buffer.allocUnsafe(size)`][`Buffer.allocUnsafe()`] but ensures 56 | that newly created `Buffer` instances never contain old and potentially 57 | sensitive data. 58 | 59 | 60 | ## Buffer和字符串encoding 61 | 62 | > Buffer instances are commonly used to represent sequences 63 | > of encoded characters such as UTF-8, UCS2, Base64 or even Hex-encoded data 64 | 65 | ## Buffers and ES6 iteration 66 | 67 | > Buffer instances can be iterated over using the ECMAScript 2015 (ES6) for..of syntax. 68 | 69 | 70 | ## Buffer方法 71 | 72 | - Buffer.byteLength(string[, encoding]) 73 | 74 | > Returns: {integer} The number of bytes contained within string 75 | 76 | - Buffer.compare(buf1, buf2) 77 | 78 | > Returns: {integer} 79 | 80 | - Buffer.concat(list[, totalLength]) 81 | 82 | > 尽量手写totalLength,避免程序为了计算totalLength,再进行loop 83 | > truncated to totalLength 84 | > Returns: {Buffer} 85 | 86 | - Buffer.isBuffer(obj) 87 | 88 | > obj {Object} 89 | > Returns: {boolean} 90 | 91 | - Buffer.isEncoding(encoding) 92 | 93 | > encoding {string} A character encoding name to check 94 | > Returns: {boolean} 95 | 96 | 97 | - buf[index] 98 | 99 | - buf.buffer 100 | 101 | > references the underlying ArrayBuffer object based on which this Buffer object is created 102 | 103 | - buf.entries() 104 | 105 | > Returns: {Iterator} 106 | 107 | - buf.indexOf(value[, byteOffset][, encoding]) 108 | 109 | - buf.keys() 110 | 111 | - buf.length 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | -------------------------------------------------------------------------------- /daemon/b.js: -------------------------------------------------------------------------------- 1 | /* 2 | * http服务 3 | */ 4 | 5 | const http = require('http'); 6 | const fs =require('fs') 7 | 8 | http.createServer((req,res)=>{ 9 | //length 38998 10 | console.log(req.headers['user-agent']) 11 | res.end('hello world') 12 | }).listen(3000); 13 | -------------------------------------------------------------------------------- /daemon/daemon.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 创建守护进程 3 | * @Author slashhuang 4 | * 参考资料 5 | * https://cnodejs.org/topic/57adfadf476898b472247eac#58dce6e9b3e60b982d089d47 6 | */ 7 | 8 | const spawn = require('child_process').spawn; 9 | const path = require('path'); 10 | const fd_child = path.resolve(__dirname,'b.js') 11 | /* 12 | * Nodejs中setsid的调用 13 | * 在 spawn 的第三个参数中,可以设置 detached 属性, 14 | * 如果该属性为true,则会调用 setsid 方法。这样就满足我们对守护进程的要求。 15 | */ 16 | const p = spawn('node',[fd_child],{ 17 | detached : true, 18 | stdio: 'ignore' 19 | }); 20 | 21 | //25465 25467 22 | console.log(process.pid, p.pid); 23 | 24 | //top | grep 25467 25 | p.unref() 26 | 27 | -------------------------------------------------------------------------------- /daemon/daemon.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashhuang/full-stack-practice/0e1a6b08573d4e79aa43b3f7d2a215ad258cc9a8/daemon/daemon.md -------------------------------------------------------------------------------- /debug/debug.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author slashhuang 3 | * debug模块 4 | * 5 | * 优化日志命名空间 6 | * 17/3/28 7 | */ 8 | 9 | // debug模块 10 | const debug = require('debug')('http') 11 | , http = require('http') 12 | , name = 'My App'; 13 | 14 | // fake app 15 | debug('booting %s', name); 16 | 17 | http.createServer((req, res)=>{ 18 | debug(`${req.method} ${ req.url}`); 19 | res.end('hello'); 20 | }).listen(3000, ()=>{ 21 | debug('listening'); 22 | }); 23 | 24 | // fake worker of some kind 25 | const worker = require('debug')('worker'); 26 | setInterval(()=>{ 27 | worker('doing some work'); 28 | }, 1000); -------------------------------------------------------------------------------- /debug/node-inspector.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author slashhuang 3 | * node-inspector 4 | * debug面板 5 | * https://github.com/node-inspector/node-inspector 6 | * 17/3/28 7 | */ 8 | 9 | const http = require('http'); 10 | console.log('fuck') 11 | http.createServer((req,res)=>{ 12 | debugger; 13 | res.end('10000') 14 | }).listen(3000,()=>{ 15 | console.log('server listen on port 3000') 16 | }) 17 | 18 | -------------------------------------------------------------------------------- /debug/repl.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author slashhuang 3 | * REPL debugger模式 4 | * https://nodejs.org/api/debugger.html 5 | * 17/3/28 6 | */ 7 | /** 8 | Stepping# 9 | 10 | cont, c - Continue execution 11 | next, n - Step next 12 | step, s - Step in 13 | out, o - Step out 14 | pause - Pause running code (like pause button in Developer Tools) 15 | */ 16 | /** 17 | Information# 18 | 19 | backtrace, bt - Print backtrace of current execution frame 20 | list(5) - List scripts source code with 5 line context (5 lines before and after) 21 | watch(expr) - Add expression to watch list 22 | unwatch(expr) - Remove expression from watch list 23 | watchers - List all watchers and their values (automatically listed on each breakpoint) 24 | repl - Open debugger's repl for evaluation in debugging script's context 25 | exec expr - Execute an expression in debugging script's context 26 | */ 27 | /** 28 | Execution control# 29 | 30 | run - Run script (automatically runs on debugger's start) 31 | restart - Restart script 32 | kill - Kill script 33 | */ 34 | 35 | const x = 5; 36 | setTimeout(() => { 37 | debugger; 38 | console.log('world'); 39 | }, 1000); 40 | console.log('hello'); 41 | -------------------------------------------------------------------------------- /events/events.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Much of the Node.js core API is built around 3 | * an idiomatic asynchronous event-driven architecture 4 | * in which certain kinds of objects (called "emitters") periodically emit named events 5 | * that cause Function objects ("listeners") to be called. 6 | * @Author slashhuang 7 | * 17/3/28 8 | */ 9 | 10 | // 简单的pub/sub api 11 | const EventEmitter = require('events'); 12 | 13 | class MyEmitter extends EventEmitter {} 14 | 15 | const myEmitter = new MyEmitter(); 16 | myEmitter.on('event', () => { 17 | console.log('an event occurred!'); 18 | }); 19 | myEmitter.emit('event'); 20 | 21 | /* 22 | * 静态方法 23 | * 监听队列长度 api 24 | * EventEmitter.listenerCount(emitter, eventName) 25 | */ -------------------------------------------------------------------------------- /http/http.md: -------------------------------------------------------------------------------- 1 | ## http.createServer((requset,response)=>res.end())对象分解 2 | 3 | > 基于事件模型的Node.js应用 4 | 5 | ```javascript 6 | let fn = (req,res)=>res.end(); 7 | let server = http.createServer(fn) 8 | ``` 9 | 10 | > 如上代码很容易让众多Node开发者形而上学认为这样work就ok,而忽略它的本质。 11 | 12 | > 实际上上面的代码等价于 13 | 14 | ```javascript 15 | let fn = (req,res)=>res.end(); 16 | let server = http.createServer() 17 | server.on('request',fn) 18 | ``` 19 | 20 | > 上篇专栏文章,我分享了如何采用[Symbol来实现Promise](https://zhuanlan.zhihu.com/p/26003835),可能写的有些晦涩。 21 | 22 | > 今天这篇文章不采用代码模式来分享Node知识。 23 | 24 | > 我把request和response对象按照tree的结构展示了http_out_going、stream、eventEmitter等的继承关系。 25 | 26 | > 文章张文即为request和response对象的树状数据结构,数据结构中我只展示了public方法,私有方法并未列出。 27 | 28 | > 这篇文章的受众是有Node开发经验的读者。 29 | 30 | ## request对象 31 | 32 | > request对象实现了Readable Stream的接口 33 | 34 | ```bash 35 | 36 | |- IncomingMessage 37 | |- |- client 38 | |- |- socket (客户端和server的request socket) 39 | |- |- connection 40 | |- | 41 | |- |- headers 42 | |- |- |- accept:'*/*' 43 | |- |- |- host:'localhost:7000' 44 | |- |- |- user-agent:'curl/7.43.0' 45 | |- | 46 | |- |- httpVersion:"1.1" 47 | |- | 48 | |- |- upgrade: false 49 | |- |- url:"/" 50 | |- | 51 | |- |- |- 原型链 Readable 52 | |- |- |- 53 | |- |- |- constructor: Readable(options) 54 | |- |- |- destroy(error) 55 | |- |- |- read(n) 56 | |- |- |- setTimeout(msecs, callback) 57 | |- |- | 58 | |- |- |- |- 原型链 Stream 59 | |- |- |- |- addListener(event,fn) 60 | |- |- |- |- on(event,fn) 61 | |- |- |- |- pipe(dest, pipeOpts) 62 | |- |- |- |- pause() 63 | |- |- |- |- push(chunk, encoding) 64 | |- |- |- |- read(n) 65 | |- |- |- |- resume() 66 | |- |- |- |- setEncoding(enc) 67 | |- |- |- |- unpipe(dest) 68 | |- |- |- |- unshift(chunk) 69 | |- |- |- |- wrap(stream) 70 | |- |- |- | 71 | |- |- |- |- 原型链 EventEmitter 72 | |- |- |- |- |- addListener(event,fn) 73 | |- |- |- |- |- on(type, listener) 74 | |- |- |- |- |- once(type, listener) 75 | |- |- |- |- |- emit(type) 76 | |- |- |- |- |- getMaxListners() 77 | |- |- |- |- |- listenerCount(type) 78 | |- |- |- |- |- listeners(type) 79 | |- |- |- |- |- prependListener(type,listener) 80 | |- |- |- |- |- prependOnceListener(type,listener) 81 | |- |- |- |- |- removeAllListeners(type) 82 | |- |- |- |- |- removeListener(type,listener) 83 | |- |- |- |- |- setMaxListener(n) 84 | 85 | ``` 86 | ## response 87 | 88 | > response对象实现了Writable Stream的接口,但它并未继承Writable Stream 89 | 90 | ```bash 91 | |- ServerResponse 92 | |- |- socket (客户端和server的request socket) 93 | |- |- connection 94 | |- | 95 | |- |- _headers:null 96 | |- | 97 | |- |- chunkEncoding:false 98 | |- | 99 | |- |- upgrading: false 100 | |- | 101 | |- |- useChunkedEncodingByDefault:true 102 | |- | 103 | |- |- writable:true 104 | |- | 105 | |- |- |- 原型链 OutgoingMessage 106 | |- |- | 107 | |- |- |- constructor: OutgoingMessage 108 | |- |- |- writeHead(statusCode,reason,obj) 109 | |- |- |- writeHeader() 110 | |- |- |- writeContinue(cb) 111 | |- |- |- statusCode:200 112 | |- |- |- statusMessage:undefined 113 | |- |- |- writeContinue(cb) 114 | |- |- | 115 | |- |- |- 原型链 Stream 116 | |- |- |- |- addTrailers(headers) 117 | |- |- |- |- destroy(error) 118 | |- |- |- |- end(data, encoding, callback) 119 | |- |- |- |- write(chunk, encoding, callback) 120 | |- |- |- |- flush() 121 | |- |- |- |- flushHeaders() 122 | |- |- |- |- headerSent 123 | |- |- |- |- removeHeader(name) 124 | |- |- |- |- setHeader(name,value) 125 | |- |- |- |- setTimeout(msecs, callback) 126 | |- |- |- |- pipe(dest,options) 127 | |- |- |- | 128 | |- |- |- |- 原型链 EventEmitter 129 | |- |- |- |- |- addListener(event,fn) 130 | |- |- |- |- |- on(type, listener) 131 | |- |- |- |- |- once(type, listener) 132 | |- |- |- |- |- emit(type) 133 | |- |- |- |- |- getMaxListners() 134 | |- |- |- |- |- listenerCount(type) 135 | |- |- |- |- |- listeners(type) 136 | |- |- |- |- |- prependListener(type,listener) 137 | |- |- |- |- |- prependOnceListener(type,listener) 138 | |- |- |- |- |- removeAllListeners(type) 139 | |- |- |- |- |- removeListener(type,listener) 140 | |- |- |- |- |- setMaxListener(n) 141 | ``` 142 | # 结语 143 | 144 | > 读者如果想实践上面列出的继承关系,可以戳这个[github response](https://github.com/slashhuang/full-stack-practice) 145 | 146 | > 如有理解错误,请在评论区指出 147 | 148 | 149 | 150 | 151 | 152 | -------------------------------------------------------------------------------- /http/incoming.js: -------------------------------------------------------------------------------- 1 | /* 2 | * incoming message for http `request` event handler 1st argument 3 | * @Author slashhuang 4 | * 17/3/28 5 | * 特别说明 ----- 6 | * 对于我们起的Node服务地位是server还是client 7 | * incomingMessage分别对应着事件`request`,`response` 8 | * 这里 ----- 9 | * 我只列举request事件的情形 10 | */ 11 | 12 | 13 | /* 14 | * 实现接口来自于readable Stream 15 | * interface:[Readable Stream](https://github.com/nodejs/node/blob/master/doc/api/stream.md#stream_class_stream_readable) 16 | */ 17 | const http = require('http'); 18 | const server = http.createServer((req,res)=>{ 19 | propertyHook(req) 20 | EventHook(req); 21 | methodHook(req) 22 | res.on('error',()=>{ 23 | console.log(`error happened`) 24 | }) 25 | setTimeout(()=>res.end('1'),2000) 26 | }); 27 | server.listen(7000); 28 | /* 29 | * events 30 | * - aborted 触发时机: 客户端停止请求+socket连接关闭 31 | * - close 触发时机: 一个请求触发一次 32 | */ 33 | const EventHook = req=>{ 34 | // socket层面 35 | req.on('aborted',()=>{ 36 | console.log(`client req aborted`) 37 | }).on('close',()=>{ 38 | console.log(`response done for once`) 39 | }) 40 | /* 41 | * Calls destroy() on the socket that received the IncomingMessage 42 | * 关闭客户端和server端 socket 43 | */ 44 | // req.destroy('fuck') 45 | } 46 | 47 | /* 48 | * properties. 49 | * - headers: The request/response headers object. ==> return object 50 | * - httpVersion ==> return string 51 | * - method ==> return string 52 | * - rawHeaders ==> return array 53 | * - socket ==> return {net.Socket} 54 | * - url 55 | * 56 | */ 57 | const propertyHook = req=>{ 58 | console.log(req.rawHeaders); 59 | //增加socket的error监听。触发顺序 error => aborted => close 60 | req.socket.on('error',()=>console.log('error')) 61 | } 62 | /* 63 | * methods, and properties. 64 | * - setTimeout(msecs, callback) :message.connection.setTimeout(msecs, callback). 65 | * return message 66 | * 67 | */ 68 | const methodHook = req=>{ 69 | //在req的socket,connection建立连接 70 | req.setTimeout(1000,()=>{ 71 | console.log('timeout for 1000') 72 | }) 73 | }; 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /http/listener.js: -------------------------------------------------------------------------------- 1 | /* 2 | * client Request 3 | * @Author slashhuang 4 | * 17/3/28 5 | * 专业术语 6 | * payload:https://en.wikipedia.org/wiki/Payload_(computing) 7 | */ 8 | 9 | /* 10 | *class: http.Server类 11 | http.createServer([requestListener]) 12 | Returns: {http.Server} 13 | The requestListener is a function 14 | which is automatically added to the 'request' event. 15 | 回调函数自动添加进request事件回调 16 | */ 17 | const http = require('http'); 18 | const server = http.createServer(); 19 | //If a client connection emits an 'error' event, it will be forwarded here 20 | server.on('clientError', (err, socket) => { 21 | socket.end('HTTP/1.1 400 Bad Request\r\n\r\n'); 22 | }); 23 | /* 24 | * When a new TCP stream is established. 25 | * socket is an object of type net.Socket 26 | */ 27 | server.on('connection', socket => { 28 | console.log(`connection comes in`) 29 | }); 30 | /* 31 | * request {http.IncomingMessage} 32 | * response {http.ServerResponse} 33 | * Emitted each time there is a request. 34 | * Note that there may be multiple requests per connection 35 | */ 36 | server.on('request',(req, res) => res.end('hello wrold')); 37 | server.on('request',(request,response) => { 38 | let {url,method} = request; 39 | console.log(`request metadata is ${method} ${url} `); 40 | // close server 41 | // server.close(()=>process.stdout.write('server closed')); 42 | }); 43 | 44 | 45 | 46 | server.listen(7000); 47 | 48 | 49 | //研究server的监听 50 | let listenerCounter = server.listenerCount('request'); 51 | // prints 2 52 | console.log(listenerCounter); 53 | // returns http.createServer的回调 54 | console.log(server.listeners('request')[0].toString()) 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /http/response.js: -------------------------------------------------------------------------------- 1 | /* 2 | * http.ServerResponse 3 | * It is passed as the second parameter to the 'request' event 4 | * @Author slashhuang 5 | * 17/3/28 6 | * The response implements, but does not inherit from, the Writable Stream interface 7 | */ 8 | 9 | /* 10 | * 实现接口来自于 Writable Stream 11 | * interface:[Writable Stream](https://github.com/nodejs/node/blob/master/doc/api/stream.md#stream_class_stream_writable) 12 | */ 13 | const http = require('http'); 14 | const server = http.createServer((req,res)=>{ 15 | propertyHook(res) 16 | EventHook(res); 17 | methodHook(res) 18 | }); 19 | server.listen(7000); 20 | /* 21 | * events 22 | * - close 23 | Indicates that the underlying connection was terminated before response.end() 24 | * - finish 触发时机: 响应返回,这是response最后触发的事件 25 | */ 26 | const EventHook = res=>{ 27 | res.on('close',()=>{ 28 | //示例case:客户端在手动kill socket的时候,比如ctrl+c执行curl, 29 | console.log('response terminated') 30 | }).on('finish',()=>{ 31 | console.log('response sent done') 32 | }) 33 | }; 34 | /* 35 | * methods, 36 | * - addTrailers(headers) : 37 | adds HTTP trailing headers (a header but at the end of the message) to the response. 38 | * return message 39 | * - end([data][, encoding][, callback]) 40 | * 每个请求必须调用,是write(data,encoding)+ res.end(callback)的facade 41 | * - write(chunk[, encoding][, callback]) 42 | * - writeHead(statusCode[, statusMessage][, headers]) 43 | * - getHeader(name) 44 | * 读取queued header 45 | * - removeHeader(name) 46 | * Removes a header that's queued for implicit sending. 47 | * - setHeader(name, value) 48 | * - writeHead(statusCode,message,headers) 49 | */ 50 | const methodHook = response=>{ 51 | //读取queued header 52 | response.setHeader('Foo', 'bar'); 53 | response.setHeader('Set-Cookie', ['foo=bar', 'bar=baz']); 54 | console.log(response.getHeader('Foo')); 55 | 56 | response.writeHead(200,'ok',{hello:'world'}) 57 | response.end('hello') 58 | }; 59 | /* 60 | * properties, 61 | * - finished : 62 | indicates whether the response has completed 63 | * - statusCode 64 | * - statusMessage 65 | */ 66 | const propertyHook = response=>{ 67 | console.log(response.finished); 68 | 69 | }; 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "full-stack", 3 | "version": "1.0.0", 4 | "description": "a full-stack-practice-for-node", 5 | "main": "index.js", 6 | "scripts": { 7 | "//": "Node核心模块及操作系统", 8 | "listener": "nodemon --inspect ./http/listener", 9 | "incoming": "nodemon --inspect ./http/incoming", 10 | "response": "nodemon --inspect ./http/response", 11 | "v-engine": "nodemon ./view-engine/index.js", 12 | "debug": "DEBUG=http,worker nodemon ./debug/debug.js", 13 | "inspector": "nodemon ./node_modules/.bin/node-debug ./debug/node-inspector.js", 14 | "repl": "nodemon debug ./debug/repl.js", 15 | "stream": "nodemon ./stream/stream.js", 16 | "buffer": "nodemon ./buffer/buffer.js", 17 | "v8": "nodemon ./v8/v8.js", 18 | "process": "nodemon ./process/process.js", 19 | "daemon": "node ./daemon/daemon", 20 | "test": "nodemon ./tmp" 21 | }, 22 | "repository": { 23 | "type": "git", 24 | "url": "git+https://github.com/slashhuang/full-stack-practice.git" 25 | }, 26 | "keywords": [ 27 | "node", 28 | "posix" 29 | ], 30 | "author": "slashhuang", 31 | "license": "ISC", 32 | "bugs": { 33 | "url": "https://github.com/slashhuang/full-stack-practice/issues" 34 | }, 35 | "homepage": "https://github.com/slashhuang/full-stack-practice#readme", 36 | "devDependencies": { 37 | "debug": "^2.6.3", 38 | "node-inspector": "^1.0.0", 39 | "nodemon": "^1.11.0" 40 | }, 41 | "dependencies": { 42 | "ejs": "^2.5.6" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /process/process.js: -------------------------------------------------------------------------------- 1 | /* 2 | * process 对象 3 | * @Author slashhuang 4 | * 17/3/30 5 | */ 6 | 7 | process.stdin.setEncoding('utf8'); 8 | 9 | process.stdin.on('readable', () => { 10 | var chunk = process.stdin.read(); 11 | if (chunk !== null) { 12 | process.stdout.write(`data: ${chunk}`); 13 | process.stdin.emit('end') 14 | } 15 | }); 16 | 17 | process.stdin.on('end', () => { 18 | process.stdout.write('end'); 19 | }); 20 | 21 | console.log('testing') -------------------------------------------------------------------------------- /stream/stream.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author slashhuang 3 | * 17/3/29 4 | * Stream in Node.js 5 | * Almost all Node.js applications, 6 | * no matter how simple, use streams in some manner 7 | */ 8 | 9 | /* 核心理念: 用来处理流式数据的接口 10 | * A stream is an abstract interface 11 | * for working with streaming data in Node.js 12 | * Not all streams will emit the 'close' event 13 | */ 14 | 15 | /* 继承关系: 16 | * Streams can be readable, writable, or both. 17 | * All streams are instances of EventEmitter. 18 | */ 19 | const stream = require('stream'); 20 | 21 | //----------------代码实践区域-------------------- 22 | 23 | /* 24 | * API for Stream Consumers 25 | * 测试例子:curl localhost:7000 -d "hello world" 26 | */ 27 | /* 28 | * Readable Streams对象 29 | * 两种模式 :flow mode + paused mode 30 | * 模式切换 31 | * 'data'事件 32 | * stream.resume()方法 33 | * stream.pipe()方法 34 | * 35 | * 示例 36 | * HTTP requests, on the server 37 | * fs read streams 38 | * zlib streams 39 | * crypto streams 40 | * TCP sockets 41 | * child process stdout and stderr 42 | * process.stdin 43 | ** 44 | * 事件 45 | * - close 46 | * - data 47 | * - end 48 | * 49 | * -readable 50 | * 'readable' event indicates that the stream has new information: 51 | * either new data is available or the end of the stream has been reached 52 | ** 53 | * 方法 54 | * - pause 55 | * - resume 56 | * - isPaused 57 | * - pipe 58 | * > 自动转换stream模式为flow 59 | * > returns a reference to the destination stream 60 | * 61 | */ 62 | // 结果 63 | //readable 64 | // chunking 65 | // pull data 66 | // readable 67 | // pull data null 68 | // end 69 | const http = require('http'); 70 | http.createServer((req,res)=>{ 71 | let body = ''; 72 | // Any data that becomes available will remain in the internal buffer 73 | req.pause(); 74 | // 5秒后 switching the stream into flowing mode 75 | setTimeout(()=>req.resume(),5000) 76 | // consume data 77 | req.on('data',(chunk)=>{ 78 | console.log('chunking',chunk) 79 | body+=chunk 80 | }).on('readable',()=>{ 81 | console.log('readable') 82 | // consume data 83 | //pulls some data out of the internal buffer and returns it. 84 | console.log('pull data',req.read()) 85 | }).on('end',()=>{ 86 | console.log('end') 87 | res.end(body) 88 | }).on('close',()=>{ 89 | console.log('end') 90 | }); 91 | }).listen(7000) 92 | /* 93 | * Writable streams 暴露了write/end来写入数据 94 | * Readable streams 采用EventEmitter来通知数据的读取状态 95 | * 96 | * stream状态读取 97 | * 无论是Readable还是Writable都采用EventEmitter来沟通暴露数据状态 98 | * 99 | * 大多数应用的stream读取状态都不需要到require('stream')这一层。 100 | */ 101 | /* 102 | * writable Stream举例 103 | * HTTP responses, on the server 104 | * fs write streams 105 | * zlib streams 106 | * crypto streams 107 | * TCP sockets 108 | * child process stdin==> stdout && stderr 109 | */ 110 | /* 111 | * writable Stream事件 112 | * - close 113 | * - drain 114 | * stream.write(chunk) returns false, 115 | * the 'drain' event will be emitted 116 | * when it is appropriate to resume writing data to the stream. 117 | * - pipe 118 | * emitted when the stream.pipe() method is called 119 | * 120 | */ 121 | function writeOneMillionTimes(writer, data, encoding, callback) { 122 | let i = 100000; 123 | writer.once('drain', ()=>write('resuming--')); 124 | write(); 125 | function write(message) { 126 | var ok = true; 127 | do { 128 | i--; 129 | if (i === 0) { 130 | writer.write(data, encoding, callback); 131 | } else { 132 | // 998509-resuming-- 133 | message && writer.write(message, encoding)&&console.log(`${i}-${message}`); 134 | ok = writer.write(data, encoding); 135 | } 136 | } while (i > 0 && ok); 137 | } 138 | }; 139 | let fs = require('fs').createWriteStream('./tmp'); 140 | // 141 | //writeOneMillionTimes(fs,'hello world','utf8',()=>console.log('done')) 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | -------------------------------------------------------------------------------- /stream/stream.md: -------------------------------------------------------------------------------- 1 | ## stream in Node.js 2 | 3 | > Almost all Node.js applications,no matter how simple, use streams in some manner. 4 | 5 | ## 核心理念: 用来处理流式数据的接口 6 | 7 | > A stream is an abstract interface 8 | > for working with streaming data in Node.js 9 | > Not all streams will emit the 'close' event 10 | 11 | ## 继承关系: 12 | 13 | > Streams can be readable, writable, or both. 14 | > All streams are instances of EventEmitter. 15 | 16 | ## Stream四大基础类型 17 | 18 | > Readble :例子fs.createReadStream() 19 | 20 | > Writable:例子fs.createWriteStream() 21 | 22 | > Duplex =>both readablea and writable :例子 net.Socket 23 | 24 | > Transform:=>Duplex stream 能够不断在读写过程中修改:例子zlib.createDeflate 25 | 26 | ## Stream数据模型 27 | 28 | - highWaterMark: stream容量 29 | 30 | - internal buffer: stream暂存区 31 | 32 | ## Buffer存储 33 | > stream在内部由Buffer存储, 34 | > highWaterMark => Buffer容量 35 | 36 | - ReadableStream : 37 | 38 | > stream.push(chunk) 39 | > => chunk goto buffer 40 | > stream.read() 41 | > => chunk get consumed 42 | > 43 | 44 | > 当Buffer的大小>highWaterMark时, 45 | > stream将会停止执行readable._read() 46 | 47 | > 直到Buffer被consume至highWaterMark底下 48 | 49 | >/ 50 | 51 | - WritableStreams 52 | 53 | > 内部逻辑和Readable一致 54 | > writable.write(chunk) 55 | 56 | > => if < highWaterMark @return true 57 | 58 | > => if >= highWaterMark @return false 59 | 60 | > 61 | 62 | - 调节API stream.pipe 63 | 64 | > pipe方法的主要作用在于调节输入输出双方。 65 | > 当输入和输出的速度不一致时,由pipe来调节双方。 66 | > 67 | 68 | - Duplex and Transform 69 | 70 | > 核心逻辑在于read write两端各自设置Buffer。 71 | > 两端对buffer的操作彼此独立 72 | 73 | 74 | ## Stream api结构 75 | ```bash 76 | 77 | |- Writable Streams 78 | |- |- EVENTS 79 | |- |- |- close 80 | |- |- |- drain 81 | |- |- |- error 82 | |- |- |- finish 83 | |- |- |- pipe 84 | |- |- |- unpipe 85 | |- |- | 86 | |- |- Methods 87 | |- |- |- end([chunk][, encoding][, callback]) 88 | |- |- |- cork 89 | |- |- |- setDefaultEncoding(encoding) 90 | |- |- |- uncork() 91 | |- |- |- write(chunk[, encoding][, callback]) 92 | 93 | 94 | |- Readable Streams 95 | |- |- MODES 96 | |- |- |- flow mode 97 | |- |- |- pause mode 98 | |- |- | 99 | |- |- State 100 | |- |- |- _readableState.flowing = null 101 | |- |- |- _readableState.flowing = false 102 | |- |- |- _readableState.flowing = true 103 | |- |- | 104 | |- |- EVENTS 105 | |- |- |- close 106 | |- |- |- data 107 | |- |- |- end 108 | |- |- |- error 109 | |- |- |- readable 110 | |- |- | 111 | |- |- Methods 112 | |- |- |- isPaused() 113 | |- |- |- pause() 114 | |- |- |- pipe(destination[, options]) 115 | |- |- |- read([size]) 116 | |- |- |- resume() 117 | |- |- |- setEncoding(encoding) 118 | |- |- |- pipe(destination[, options]) 119 | |- |- |- unpipe([destination]) 120 | |- |- |- unshift(chunk) 121 | 122 | ``` 123 | 124 | 125 | 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /v8/v8.js: -------------------------------------------------------------------------------- 1 | /* 2 | * V8知识学习 3 | * @Author slashhuang 4 | * 17/3/30 5 | */ 6 | 7 | const v8 = require('v8'); 8 | /* 9 | * 返回v8的堆组成部分 10 | * [ { space_name: 'new_space', 11 | space_size: 2097152, 12 | space_used_size: 651728, 13 | space_available_size: 379952, 14 | physical_space_size: 2097152 }, 15 | { space_name: 'old_space', 16 | space_size: 2052096, 17 | space_used_size: 1559616, 18 | space_available_size: 776, 19 | physical_space_size: 2052096 }, 20 | { space_name: 'code_space', 21 | space_size: 2097152, 22 | space_used_size: 649728, 23 | space_available_size: 96, 24 | physical_space_size: 2097152 }, 25 | { space_name: 'map_space', 26 | space_size: 1130496, 27 | space_used_size: 210056, 28 | space_available_size: 0, 29 | physical_space_size: 1130496 }, 30 | { space_name: 'large_object_space', 31 | space_size: 0, 32 | space_used_size: 0, 33 | space_available_size: 1489972736, 34 | physical_space_size: 0 } ] 35 | 36 | */ 37 | console.log(v8.getHeapSpaceStatistics()) 38 | /* 39 | { total_heap_size: 7376896, 40 | total_heap_size_executable: 5242880, 41 | total_physical_size: 7376896, 42 | total_available_size: 1490292928, 43 | used_heap_size: 3516352, 44 | heap_size_limit: 1501560832 } 45 | */ 46 | 47 | console.log(v8.getHeapStatistics()) 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /v8/v8.md: -------------------------------------------------------------------------------- 1 | # v8 2 | 3 | [chrome v8官方](https://developers.google.com/v8/) -------------------------------------------------------------------------------- /view-engine/ejs.js: -------------------------------------------------------------------------------- 1 | /* EJS模板引擎测试 2 | */ 3 | 4 | const path =require('path'); 5 | const ejs = require('ejs'); 6 | const input = `<%- $.hello %> 7 | <%- include('test') %> 8 | <% $.pets.forEach(function (pet) { %> 9 |
  • <%= pet.name %>
  • 10 | <% }) %> 11 | <> 12 | <%% hello world %%> 13 | 14 | 15 | <%- $.script %>` 16 | 17 | /* <%= %>的作用 18 | 过滤敏感字符 比如<、> 19 | var _ENCODE_HTML_RULES = { 20 | '&': '&', 21 | '<': '<', 22 | '>': '>', 23 | '"': '"', 24 | "'": ''' 25 | }; 26 | */ 27 | /* <%- %>的作用 28 | 不过滤敏感字符 29 | */ 30 | let func = ejs.compile(input, { 31 | compileDebug:true, 32 | _with:false, 33 | filename:path.resolve(__filename), //所有include的路径相对这个路径 34 | localsName:'$' 35 | }); 36 | 37 | let output = func({ 38 | hello:'world', 39 | script:'', 40 | "pets": [ 41 | { 42 | "name": "Hazel" 43 | } 44 | , { 45 | "name": "Crystal" 46 | } 47 | , { 48 | "name": "Catcher" 49 | } 50 | ] 51 | }); 52 | console.log(output); 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /view-engine/ejs.md: -------------------------------------------------------------------------------- 1 | ## EJS原理浅析 2 | 3 | [ejs的github源码](https://github.com/mde/ejs) 4 | 5 | EJS的模板引擎实现分为标签语法解析和函数动态性注入两块。 6 | 7 | ### 代码示例 8 | 9 | ```js 10 | 11 | const input = `<%- $.hello %> 12 | <%- include('test') %> 13 | 14 | <%- $.script %>` 15 | 16 | ``` 17 | 18 | 19 | EJS采用XML开闭标签和delimeter来标示需要动态渲染的数据。 20 | 21 | ### 基本的语法架构: 22 | 23 | 1、Delimiters 分割符 24 | 25 | 2、开始标记 26 | 27 | ```js 28 | <%=: Escaped output 29 | <%-: Unescaped output 30 | <%#: Comments 31 | <%: Scriptlet 32 | <%_: Scriptlet, removes all preceeding whitespace 33 | ``` 34 | 35 | 3、闭合标记 36 | 37 | ```js 38 | %>: Regular ending tag 39 | -%>: Removes trailing newline 40 | _%>: Removes all trailing whitespace 41 | 42 | ``` 43 | 44 | 45 | 4、语义化标记 46 | 47 | ```js 48 | 转义 <%% 会变为 <% 49 | 50 | ``` 51 | 52 | 5、文件标记 53 | 54 | ```js 55 | <%- include(filename, [locals]) %> 56 | 57 | ``` 58 | 59 | 60 | ### 采用new Function生成动态代码块 61 | 62 | [new Function参考资料](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function) 63 | 64 | ```js 65 | // returns 6 66 | new Function('a','b','c',"return a+ b +c")(1,2,3) 67 | 68 | ``` 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /view-engine/index.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | /* 4 | * @Author slashhuang 5 | * 模板引擎 6 | */ 7 | 8 | require('./ejs'); -------------------------------------------------------------------------------- /view-engine/test.ejs: -------------------------------------------------------------------------------- 1 | 这是一个测试include 2 | 这是一个测试include 3 | 4 | 5 | --------------------------------------------------------------------------------