Node
Node 是以 Js
为基础扩展而来的一门后端语言。node
Js
此处仅对 Js
作一些简单介绍。ios
基础
$tag$ |
解释 |
备注 |
语言 |
解释类型,脚本语言 |
语法类似于 Java |
变量 |
弱类型,区分基础-引用类型 |
let,const,var,无 |
boolean
,number
,string
基础类型。
array
,object
,function
引用类型。
$typeof$ |
示例 |
备注 |
空值 |
var x = null; |
亦被定义为 undefined |
布尔 |
var x = false, y = true; |
注意弱类型隐式转化 |
数值 |
var x = 1, y = 1.2; |
默认以 $double$ 位存储 |
字符串 |
var x= "string"; |
配合 buffer 处理字节流 |
函数 |
var x = function() {}; |
|
对象 |
var x = new Object (); |
也能够看成表用 |
数据结构 |
var x = [], y = {}; |
数组,对象 |
&&
||
默认短路执行,!
复合时,通常要加()
。
- 比较运算存在隐式转化,
===
则仅在类型与数值相同时才返回 $true$。
$boolean$ |
枚举 |
$true$ |
非零值 ,定义值 ,true |
$false$ |
null ,undefined ,0 ,0.0 ,false |
语法
assert(ok)
,当ok = false
时,中断进程输出异常,仅用于 Debug。
console.time("label")
,记录程序执行时间,且记录器自己亦会消耗时间。
try catch finally
throw
,捕获异常,但异常类型无限定,注意避免异常重叠致使 Bug。
// 断言,中断进程,输出异常
const assert = require("assert");
assert(false);
// 测试代码执行速度
console.time("act");
// running()
console.timeEnd("act");
// 标准异常处理
try {
// try catch finally
// 实际亦可用 throw,主动抛出异常
} catch (err) {
console.log(err);
return -1;
} finally {
return 0;
}
// 经典 if else
if (true) {
console.log("true");
} else {
console.log("false");
}
// 二元选择
let ret = true ? 1 : 0;
// 经典 while 循环
while (true) {
console.log("foreach");
if (true) {
break;
}
}
// 经典 for 循环
for (let i = 0; i < 10; i++) {
console.log(i);
}
// 经典 switch 循环
switch (id) {
case 0:
break;
default:
break;
}
- 通用的传值规则,基础执行深复制,引用执行浅复制。
- 函数的传参过程,一样执行通用传值规则。
- 面向函数编程,以函数为参数的一种开发思路。
// 函数参数传递
function demo (dat) {
// dat 根据类型,肯定传值规则
// dat 参数传递完成,局部变量
console.log(dat);
}
// 函数参数域
let dat = 100;
function demo () {
// dat 无需参数传递,全局变量
console.log(dat);
}
// 闭包函数
function demo (dat) {
// 此时 dat参数会传递给闭包,而外部全局变量,则不作处理
return function () {
// 以父级函数参数域为基准,保存局部变量,不处理全局变量
console.log(dat);
}
}
// 语法糖
let demo = () => {}
let demo = function () {}
数据结构
- 可将Js-对象,Lua-表,视为同一类东西。
- 二者并没有过大差距,操做核心都是 hash映射。
- 对象和数组的基准访问速度相差不是特别大。
- 对象通常不推荐构建过于臃肿的键值对,数组则无限制。
// 对象属性三种定义,新增方式
let tab = { a : true };
tab.b = 100; // 标准方式建立字段
tab[" "] = 1; // key 合法字符串均可做为键,包含空格,特殊字符
tab[123] = 2; // key 为数字,则会被隐式转化为字符串
tab.a = null; // 此处仅会释放 value 所占内存,key所占内存不会被释放
delete tab.a; // 完全删除,key-value 键值对,但执行效率较低
// 通常用此法迭代对象
for (let idx in arr) {
// 速度较慢,适用于数组,对象
}
// 数组
let arr = [];
let len = arr.length;
arr.push("hello world!");
let len = arr.len;
for (let i = 0; i < len; i++) {
// 速度最快的迭代
}
arr.forEach(function(val, idx, all){
// 速度适中的迭代,适用于数组
})
for (let val of arr) {
// 速度较慢,适用于数组
}
- Map,就是一种特殊的 key-val数据结构,由
{}
向上封装而来。
- Set,是一种无重复,无序的数据结构,由
[]
向上封装而来。
// 初始化 Map 对象
const map = new Map();
// 删除指定 键值对,不存在时跳过无异常
map.delete("key"); // delete map["key"]; 等同操做
// 新增或覆盖指定 键值对,key 合法字符串便可,包括空格与特殊字符
map.set("key", {});
// 检测是否存在 键位,存在 true,不存在 false
map.has("key");
// 获取指定 键位的 value,不存在返回 null
map.get("key");
// 用对象的方式迭代 map
for (let idx in map) {
console.log(map[idx]);
}
// 初始化 Set 对象
const set = new Set(); // new Set([1,2,3]); 利用数组快速初始化
// 删除set 中指定元素,不存在则跳过
set.delete(12);
// set 集合,插入元素时作强等于判断。
set.add(12);
set.add('12');
// 用数组的方式迭代 set
for (let val of set) {
console.log(val);
}
// 删除全部,内容
obj.clear();
// 数据结构,大小
obj.size;
node
此处主要为 Js 扩展向 node的特性。shell
模块
npm
是 node
自带的包管理器。
node -v
npm -v
# 初始化操做
npm init
# 生成指定的 package.json,保存配置,可用于版本控制
npm install package --save
npm uninstall package --save
npm i # 当项目存在 package 时,仅需执行 install 便可
- node本质上只是一种异步编程思想,执行效率在 v8引擎下也不至于太慢。
- 32 位系统下,node 默认700Mb最佳,64位则 1400Mb最佳。
- 且当内存超出范围时,进程会崩溃,故避免内存超过 1400 MB。
- 但也可在启动时,扩充最大内存空间,保证服务不至于宕机,但会影响性能。
V8
1. V8引擎下的GC,主要靠分代机制,即新生代,老生代。
2. 新生代,又分为 from,to 区,Java中比例为 (from:to:old = 1:1:8)。
3. 新生代,若内存块超过区域的 25%,会直接进入老生代空间。
3. 新生代,from 中存活的内存块,会被复制到 to 区域,to区域中上轮依然存活的元素,进入老生代空间。
4. 老生代,通常执行标记清楚,但易产生内存碎片,当内存达到必定阈值时,会执行一次标记复制。
# 启动时,设置虚拟机内存大小
node --max-old-space-size = 2000 main.js
node --max-new-space-size = 1024 main.js
- node中,文件即为基本的模块单位,一个文件一个模块。
- 模块导出部分,会被加载为对象存储下来。
全局变量 |
__dirname |
__filename |
模块内生 |
当前模块绝对路径 |
当前文件绝对路径 |
// 进程级,全局变量
console.log(process);
// 模块内生的全局变量
console.log(__dirname, __filename);
// 全局变量,任意模块可读写
exports.val = 1000;
// 全局方法,任意模块可调用
exports.show = function () {
// 此处必须使用, this 用以引用对象自身的一些属性
console.log(this.val);
}
// 这种方式,用于导出一个完整的对象,此处尽可能只导出引用,基础类型会被深复制,继而引起Bug
module.exports = {
val,
show,
}
// 不要相互引用,避免形成循环引用,BUG
const demo = require("./demo");
- Js面向对象,依赖于
this
引用调用自身属性或方法。
function Demo(id, name, a){
// 协议基本属性
this.id = id;
this.name = name;
this.a = a;
// 协议内方法
this.getMutli = function(){
return this.a * 10;
}
}
// new 对象,调用方法,实际就是 {}的简化
new Demo(1,2,3).getMutli();
特性
node 外向为单线程,但实际其内核为多线程执行。npm
线程 |
解释 |
备注 |
主线程 |
编译执行代码 |
应用执行层 |
优化线程 |
优化程序执行 |
|
事件线程 |
执行异步任务的主要线程 |
异步,观察者 |
IO-线程池 |
IO-线程池,默认大小为4, |
并发执行 |
GC-线程池 |
回收垃圾用的,不止一个线程 |
|
事件循环线程,异步任务的关键所在,存在对应的事件观察者。编程
- 事件循环线程,每轮处理一次全部符合条件的事件,周期长度 tick。
- 每轮tick,会逐个处理全部队列中的事件,该结束结束,该回调回调。
- 每轮tick,idle 观察者,IO 观察者,check 观察者,按照优先级依次执行。
function demo(dat) {
console.log(dat);
}
// 5000 ms 后,处理事件
setTimeout(demo, "5000ms", 5000);
// 每隔 1000 ms,处理事件
setInterval(demo, "1000ms", 1000);
// 进程的下个周期处理事件,idle 观察者
process.nextTick(demo, "next-tick-idle")
// 进程的下个周期处理事件,IO 观察者
// 进程的下个周期处理事件,check观察者
setImmediate(demo, "next-tick-check")
// 清空相应的 事件循环
clearTimeout();
clearInterval();
clearImmediate();
- 将事件挂载到监听器对象上。
- 触发监听器时,代码区直接作入栈操做,不参与事件循环。
- 若要将事件转化为异步执行,须要将
const events = require("events");
// EventEmitter 为监听对象
const event = new events.EventEmitter();
// 监听挂载
event.on("msg", function (msg) {
// setImmediate()
console.log("msg:", msg);
})
// 触发监听,此处直接入栈,会当即执行,不参与事件循环
event.emit("msg", "hello world!");
async/await
,事件线程,Promise对象。json
标识符 |
解释 |
备注 |
async |
async 函数,返回值 Promise |
async 只能用于修饰函数 |
await |
await 限制的方法,会等待同步 |
await 只能在 async 函数中 |
Promise |
new Promise((res, rej)=>{}) |
pending,fulfilled,rejected |
- Promise 对象由事件线程处理,仅有,等待,完成,失败等状态。
- resolve 表明事件处理成功时,执行的回调函数。
- reject 表明事件发生异常时,执行的回调函数。
async function demo () {
// 同步代码区
console.log("同步执行");
// 异步回调函数
return new Promise(function (resolve, reject) {
console.log("异步执行");
setTimeout(function () {
console.log("延迟执行");
// resolve 默认至关于 return
// reject 则是默认的异常出口
resolve("result");
});
});
}
async function () {
let res = await demo();
console.log(res);
}
demo()
// 异步并发, 执行 a b c
// 同步等待,等待全部异步任务所有完成
// 结果被存储 res 数组
let res = await Promise.all([a, b, c]);
- IO线程池,IO观察者,以及IO请求对象,共同组成IO事件。
- 在 主线程,事件线程,IO线程池的支持下,可并发异步IO。
IO 是一种特殊的事件类型,本次仅以进程为单位说明其主要流程。
read
进程首先须要申请一块内存块,用于存储数据,以后则向cpu发起请求。
当cpu接收到read请求以后,继续向IO设备发出指令,此处IO设备为虚拟IO设备,可因为端口,协议区分。
当IO设备接收到指令时,即开始工做,当有数据到达时,IO设备会向cpu发信号。
当cpu接收到IO设备的信号时,cpu继续将IO设备中的数据复制至cpu缓存内部,并判断是否继续read。
当cpu接收到数据结束符时,或已数据已超过cpu缓存,此时中止将IO设备中的数据复制至cpu缓存内部。
此时继续将cpu缓存内部的数据复制至指定进程的指定内存区,cpu会循环执行直至接收到结束符。
当以上步骤执行完成,cpu即会改变这次IO的状态,无论同步仍是异步,程序即会向下继续执行。
write
同步,程序在获取到IO数据以前,必须当即返回。
异步,程序在获取到IO数据以前,能够异步返回。
阻塞,程序在获取到IO数据以前,不会执行其余操做,线程会被阻塞。
非阻塞,程序在获取到IO数据以前,能够执行其余操做,线程依旧运行。
非阻塞式IO
select,全部的IO的信息都会被加入一个数组中,当有IO设备完成时,会在表中遍历以肯定IO的进程以及内存块位置。1024
poll,前者存在最大1024的数量限制,故将其设定为链表,以突破限制。
epoll,前二者都存在,每次cpu数据完成时,须要遍历才能获取到内存块位置以及进程信息。
epoll,内部存在一张表,将进程,内存块,当前IO,以及IO设备等信息关联保存,当cpu数据完成时,可快速定位。
经常使用
本质上是一个 C++ 数组。axios
- Buffer 对象是
C++
Js
相互结合的一个案例。
- 全部的 Js 字符串执行网络传输时,都会被转化为 Buffer,特殊状况下直接转化为 Buffer 会快不少。
let str = "hello world!";
// 一些经常使用的字符串方法
console.log(str, str.length, str.strsub(0, 4), str.indexOf(' '));
// 将指定字符串转为,指定字节码的,字节流
let buf = new Buffer(str, "utf-8");
// 建立指定大小的字节流
let buf = new Buffer(100);
// 将 Buffer对象输出一部分
console.log(buf[0]);
// Buffer 转为字符串
buf.toString("utf-8", [start], [end]);
// 文件操做相关
const fs = require("fs");
// 系统Api相关
const os = require("os");
// 调用网络接口库
const axios = require("axios");
// 字符串加解密
const crypto = require("crypto");
// 经常使用的一些 utils
const lodash = require("lodash");
// mongo 使用库
const mongoose = require("mongoose");
// node.js 集群库
const cluster = require("cluster");
// 进程 process
// 命令行 传参
let argv = process.argv
// 主进程 id
let pid = process.pid
// 父进程 id
let ppid = process.ppid;
// 进程内存 info
let memory = process.memoryUsage();
// 此处的全局对象,
Math.floor();
Date.now();
// 控制台
console
// 全局变量
global
- Node 自己的异步非阻塞IO,使得其网络IO效率都比较高。
- Tcp 套接字所传输的都是字节流,Js 中就是 Buffer,须要注意效率问题。
事件 |
解释 |
备注 |
data |
接收到数据时触发 |
server,client |
end |
发送数据结束符 |
server, client |
error |
异常处理 |
server,client |
timeout |
超时触发 |
server,client |
close |
完全关闭套接字触发 |
server,client |
drain |
执行write会触发 |
仅在执行write端触发 |
connect |
执行connect会触发 |
仅在客户端触发 |
// server
const net = require("net");
const server = net.createServer(function (socket) {
socket.on("data", function (data) {
console.log(data.toString());
socket.write("server!");
});
socket.on("end", function () {
console.log("client end!");
});
socket.on("close", function () {
console.log("client closed!");
})
socket.on("error", function (err) {
console.log(err);
});
});
server.listen(8000, "127.0.0.1", function () {
let addr = server.address();
console.log("Tcp-socket is running", addr.address, addr.port, addr.family);
})
// client
const net = require("net");
const client = net.connect(8000, "127.0.0.1", function () {
console.log("Socket-tcp connected succeed!");
client.write("client");
});
client.on("data", function (data) {
console.log(data.toString());
client.end();
});
事件 |
解释 |
备注 |
listening |
绑定端口开始监听 |
server |
message |
接收到数据时触发 |
server |
error |
异常处理 |
server |
close |
关闭监听 |
server |
// server
const dgram = require("dgram");
const server = dgram.createSocket("udp4");
server.on("message", function (msg, info) {
console.log(msg.toString(), info);
});
server.on("listening", function () {
let addr = server.address();
console.log("server running ", addr.address, addr.port, addr.family);
});
server.bind(8000, "127.0.0.1");
// client
const dgram = require("dgram");
const client = dgram.createSocket("udp4");
let msg = "hello";
client.send(msg, 0, msg.length, 8000, "127.0.0.1", function () {
client.close();
});
// 开启 ws 长链接0
const ws = new WebSocket("ws://127.0.0.1:8800/ws");