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");
相關文章
相關標籤/搜索