国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Node 之 Event 模塊

mrli2016 / 1360人閱讀

摘要:為什么把叫做集合而不能稱為嚴(yán)格意義上的對(duì)象,來(lái)看這個(gè)集合的構(gòu)造函數(shù)可以見(jiàn)得,是與處于同一層級(jí)的而非是繼承自,所以說(shuō)由實(shí)例出來(lái)的對(duì)象更加的純凈,并沒(méi)有諸如等方法,更像是一個(gè)集合。

寫(xiě)在前面

事件的編程方式具有輕量級(jí)、松耦合、只關(guān)注事務(wù)點(diǎn)等優(yōu)勢(shì),在瀏覽器端,有著自己的一套DOM事件機(jī)制,其中含包括這諸如事件冒泡,事件捕獲等;然而Node的事件機(jī)制沒(méi)有事件冒泡等,其原理就是設(shè)計(jì)模式中的觀察者模式Node很多的模塊繼承這個(gè)事件模塊,下面我們就來(lái)根據(jù)源碼來(lái)學(xué)習(xí)下其API,做到知其然更知其所以然。

引入模塊
const EventEmitter = require("events");
const EventEmitter = require("events").EventEmitter;

經(jīng)常會(huì)看到這種兩種方式來(lái)引入我們的events模塊,但是在Node的高版本中可以直接使用第一種方式,高版本也支持下面這種方式,下面的這種方式主是在Node0.10.x版本時(shí)使用,源碼中也是很清楚,之所以這么做就是為了兼容低版本時(shí)寫(xiě)下的Node代碼:

module.exports = EventEmitter;
EventEmitter.EventEmitter = EventEmitter;

注:之后提到的objEventEmitter的實(shí)例對(duì)象,也就是obj = new EventEmitter()

基本使用

得到我們的事件構(gòu)造函數(shù)后,我們就可以來(lái)實(shí)例化一個(gè)事件對(duì)象:

const EventEmitter = require("events"),
    follow = new EventEmitter();
follow.on("node", question => {
    console.log(`有一個(gè)關(guān)于node的問(wèn)題: ${question}`);
});
follow.emit("node", "jade與ejs選擇哪個(gè)?");

這是一個(gè)簡(jiǎn)單的使用,下面我們就來(lái)看看我們所用到的API以及它們的實(shí)現(xiàn):

訂閱事件

on(type, listener)來(lái)訂閱事件,傳入的type參數(shù)為事件名,listener為待發(fā)布函數(shù)。同時(shí)addListener方法和on方法有著同樣的效果,指向的是內(nèi)存的同一塊:

EventEmitter.prototype.on = EventEmitter.prototype.addListener;

在調(diào)用on時(shí),假若我們訂閱了newListener事件,該事件會(huì)先被發(fā)布。

那么問(wèn)題來(lái)了?訂閱的事件被存儲(chǔ)在了哪里呢?

答案就是obj._events,這是一個(gè)事件集合,事件名就是該集合的鍵名,當(dāng)事件的待補(bǔ)發(fā)函數(shù)只有一個(gè)時(shí),鍵值為函數(shù);當(dāng)有多個(gè)時(shí),鍵值為數(shù)組。為什么把obj._events叫做集合而不能稱為嚴(yán)格意義上的對(duì)象,來(lái)看這個(gè)集合的構(gòu)造函數(shù):

function EventHandlers () {}
EventHandlers.prototype = Object.create(null);

可以見(jiàn)得,EventHandlers.prototype是與Object.prototype處于同一層級(jí)的而非是繼承自Object.prototype,所以說(shuō)由EventHandlers實(shí)例出來(lái)的對(duì)象更加的"純凈",并沒(méi)有諸如toString等方法,更像是一個(gè)集合。

隨著給一個(gè)事件添加待發(fā)布函數(shù),當(dāng)添加的數(shù)量超過(guò)10條是,會(huì)發(fā)現(xiàn)有警告:

(node) warning: possible EventEmitter memory leak detected. 11 git listeners added. Use emitter.setMaxListeners() to increase limit.

產(chǎn)生警告的原因就是事件待發(fā)布函數(shù)數(shù)組的長(zhǎng)度超過(guò)了默認(rèn)的最大容量,默認(rèn)的最大容量是EventEmitter.defaultMaxListeners,而這個(gè)屬性是一個(gè)getter/setter訪問(wèn)器,訪問(wèn)的是變量defaultMaxListeners的值,也就是10。

// 得到最大容量
function $getMaxListeners (that) {
    if (that._maxListeners === undefined) 
        return EventEmitter.defaultMaxListeners;
    return that._maxListeners;
}
// 發(fā)出警告代碼:
if (!existing.warned) {
    m = $getMaxListeners(target);
    if (m && m > 0 && existing.length > m) {
        existing.warned = true;
      process.emitWarning("Possible EventEmitter memory leak detected. " + `${existing.length} ${type} listeners added. ` + "Use emitter.setMaxListeners() to increase limit");
    }
}

