關於 event 菜狗子在這作點簡單的整理下而已。 有錯誤歡迎指出斧正javascript
在node中咱們要實現一套事件機制真的是無比簡單的。直接繼承下原生的EventEmitter
完成~。菜狗子比較閒,就和你們一塊兒讀讀這部分的源碼。java
源碼地址node
/** 發佈一個事件,這部分比較簡單 */
EventEmitter.prototype.emit = function emit(type, ...args) {
const events = this._events;
/** * 這裏是一大段錯誤處理,附加錯誤信息的代碼 * 有幾個輔助的函數,想了解建議看源碼。 * tips:同時會對`error`這個時間進行處理。(若是沒有監聽error的話) * 這裏其實安利一個調試利器 * 原生函數:Error.captureStackTrace 掛載調用棧。 * 之後獲取調用棧就省的遞歸callee或者扔一個沒用的錯誤了 * @link */
/** on 或者 addListener 的時候掛載的*/
const handler = events[type];
if (handler === undefined)
// 這裏能夠看到哈,返回值決定了是否有被監聽捕獲,某些狀況也許就用上餓了
return false;
if (typeof handler === 'function') {
/** 這裏效果和Function.prototype.apply一致 */
Reflect.apply(handler, this, args);
} else {
/** 這個len能讓程序稍稍快一點,比起每次循環都去求算。常常被忽略*/
const len = handler.length;
const listeners = arrayClone(handler, len);
for (var i = 0; i < len; ++i)
Reflect.apply(listeners[i], this, args);
}
return true;
};
複製代碼
監聽一個事件,這部分其實也比較簡單git
/** * 菜狗子手動註釋TAT * @param { EventEmitter } target event 實例 * @param { string } type 事件名稱 * @pram { function } listener 回調事件,this指向當前event對象 * @pram { boolean } prepend 是否往前插入 * 沒想到吧人家支持插入在前面來解決事件調用順序的問題 * 可是on是日後插,插來插去的怎麼感受好污 * 往前插入請用prependListener */
function _addListener(target, type, listener, prepend) {
var m;
var events;
var existing;
// 檢查你給的監聽器是不是函數
checkListener(listener);
events = target._events;
if (events === undefined) {
// Object.create 這個小技巧就不用介紹了吧,省內存。
events = target._events = Object.create(null);
// event 監聽器是有限制的,如下是爲啥要有限制的解釋,就是方便排查內存問題
// By default EventEmitters will print a warning if more than 10 listeners are
// added to it. This is a useful default which helps finding memory leaks.
target._eventsCount = 0;
} else {
// 這塊 newListener 是不少其餘模塊有用到,好比https啥的。
if (events.newListener !== undefined) {
target.emit('newListener', type,
listener.listener ? listener.listener : listener);
events = target._events;
}
existing = events[type];
}
// 這裏我有些不理解爲啥要用兩種數據結構存監聽器。。
// 一個的時候是函數,多個的是才存成數組。感受就是給本身挖坑啊!!
if (existing === undefined) {
// Optimize the case of one listener. Don't need the extra array object.
events[type] = listener;
++target._eventsCount;
} else {
if (typeof existing === 'function') {
existing = events[type] =
prepend ? [listener, existing] : [existing, listener];
} else if (prepend) {
existing.unshift(listener);
} else {
existing.push(listener);
}
// 一部分檢查監聽器數量的代碼,超過就給你報錯
}
// 優雅的鏈式調用。。。
return target;
}
複製代碼
once的實現,也十分簡單,不贅述了,就是簡單的把上面_addListener包裹下 這種高階函數的寫法卻是比某些人重寫一個要好得多github
function onceWrapper(...args) {
if (!this.fired) {
this.target.removeListener(this.type, this.wrapFn);
this.fired = true;
return Reflect.apply(this.listener, this.target, args);
}
}
function _onceWrap(target, type, listener) {
const state = { fired: false, wrapFn: undefined, target, type, listener };
const wrapped = onceWrapper.bind(state);
wrapped.listener = listener;
state.wrapFn = wrapped;
return wrapped;
}
複製代碼
event還提供了一些獲取監聽函數的,事件名稱,設置最大監聽數的函數。就不贅述了。算法
最後竟然還提供了一個once函數用來觸發某個事件一次 ,能夠說是很良心了數組
在瀏覽器端使用事件機制咱們每每會找個庫或者本身寫一個事件機制。其實瀏覽器原生就有。(兼容性本身測試哈,只作科普向)瀏覽器
文檔看這個數據結構
const eventBus = document.createElement('div')
const customEvent = new CustomEvent('demo',{
test:'test'
})
eventBus.addEventListener('demo',function(e){
console.log(e)
})
eventBus.dispatchEvent(customEvent);
複製代碼
爲啥沒有圖片?由於用有道雲寫的。上傳要高貴的會員,做圖還麻煩。分享只是舉手之勞。app
超級排序算法壓軸
function sortArr(arr){
arr.forEach(el=>setTimeout(_=>console.log(el),el))
}
複製代碼