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");