监听js变量的变化_Node.js从零开始——事件、系统和流
畢竟不是一個真正的教程,這里主要還是以普及和介紹為主,所以這一部分就是 Node.js 的其他部分介紹了,主要也就是事件觸發、操作系統以及流的知識。
1 事件觸發器
因為我們之前在瀏覽器中使用 JavaScript,所以知道 JS 通過事件處理了許多用戶的交互:鼠標的單擊、鍵盤按鈕的按下、對鼠標移動的反應等等。
在后端,Node.js 也提供了使用 events 模塊 構建類似系統的選項。
具體上,此模塊提供了 EventEmitter 類,用于處理事件。
使用以下代碼進行初始化:
const EventEmitter = require('events');const eventEmitter = new EventEmitter();該對象公開了 on 和 emit 方法:
- emit 用于觸發事件
- on 用于添加回調函數(會在事件被觸發時執行)
例如,創建 start 事件,并提供一個示例,通過記錄到控制臺進行交互:
eventEmitter.on('start', () => {console.log('開始'); });當運行以下代碼時:
eventEmitter.emit('start');事件處理函數會被觸發,且獲得控制臺日志。
可以通過將參數作為額外參數傳給 emit() 來將參數傳給事件處理程序:
eventEmitter.on('start', number => {console.log(`開始 ${number}`); }); eventEmitter.emit('start', 23);多個參數:
eventEmitter.on('start', (start, end) => {console.log(`從 ${start} 到 ${end}`); }); eventEmitter.emit('start', 1, 100);EventEmitter 對象還公開了其他幾個與事件進行交互的方法,例如:
- once():添加單次監聽器
- removeListener() / off():從事件中移除事件監聽器
- removeAllListeners():移除事件的所有監聽器
下面詳細的介紹一下事件模塊:
2 事件模塊
events 模塊為提供了 EventEmitter 類,這是在 Node.js 中處理事件的關鍵,如上文簡單的引入 events 即可使用,這里是另一個例子:
const EventEmitter = require('events'); const door = new EventEmitter();事件監聽器返回及使用以下事件:
- 當監聽器被添加時返回 newListener
- 當監聽器被移除時返回 removeListener
以下是最常用的方法的詳細說明:
2.1 emitter.addListener()
emitter.on() 的別名,這是為了和 DOM API 保持一定的一致([DOM].addEventListener())。
2.2 emitter.emit()
觸發事件, 按照事件被注冊的順序同步地調用每個事件監聽器:
door.emit("slam"); // 觸發 "slam" 事件。2.3 emitter.eventNames()
返回字符串(表示在當前 EventEmitter 對象上注冊的事件)數組:
door.eventNames();2.4 emitter.getMaxListeners()
獲取可以添加到 EventEmitter 對象的監聽器的最大數量(默認為 10,但可以使用 setMaxListeners() 進行增加或減少)。
door.getMaxListeners();2.5 emitter.listenerCount()
獲取作為參數傳入的事件監聽器的計數:
door.listenerCount('open');2.6 emitter.listeners()
獲取作為參數傳入的事件監聽器的數組:
door.listeners('open');2.7 emitter.off()
emitter.removeListener() 的別名,新增于 Node.js 10;這是為了和 emitter.on() 形成對應,簡化記憶。
2.8 emitter.on()
添加當事件被觸發時調用的回調函數,用法:
door.on('open', () => { console.log('打開'); });2.9 emitter.once()
添加當事件在注冊之后首次被觸發時調用的回調函數, 該回調只會被調用一次,不會再被調用:
const EventEmitter = require('events'); const ee = new EventEmitter();ee.once('my-event', () => {//只調用一次回調函數 });2.10 emitter.prependListener()
當使用 on 或 addListener 添加監聽器時,監聽器會被添加到監聽器隊列中的最后一個,并且最后一個被調用; 使用 prependListener 則可以在其他監聽器之前添加并調用。
2.11 emitter.prependOnceListener()
當使用 once 添加監聽器時,監聽器會被添加到監聽器隊列中的最后一個,并且最后一個被調用; 使用 prependOnceListener 則可以在其他監聽器之前添加并調用。
2.12 emitter.removeAllListeners()
移除 EventEmitter 對象的所有監聽特定事件的監聽器:
door.removeAllListeners('open');2.13 emitter.removeListener()
移除特定的監聽器,可以通過將回調函數保存到變量中(當添加時),以便以后可以引用它:
const doSomething = () => {};door.on('open', doSomething); door.removeListener('open', doSomething);2.14 emitter.setMaxListeners()
設置可以添加到 EventEmitter 對象的監聽器的最大數量(默認為 10,但可以增加或減少):
door.setMaxListeners(50);3 操作系統模塊
為了能夠真正的和操作系統交互,Node.js 同樣提供了操作系統模塊;該模塊提供了許多函數,可用于從底層的操作系統和程序運行所在的計算機上檢索信息并與其進行交互,引用方法保持一致:
const os = require('os');有一些有用的屬性可以告訴我們一些與處理文件有關的關鍵事項:
- os.EOL 可給出行定界符序列:在 Linux 和 macOS 上為 n,在 Windows 上為 rn
- os.constants.signals 可告知所有與處理過程信號相關的常量,例如 SIGHUP、SIGKILL 等
- os.constants.errno 可設置用于錯誤報告的常量,例如 EADDRINUSE、EOVERFLOW 等
現在看一下 os 提供的主要方法:
3.1 os.arch()
返回標識底層架構的字符串,例如 arm、x64、arm64:
3.2 os.cpus()
返回關于系統上可用的 CPU 的信息。
例如:
3.3 os.endianness()
根據是使用大端序或小端序編譯 Node.js,返回 BE 或 LE:
3.4 os.freemem()
返回代表系統中可用內存的字節數:
3.5 os.homedir()
返回到當前用戶的主目錄的路徑,例如:
3.6 os.hostname()
返回主機名:
3.7 os.loadavg()
返回操作系統對平均負載的計算,這僅在 Linux 和 macOS 上返回有意義的值;Windows 因為對系統負載的計算方法不同,返回值為 0。
例如:
3.8 os.networkInterfaces()
返回系統上可用的網絡接口的詳細信息,例如(因為是我真實的電腦,所以我把 mac 地址遮蔽了,見諒):
3.9 os.platform()
返回為 Node.js 編譯的平臺:
- darwin
- freebsd
- linux
- openbsd
- win32
- ...等
3.10 os.release()
返回標識操作系統版本號的字符串:
3.11 os.tmpdir()
返回指定的臨時文件夾的路徑:
3.12 os.totalmem()
返回表示系統中可用的總內存的字節數:
3.13 os.type()
標識操作系統:
- Linux
- macOS 上為 Darwin
- Windows 上為 Windows_NT(這是因為非 NT 架構的 Windows 系統現在占有率太低了,比如 Win 95、98、Me,所以默認如此顯示)
3.14 os.uptime()
返回自上次重新啟動以來計算機持續運行的秒數:
3.15 os.userInfo()
當前登錄用戶的信息:
4 流
流是為 Node.js 應用程序提供動力的基本概念之一,這是一種以高效的方式處理讀/寫文件、網絡通信、或任何類型的端到端的信息交換。
流不是 Node.js 特有的概念,它是幾十年前在 Unix 操作系統中引入的,程序可以通過管道運算符(|)對流進行相互交互。
例如,在傳統的方式中,當告訴程序讀取文件時,這會將文件從頭到尾讀入內存,然后進行處理。
使用流,則可以逐個片段地讀取并處理(而無需全部保存在內存中)。
Node.js 的 stream 模塊 提供了構建所有流 API 的基礎,所有的流都是 EventEmitter 的實例(這樣講就容易明白了吧)。
4.1 為什么使用流
相對于使用其他的數據處理方法,流基本上提供了兩個主要優點:
- 內存效率:無需加載大量的數據到內存中即可進行處理
- 時間效率:當獲得數據之后即可立即開始處理數據,這樣所需的時間更少,而不必等到整個數據有效負載可用才開始
4.2 流的示例
一個典型的例子是從磁盤讀取文件。
使用 Node.js 的 fs 模塊,可以讀取文件,并在與 HTTP 服務器建立新連接時通過 HTTP 提供文件:
const http = require('http'); const fs = require('fs');const server = http.createServer(function(req, res) {fs.readFile(__dirname + '/data.txt', (err, data) => {res.end(data);}); }); server.listen(3000);readFile() 讀取文件的全部內容,并在完成時調用回調函數;而回調中的 res.end(data) 會返回文件的內容給 HTTP 客戶端。
如果文件很大,則該操作會花費較多的時間,這個時候我們可以使用流,如下例:
const http = require('http'); const fs = require('fs');const server = http.createServer((req, res) => {const stream = fs.createReadStream(__dirname + '/data.txt');stream.pipe(res); }) server.listen(3000);當要發送的數據塊已獲得時就立即開始將其流式傳輸到 HTTP 客戶端,而不是等待直到文件被完全讀取。
4.3 pipe()
上面的示例使用了 stream.pipe(res) 這行代碼:在文件流上調用 pipe() 方法。
該代碼的作用是獲取來源流,并將其通過管道傳輸到目標流,在該示例中,文件流通過管道傳輸到 HTTP 響應。
pipe() 方法的返回值是目標流,它可以鏈接多個 pipe() 調用,從而非常方便,如下所示:
src.pipe(dest1).pipe(dest2);此構造相對于:
src.pipe(dest1); dest1.pipe(dest2);要更容易理解和編寫。
4.4 流驅動的 Node.js API
由于它的優點,許多 Node.js 核心模塊提供了原生的流處理功能,最值得注意的有:
- process.stdin 返回連接到 stdin 的流
- process.stdout 返回連接到 stdout 的流
- process.stderr 返回連接到 stderr 的流
- fs.createReadStream() 創建文件的可讀流
- fs.createWriteStream() 創建到文件的可寫流
- net.connect() 啟動基于流的連接
- http.request() 返回 http.ClientRequest 類的實例,該實例是可寫流
- zlib.createGzip() 使用 gzip(壓縮算法)將數據壓縮到流中
- zlib.createGunzip() 解壓縮 gzip 流
- zlib.createDeflate() 使用 deflate(壓縮算法)將數據壓縮到流中
- zlib.createInflate() 解壓縮 deflate 流
4.5 不同類型的流
流分為四類:
- Readable:可以通過管道讀取、但不能通過管道寫入的流(可以接收數據,但不能向其發送數據);當推送數據到可讀流中時,會對其進行緩沖,直到使用者開始讀取數據為止
- Writable:可以通過管道寫入、但不能通過管道讀取的流(可以發送數據,但不能從中接收數據)
- Duplex:可以通過管道寫入和讀取的流,基本上相對于是可讀流和可寫流的組合
- Transform:類似于雙工流、但其輸出是其輸入的轉換的轉換流
4.6 如何創建可讀流
從 stream 模塊獲取可讀流,對其進行初始化并實現 readable._read() 方法。
首先創建流對象:
const Stream = require('stream'); const readableStream = new Stream.Readable();然后實現 _read:
readableStream._read = () => {};也可以使用 read 選項實現 _read:
const readableStream = new Stream.Readable({read() {} });現在,流已初始化,可以向其發送數據了:
readableStream.push('hi!'); readableStream.push('ho!');4.7 如何創建可寫流
若要創建可寫流,需要繼承基本的 Writable 對象,并實現其 _write() 方法。
首先創建流對象:
const Stream = require('stream'); const writableStream = new Stream.Writable();然后實現 _write:
writableStream._write = (chunk, encoding, next) => {console.log(chunk.toString());next(); };現在,可以通過以下方式傳輸可讀流:
process.stdin.pipe(writableStream);4.8 如何從可讀流中獲取數據
使用可寫流:
const Stream = require('stream');const readableStream = new Stream.Readable({read() {} }); const writableStream = new Stream.Writable();writableStream._write = (chunk, encoding, next) => {console.log(chunk.toString());next(); };readableStream.pipe(writableStream);readableStream.push('hi!'); readableStream.push('ho!');也可以使用 readable 事件直接獲取可讀流數據:
readableStream.on('readable', () => {console.log(readableStream.read()); });4.9 如何發送數據到可寫流
使用流的 write() 方法:
writableStream.write('hey!n');4.10 使用信號通知已結束寫入的可寫流
使用 end() 方法:
const Stream = require('stream');const readableStream = new Stream.Readable({read() {} }); const writableStream = new Stream.Writable();writableStream._write = (chunk, encoding, next) => {console.log(chunk.toString());next(); };readableStream.pipe(writableStream);readableStream.push('hi!'); readableStream.push('ho!');writableStream.end();眼看著基礎的 Node.js 快要結束了,其實如果不算第三方的模塊,它自帶的東西的確就是這么多,我們的重點還是要有模塊化開發的思路和方法論,或者大量的練習,畢竟熟能生巧么。
總結
以上是生活随笔為你收集整理的监听js变量的变化_Node.js从零开始——事件、系统和流的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 建立数组并写入数据_Visual Stu
- 下一篇: 安装完的纯净版系统没有任何驱动程序怎么办