node.js

Node

Node 是以 Js 为基础扩展而来的一门后端语言。node

Js

此处仅对 Js作一些简单介绍。ios

基础

  • 概念
$tag$ 解释 备注
语言 解释类型,脚本语言 语法类似于 Java
变量 弱类型,区分基础-引用类型 let,const,var,无
  • 类型
  1. booleannumberstring 基础类型。
  2. arrayobjectfunction 引用类型。
$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 = {}; 数组,对象
  • 补充
  1. && || 默认短路执行,!复合时,通常要加()
  2. 比较运算存在隐式转化,=== 则仅在类型与数值相同时才返回 $true$。
$boolean$ 枚举
$true$ 非零值定义值true
$false$ nullundefined00.0false

语法

  • 示例
  1. assert(ok),当ok = false时,中断进程输出异常,仅用于 Debug。
  2. console.time("label"),记录程序执行时间,且记录器自己亦会消耗时间。
  3. 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;
}
  • 函数
  1. 通用的传值规则,基础执行深复制,引用执行浅复制。
  2. 函数的传参过程,一样执行通用传值规则。
  3. 面向函数编程,以函数为参数的一种开发思路。
// 函数参数传递
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 () {}

数据结构

  • 对象-数组
  1. 可将Js-对象,Lua-表,视为同一类东西。
  2. 二者并没有过大差距,操做核心都是 hash映射。
  3. 对象和数组的基准访问速度相差不是特别大。
  4. 对象通常不推荐构建过于臃肿的键值对,数组则无限制。
// 对象属性三种定义,新增方式
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-Set
  1. Map,就是一种特殊的 key-val数据结构,由 {}向上封装而来。
  2. 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

模块

  • 项目管理
  1. npmnode 自带的包管理器。
node -v
npm -v

# 初始化操做
npm init

# 生成指定的 package.json,保存配置,可用于版本控制
npm install package --save
npm uninstall package --save

npm i # 当项目存在 package 时,仅需执行 install 便可
  • 内存
  1. node本质上只是一种异步编程思想,执行效率在 v8引擎下也不至于太慢。
  2. 32 位系统下,node 默认700Mb最佳,64位则 1400Mb最佳。
  3. 且当内存超出范围时,进程会崩溃,故避免内存超过 1400 MB。
  4. 但也可在启动时,扩充最大内存空间,保证服务不至于宕机,但会影响性能。
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
  • 单位
  1. node中,文件即为基本的模块单位,一个文件一个模块。
  2. 模块导出部分,会被加载为对象存储下来。
全局变量 __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");
  • 面向对象
  1. 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-线程池 回收垃圾用的,不止一个线程
  • 事件

事件循环线程,异步任务的关键所在,存在对应的事件观察者。编程

  1. 事件循环线程,每轮处理一次全部符合条件的事件,周期长度 tick。
  2. 每轮tick,会逐个处理全部队列中的事件,该结束结束,该回调回调。
  3. 每轮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();
  • 监听器
  1. 将事件挂载到监听器对象上。
  2. 触发监听器时,代码区直接作入栈操做,不参与事件循环。
  3. 若要将事件转化为异步执行,须要将
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
  1. Promise 对象由事件线程处理,仅有,等待,完成,失败等状态。
  2. resolve 表明事件处理成功时,执行的回调函数。
  3. 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
  1. IO线程池,IO观察者,以及IO请求对象,共同组成IO事件。
  2. 在 主线程,事件线程,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数据完成时,可快速定位。

经常使用

  • Buffer

本质上是一个 C++ 数组。axios

  1. Buffer 对象是 C++ Js 相互结合的一个案例。
  2. 全部的 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
  • Tcp
  1. Node 自己的异步非阻塞IO,使得其网络IO效率都比较高。
  2. 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();
});
  • Udp
事件 解释 备注
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();
});
  • WebSocket
// 开启 ws 长链接0
const ws = new WebSocket("ws://127.0.0.1:8800/ws");
相关文章
相关标签/搜索