此栏最终目标
认知Buffer认知Stream认知Eventsnode中的自上而下第一类科学知识关键点
认知BufferArrayBuffer通用型的、一般来说宽度的原初十进制统计数据头文件,不能间接操作方式,而要透过类别字符串第一类(TypedArray)操作方式,将头文件中的Luzy为某一的文件格式。能将其认知为几块缓存,具体文本存甚么须要其它的新闻稿const buffer = new rrayBuffer(8)
console.log(buffer) // ArrayBuffer { [Uint8Contents]:<00 00 00 00 00 00 00 00>,[byteLength]:8 }
const int16Buffer = new Int16Array(buffer)
console.log(int16Buffer) // Int16Array(4) [0,0,0,0]
2. Unit8Array
则表示两个8位的无记号auth字符串,建立的这时候文本被调用为0const unit8 = new Uint8Array(2)
unit8[0] = 42
console.log(unint8[0]) // 42
console.log(unit8.length) // 2console.log(unit8.BYTES_PER_ELEMENT) // 1
const arr = new Uint8Array([21,31])
console.log(arr[1]) // 31
3. ArrayBuffer与TypedArray的关系
TypedArray:Uint8Array, Int32Array, Int16Array
ArrayBuffer: 本身是两个0、1存放在一行里的集合
关系:使用TypedArray划分ArrayBuffer中的十进制字节,4. node中使用Buffer
Buffer实现了Uint8Array的api
Buffer的实例 => auth字符串 (用于则表示编码字符的序列 UTF-8 Base64 十六进制)
Buffer的大小是一般来说的,在建立时就确定了,无法调整const buf1 = Buffer.alloc(10) // 建立两个宽度为10 填充为0的Buffer
console.log(buf1) // <Buffer 00 00 00 00 00 00 00 00 00 00>
const buf2 = Buffer.alloc(10,1) // 建立两个宽度为10 填充为1的Bufferconsole.log(buf2) // <Buffer 01 01 01 01 01 01 01 01 01 01 >
const buf3 = Buffer.allocUnsafe(10) // 建立两个宽度为10, 速度比alloc快 但是建立的头文件里可能有旧统计数据
console.log(buf3) // <Buffer d8 0b 01 06 00 00 00 00 08 da>
const buf = Buffer.from(hellow world, ascii) // 参数1:生成文本 参数2:文本的编码方式
console.log(but) // <Buffer 68 65 6c 6f 20 77 6f 72 6c 64>console.log(buf.toString(base64)) // …base64编码的字符串
认知Streamshell透过管道连接各个部分,输入和输出的规范是文本流
nodejs内置的Stream模块实现了类似的功能,各个部分之间透过pipe()连接
流中的统计数据默认情况下都是Buffer类别。产生的统计数据一放入流中,便转成Buffer被消耗;写入的统计数据在传给底层写逻辑时,也被转成Buffer类别。Stream.Readable 可读建立可读流时须要继承Readable并实现_read方法
_read方法是从底层系统读取具体文本统计数据的逻辑,即生产统计数据的逻辑。 在_read方法中,透过调用push(data)将统计数据放入可读流中供下游消耗。 在_read方法中,能同步调用push(data),也能异步调用。 当全部统计数据都生产出来后,必须调用push(null)来结束可读流。 流一旦结束,便不能再调用push(data)添加统计数据。const Readable = require(stream).Readable
class ToReadable extends Readable {
constructor(){
super()
this.iterator = iterator
}
_read(){
const res = this.iterator.next()
if(res.done){
return this.push(null)
}
setTimeout(()=>{
this.push(res.value)
},0)
}
}
const iterator = function(limit){
return {
next: function(){
if(limit–){ return {done:false,value:limit+Math.random()} }
return { done: true }
}
}
}(10)
const readable = new ToReadable(iterator)
readable.on(data, data=>{process.stdout.write(data)})
readaable.on(end, ()=>{process.stdout.write(DONE)})
2. Stream.Writeable 可写
Readable透过继承的方式去建立一类可读流,Writeable也适用于建立一类可写流,只是须要实现的是write(data, enc, next)方法,而不是read()方法
上游透过调用writable.write(data)将统计数据写入可写流中。write()方法会调用_write()将data写入底层。在_write中,当统计数据成功写入底层后,必须调用next(err)告诉流开始处理下两个统计数据。next的调用既能是同步的,也能是异步的。上游必须调用writable.end(data)来结束可写流,data是可选的。此后,不能再调用write新增统计数据。在end方法调用后,当所有底层的写操作方式均完成时,会触发finish事件。const Writable = require(stream).Writable
const writable = Writable()
// 实现`_write`方法
// 这是将统计数据写入底层的逻辑
writable._write = function (data, enc, next) {
// 将流中的统计数据写入底层
process.stdout.write(data.toString().toUpperCase())
// 写入完成时,调用`next()`方法通知流传入下两个统计数据
process.nextTick(next)
}
// 所有统计数据均已写入底层writable.on(finish, () => process.stdout.write(DONE))
// 将两个统计数据写入流中
writable.write(a + \n)
writable.write(b + \n)
writable.write(c + \n)
// 再无统计数据写入流时,须要调用`end`方法
writable.end()
3. Stream.Duplex 可读可写
Duplex实际上就是继承了Readable和Writable的一类流。 所以,两个Duplex第一类既可当成可读流来使用(须要实现read方法),也可当成可写流来使用(须要实现write方法)。
var Duplex = require(stream).Duplex
var duplex = Duplex()
// 可读端底层读取逻辑
duplex._read = function () {
this._readNum = this._readNum || 0
if (this._readNum > 1) {
this.push(null)
} else {
this.push( + (this._readNum++))
}
}
// 可写端底层写逻辑
duplex._write = function (buf, enc, next) {
// a, b
process.stdout.write(_write + buf.toString() + \n)
next()
}
// 0, 1
duplex.on(data, data => console.log(ondata, data.toString()))
duplex.write(a)
duplex.write(b)
duplex.write(x)
duplex.end()
4. Stream.Transform 可读可写可变换
Duplex的例子中,可读流中的统计数据(0, 1)与可写流中的统计数据(’a’, ‘b’)是隔离开的,但在Transform中可写端写入的统计数据经变换后会自动添加到可读端。 Tranform继承自Duplex,并已经实现了read和write方法,同时要求用户实现两个_transform方法。
const Transform = require(stream).Transform
class Rotate extends Transform {
constructor(n) {
super()
// 将字母移动`n`个位置
this.offset = (n || 13) % 26
}
// 将可写端写入的统计数据变换后添加到可读端 _transform(buf, enc, next) {
var res = buf.toString().split().map(c => {
var code = c.charCodeAt(0)
if (c >= a && c <= z) {
code += this.offset
if (code > z.charCodeAt(0)) {
code -= 26
}
} else if (c >= A && c <= Z) {
code += this.offset
if (code > Z.charCodeAt(0)) {
code -= 26
}
}
return String.fromCharCode(code)
}).join()
// 调用push方法将变换后的统计数据添加到可读端 this.push(res)
// 调用next方法准备处理下两个
next()
}
}
var transform = new Rotate(3)
transform.on(data, data => process.stdout.write(data))
transform.write(hello, )
transform.write(world!)
transform.end()
认知Eventsevents模块是node的核心模块之一,几乎所有常用的node模块都继承了events模块,比如http、fs等。
注册事件监听器前,事件先触发,则该事件会间接被忽略单个事件监听器// 事件触发时,事件监听器按照注册的顺序执行var EventEmitter = require(events);
class Man extends EventEmitter {}
var man = new Man();
man.on(wakeup, function(){
console.log(man has woken up);
});
man.emit(wakeup); // man has woken up
2. 同个事件 多个事件监听器
var EventEmitter = require(events);
class Man extends EventEmitter {}
var man = new Man();
man.on(wakeup, function(){
console.log(man has woken up);
});
man.on(wakeup, function(){
console.log(man has woken up again);
});
man.emit(wakeup);
3. 只运行一次的事件监听器
var EventEmitter = require(events);
class Man extends EventEmitter {}
var man = new Man();
man.on(wakeup, function(){
console.log(man has woken up);
});
man.once(wakeup, function(){
console.log(man has woken up again);
});
man.emit(wakeup);
man.emit(wakeup);
// 输出如下:
// man has woken up
// man has woken up again// man has woken up
4. 移除事件监听
var EventEmitter = require(events);
function wakeup(){
console.log(man has woken up);
}
class Man extends EventEmitter {}
var man = new Man();
man.on(wakeup, wakeup);
man.emit(wakeup);
man.removeListener(wakeup, wakeup);
man.emit(wakeup);
// 输出如下:
// man has woken up
node中的自上而下第一类Node.js 中的自上而下第一类是 global,所有自上而下变量(除了 global 本身以外)都是 global 第一类的属性。
global 最根本的作用是作为自上而下变量的宿主
自上而下变量的定义:在最外层定义的变量;自上而下第一类的属性;隐式定义的变量(未定义间接赋值的变量)。// node中的this其实是module.exports
console.log(this); // {}
module.exports.foo = 5;
console.log(this); // { foo:5 }
自上而下变量__filename__filename 则表示当前正在执行的脚本的文件名。它将输出文件所在位置的绝对路径,且和命令行参数所指定的文件名不一定相同。 如果在模块中,返回的值是模块文件的路径。
2. 自上而下变量__dirname
__dirname 则表示当前执行脚本所在的目录。
3. 自上而下方法 setTimeout(cb, ms), clearTimeout, setInterval, clearInterval, console,
4. 自上而下变量 process
用于描述当前Node.js 进程状态的第一类,提供了两个与操作方式系统的简单接口。
// process 对象的一些最常用的成员方法
// exit 当进程准备退出时触发。
process.on(exit, function(code) {})
// beforeExit
当 node 清空事件循环,并且没有其它安排时触发这个事件。通常来说,当没有进程安排时 node 退出,但是 ‘beforeExit’ 的监听器能异步调用,这样 node 就会继续执行。
process.on(beforeExit, function(code) {})
// uncaughtException
当两个异常冒泡回到事件循环,触发这个事件。如果给异常添加了监视器,默认的操作方式(打印堆栈跟踪信息并退出)就不会发生。
process.on(uncaughtException, function(code) {})
// Signal
当进程接收到信号时就触发。信号列表详见标准的 POSIX 信号名,如 SIGINT、SIGUSR1 等。
process.on(Signal, function(code) {})
// 输出到终端
process.stdout.write(“Hello World!” + “\n”);
// 透过参数读取
process.argv.forEach(function(val, index, array) {
co // 平台信息
console.log(process.platform);
补充
1字节(byte)= 8位(bit)js中Number 64位 => 8bytenode中目前支持的字符编码 [ 7位的ascii统计数据 | 多字节编码的Unicode字符.html | base64 ]退出时的状态码Uncaught Fatal Exception 有未捕获异常,并且没有被域或 uncaughtException 处理函数处理。
3 Internal JavaScript Parse Error JavaScript的源码启动 Node 进程时引起解析错误。非常罕见,仅会在开发 Node 时才会有。
4 Internal JavaScript Evaluation Failure JavaScript 的源码启动 Node 进程,评估时返回函数失败。非常罕见,仅会在开发 Node 时才会有。
5 Fatal Error V8 里致命的不可恢复的错误。通常会打印到 stderr ,文本为: FATAL ERROR
6 Non-function Internal Exception Handler 未捕获异常,内部异常处理函数不知为何设置为on-function,并且不能被调用。
7 Internal Exception Handler Run-Time Failure 未捕获的异常, 并且异常处理函数处理时自己抛出了异常。例如,如果 process.on(‘uncaughtException’) 或 domain.on(‘error’) 抛出了异常。
9 Invalid Argument 可能是给了未知的参数,或者给的参数没有值。
10 Internal JavaScript Run-Time Failure JavaScript的源码启动 Node 进程时抛出错误,非常罕见,仅会在开发 Node 时才会有。
12 Invalid Debug Argument 设置了参数–debug 和/或 –debug-brk,但是选择了错误端口。