觀察獲得最大容量函數(shù)可以發(fā)現(xiàn),給obj._maxListeners賦值可以提升我們的最大容量(obj._maxListeners初始化時(shí)被賦值為undefined),可以利用setMaxListeners(n)方法來(lái)進(jìn)行賦值:

EventEmitter.prototype.setMaxListeners = function (n) {
    if (typeof n !== "number" || n < 0 || isNaN(n))
        throw new TypeError("n must be a position number");
    this._maxListeners = n;
    return this;
};

看源碼可以發(fā)現(xiàn),訂閱事件其實(shí)是用的_addListener函數(shù),其最后一個(gè)參數(shù)為prepend,代表著是否將待發(fā)布函數(shù)添加到事件數(shù)組的第一個(gè),所以應(yīng)該還有一個(gè)prependListener(type, listener)函數(shù),可以將listener添加到obj.events.type的第一個(gè)位置。

once(type, listener),通過(guò)這種方式添加的待發(fā)布函數(shù),只能被發(fā)布一次,發(fā)布一次后就會(huì)被移除。

// 將listener包裹在一個(gè)g函數(shù)中,在每次執(zhí)行時(shí),現(xiàn)將該函數(shù)從事件數(shù)組中移除
// 真正的待發(fā)布函數(shù)成為了g函數(shù)的屬性
function _onceWrap (target, type, listener) {
    var fired = false;
    function g () {
        target.removeListener(type, g);  // 先移除
        if (!fired) {
            fired = true;
            listener.apply(target, arguments);  // 再發(fā)布
        }
    }
    g.listener = listener;
    return g;
}

于此對(duì)應(yīng)的還有prependOnceListener方法,下面來(lái)看一個(gè)例子:

work.once("git", pull);
work.on("git", () => {
    console.log("git status");
});
work.emit("git");
console.log("第二次");
work.emit("git");
// git pull
// git status
// 第二次
// git status
發(fā)布事件

emit(type, args)來(lái)進(jìn)行事件的發(fā)布,在實(shí)現(xiàn)上也很簡(jiǎn)單就是執(zhí)行obj.events.type的函數(shù)或者遍歷obj.events.type數(shù)組一次執(zhí)行函數(shù),需要注意的是error事件的發(fā)布,如果沒(méi)有訂閱error事件的話,發(fā)布時(shí),就會(huì)用到throw new Error()

移除事件

removeListener(type, listener)來(lái)移除${type}事件的listener函數(shù)

removeAllListeners(type),當(dāng)傳入type時(shí)會(huì)將type事件全部移除;不傳入?yún)?shù)時(shí),會(huì)將obj._events重置。

在移除時(shí),假若給obj訂閱了removeListener事件的話,那么在每移除一個(gè)待發(fā)布函數(shù)時(shí),會(huì)發(fā)布一次該事件,在將obj重置時(shí),也會(huì)最后將該事件移除。

function pull () {
    console.log("git pull");
};
work.on("removeListener", (type, fn) => {
    console.log(`remove ${type} ${fn.name} event`);
})
work.on("git", pull);
work.on("git", () => {
    console.log("git status");
});

work.removeListener("git", pull);
work.emit("git");
// 依次輸出
// remove git pull event
// git status
其余API

eventNames,返回實(shí)例對(duì)象所有的事件名數(shù)組或者一個(gè)空數(shù)組,源碼中利用了ES6的新方法Reflect.ownKeys來(lái)獲得obj._events對(duì)象的自身屬性:

EventEmitter.prototype.eventNames = function eventNames() {
  return this._eventsCount > 0 ? Reflect.ownKeys(this._events) : [];
};

listenerCount(type)返回事件的待發(fā)布函數(shù)的的數(shù)量,也就是obj._events.length or 1,這個(gè)方法在obj上和EventEmitter都有,本質(zhì)上都是調(diào)用下面這個(gè)方法,實(shí)現(xiàn)也是很明了:

function listenerCount (type) {
    const events = this._events;
    
    if (events) {
        const evlistener = events[type];

        if (typeof evlistener === "function") {
            return 1;
        } else if (evlistener) {
            return evlistener.length;
        }
    }
}

listeners(type),返回${type}事件的待發(fā)布函數(shù)數(shù)組或者空數(shù)組,需要注意是這個(gè)數(shù)組并不是obj.events.type的引用。

總結(jié)

