Node.js 是一個基於 Chrome V8 引擎的 JavaScript 運行環境。Node.js 使用了一個事件驅動、非阻塞式 I/O的模型,使其輕量又高效。Allows you to build scalable network applications usingJavaScript on the server-side.
對於上面這段官方的引用你們應該都看過,nodejs是基於事件驅動和非阻塞I/O的方式來設計運行的,那麼做爲實現事件驅動的核心模塊Events就成了深刻學習node.js的關鍵。在node中大部分的模塊的實現都繼承了Events類。 好比,文件操做中的fs事件流,網絡編程所用到的tcp,http模塊等,當你回想本身寫的程序後,會發現不少操做都基於事件驅動,Events類。node
簡單來講,就是經過監聽事件的狀態變化來作出相應的操做。好比讀取一個文件,文件讀取完畢,或者文件讀取錯誤,那麼就觸發對應的狀態,而後調用對應的回掉函數來進行處理。編程
咱們來簡單的看幾段代碼來回憶一下:數組
const fs = require('fs'); let rs = fs.createReadStream('1.txt'); // 監聽文件打開操做 rs.on('open', function() { console.log('open'); }); // 監聽數據流讀取 rs.on('data', function(data) { console.log(data); }); // 監聽錯誤 rs.on('error', function() { console.log('error'); }); // 監聽讀取結束操做 rs.on('end', function() { console.log('end'); }); // 監聽文件關閉操做 rs.on('close', function() { console.log('close'); });
上面這段在建立文件讀取流的操做上,針對文件的打開,數據,錯誤,結束,關閉等幾個狀態進行了監聽的回調處理,這也應徵了咱們上面的定義經過監聽事件的狀態變化來作出相應的操做。
那麼這些監聽事件又是如何觸發的呢?它是經過Events類中的emit方法去發射事件的。那麼下面咱們來看一下Events是如何實現這樣的監聽操做的。微信
下面來看一下代碼,看看是怎麼使用的網絡
const events = require('events'); const EventsEmitter = new events(); //===============事件監聽部分=============== EventsEmitter.on('open', function() { console.log('open'); }); EventsEmitter.on('data', function(data) { console.log(data); }); EventsEmitter.on('error', function() { console.log('error'); }); EventsEmitter.on('end', function() { console.log('end'); }); EventsEmitter.on('close', function() { console.log('close'); }); //=============事件觸發部分================= // 觸發open事件 EventsEmitter.emit('open'); // 觸發data事件,並傳遞一個字符串參數'test' EventsEmitter.emit('data','test'); // 觸發error事件 EventsEmitter.emit('error'); // 觸發end事件 EventsEmitter.emit('end'); // 觸發close事件 EventsEmitter.emit('close');
看完上面這段代碼是否是更進一步的理解了呢。咱們回顧最最上面的那段文件流監聽的代碼,其實就是文件在不一樣的狀態下去發射相應的emit事件。 而在那段代碼中咱們並無去引入events這個node提供的模塊,是由於文件流中繼承了events模塊,因此rs這個變量也就擁有了相應的rs.on()這個方法了。app
看到這裏我想應該都瞭解的差很少了。那麼下面來試着實現一下Events這個類,加深理解。tcp
class Events { constructor() { this.events = {}; } } module.exports = Events;
on方法接收兩個參數:ide
將對應的事件先存放在一個對象中,分兩種狀況:函數
/** * 事件監聽 * @param {*} type 監聽的事件類型 * @param {*} listener 回調函數 */ on(type, listener) { if (this.events[type]) { this.events[type].push(listener); } else { this.events[type] = [listener]; } }
這裏在補充一下,同一個監聽事件是能夠添加多個的,因此這裏纔會this.events[type]纔會給一個數組來存儲學習
接收兩個參數:
/** * 事件觸發 * @param {*} type 要觸發的事件類型 * @param {...any} rest 接收到的若干個參數,這個參數會做爲參數被傳遞到對應事件的回調函數中 */ emit(type, ...rest) { if (this.events[type]) { this.events[type].forEach(listener => { listener.apply(this, rest); }); } }
寫到這裏,咱們以前的代碼就可以引入本身寫的這個Events模塊來執行了
這個方法是用來刪除對應事件的某個監聽函數,那麼咱們只須要把該事件從this.events[type]中刪除便可
接收兩個參數:
/** * 刪除指定事件中的監聽函數 * @param {*} type 對應的事件 * @param {*} listener 要刪除的監聽函數 */ removeListener(type, listener) { if (this.events[type]) { this.events[type].filter(l => l !== listener); } }
這個方法和on同樣,惟一的區別就是它只會執行一次,即使屢次調用emit去觸發相同的事件監聽,它也只會執行一次。
/** * 事件監聽,可是隻執行一次 * @param {*} type 監聽的事件類型 * @param {*} listener 回調函數 */ once(type, listener) { const wraper = (...rest) => { listener.apply(this, rest); this.removeListener(type, wraper); }; this.on(type, wrapper); }
class Events { constructor() { this.events = {}; } /** * 事件監聽 * @param {*} type 監聽的事件類型 * @param {*} listener 回調函數 */ on(type, listener) { if (this.events[type]) { this.events[type].push(listener); } else { this.events[type] = [listener]; } } /** * 事件監聽,可是隻執行一次 * @param {*} type 監聽的事件類型 * @param {*} listener 回調函數 */ once(type, listener) { const wraper = (...rest) => { listener.apply(this, rest); this.removeListener(type, wraper); }; this.on(type, wrapper); } /** * 事件觸發 * @param {*} type 要觸發的事件類型 * @param {...any} rest 接收到的若干個參數,這個參數會做爲參數被傳遞到對應事件的回調函數中 */ emit(type, ...rest) { if (this.events[type]) { this.events[type].forEach(listener => { listener.apply(this, rest); }); } } /** * 刪除指定事件中的監聽函數 * @param {*} type 對應的事件 * @param {*} listener 要刪除的監聽函數 */ removeListener(type, listener) { if (this.events[type]) { this.events[type].filter(l => l !== listener); } } } module.exports = Events;
Events模塊是node很是核心的模塊,它對你深刻去學習node有很大的幫助,上面我只寫了幾個方法,內部還有幾個API以及一些很是細節的地方能夠本身試着去擴展,我這裏就不一個一個的去寫了,文章有寫很差的地方或者看不懂的地方均可以給我留言哦。
若是以爲寫的還行,方便的話幫忙點個贊哦,謝謝了。
如下個人新我的微信公衆號,在裏面也會爲你們提供原創文章,歡迎你們關注,當關注用戶量夠了,我會在裏面推出一些視頻教程