這次閱讀Node的源代碼發(fā)現(xiàn),Node源碼中對(duì)于原生的slicesplice并沒(méi)有使用,而是自己寫(xiě)了一個(gè)針對(duì)性更加強(qiáng)的arrayClonespliceOne函數(shù),不知這樣寫(xiě)的原因是不是要將速度提升,因?yàn)榭碫8源碼會(huì)發(fā)現(xiàn),slicesplice的實(shí)現(xiàn)有一些復(fù)雜,都有額外的判斷來(lái)對(duì)參數(shù)進(jìn)行規(guī)范化,像Node源碼自己寫(xiě)的話,減少了這些無(wú)用的判斷,從而提升了效率。當(dāng)然這只是我個(gè)人的一些理解,如有錯(cuò)誤還請(qǐng)大家指出。

附:解析的events.js

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/87862.html

相關(guān)文章

  • 前端小項(xiàng)目在線便利貼

    摘要:實(shí)現(xiàn)的效果如下界面可能不是太好看,考慮到容器的高度會(huì)被拉長(zhǎng),因此沒(méi)有用圖片做背景。 實(shí)現(xiàn)的效果如下: showImg(https://segmentfault.com/img/remote/1460000011155402); 界面可能不是太好看?,考慮到容器的高度會(huì)被拉長(zhǎng),因此沒(méi)有用圖片做背景。 預(yù)覽 便利貼 涉及的知識(shí)點(diǎn) sass(css 預(yù)編譯器) webpack(自動(dòng)化構(gòu)...

    microelec 評(píng)論0 收藏0
  • Node.js Event LoopTimers, process.nextTick()

    摘要:前言以異步和事件驅(qū)動(dòng)的特性著稱但異步是怎么實(shí)現(xiàn)的呢其中核心的一部分就是下文中內(nèi)容基本來(lái)自于文檔有不準(zhǔn)確地方請(qǐng)指出什么是能讓的操作表現(xiàn)得無(wú)阻塞盡管是單線程的但通過(guò)盡可能的將操作放到操作系統(tǒng)內(nèi)核由于現(xiàn)在大多數(shù)內(nèi)核都是多線程的它們可以在后臺(tái)執(zhí)行多 前言 Node.js以異步I/O和事件驅(qū)動(dòng)的特性著稱,但異步I/O是怎么實(shí)現(xiàn)的呢?其中核心的一部分就是event loop,下文中內(nèi)容基本來(lái)自于N...

    sarva 評(píng)論0 收藏0
  • Swoole 源碼分析——Server模塊Timer模塊與時(shí)間輪算法

    摘要:當(dāng)其就緒時(shí),會(huì)調(diào)用執(zhí)行定時(shí)函數(shù)。進(jìn)程超時(shí)停止進(jìn)程將要停止時(shí),并不會(huì)立刻停止,而是會(huì)等待事件循環(huán)結(jié)束后停止,這時(shí)為了防止進(jìn)程不退出,還設(shè)置了的延遲,超過(guò)就會(huì)停止該進(jìn)程。當(dāng)允許空閑時(shí)間小于時(shí),統(tǒng)一每隔檢測(cè)空閑連接。 前言 swoole 的 timer 模塊功能有三個(gè):用戶定時(shí)任務(wù)、剔除空閑連接、更新 server 時(shí)間。timer 模塊的底層有兩種,一種是基于 alarm 信號(hào),一種是基于...

    qieangel2013 評(píng)論0 收藏0
  • 讀Zepto源碼Gesture模塊

    摘要:模塊基于上的事件的封裝,利用屬性,封裝出系列事件。這個(gè)判斷需要引入設(shè)備偵測(cè)模塊。然后是監(jiān)測(cè)事件,根據(jù)這三個(gè)事件,可以組合出和事件。其中變量對(duì)象和模塊中的對(duì)象的作用差不多,可以先看看讀源碼之模塊對(duì)模塊的分析。 Gesture 模塊基于 IOS 上的 Gesture 事件的封裝,利用 scale 屬性,封裝出 pinch 系列事件。 讀 Zepto 源碼系列文章已經(jīng)放到了github上,歡...

    coolpail 評(píng)論0 收藏0
  • 【譯】Node.js 前端開(kāi)發(fā)指南

    摘要:定時(shí)器在和瀏覽器中的表現(xiàn)形式是相同的。關(guān)于定時(shí)器的一個(gè)重要的事情是,我們提供的延遲不代表在這個(gè)時(shí)間之后回調(diào)就會(huì)被執(zhí)行。它的真正含義是,一旦主線程完成所有操作包括微任務(wù)并且沒(méi)有其它具有更高優(yōu)先級(jí)的定時(shí)器,將在此時(shí)間之后執(zhí)行回調(diào)。 眾成翻譯 原文鏈接 關(guān)于作者 2018年6月21日出版? 本指南面向了解Javascript但尚未十分熟悉Node.js的前端開(kāi)發(fā)人員。我這里不專注于語(yǔ)言本身...

    CntChen 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<