suite('key / value store', function () { functionStore () {} Store.prototype = Object.create(null)
bench('let store = {}', function () { let store = {} store.key = 'value' })
bench('let store = new Map()', function () { let store = newMap() store.set('key', 'value') })
bench('let store = Object.create(null)', function () { let store = Object.create(null) store.key = 'value' })
bench('EventEmitter way', function () { let store = new Store() store.key = 'value' }) })
比较结果:
1 2 3 4 5
key / value store 83,196,978 op/s » let store = {} 4,826,143 op/s » let store = new Map() 7,405,904 op/s » let store = Object.create(null) 165,608,103 op/s » EventEmitter way
效率更高的从数组中去除一个元素
在 EventEmitter#removeListener 这个 API 的实现里,需要从存储的监听器数组中除去一个元素,我们首先想到的就是使用 Array#splice 这个 API ,即 arr.splice(i, 1) 。不过这个 API 所提供的功能过于多了,它支持去除自定义数量的元素,还支持向数组中添加自定义的元素。所以,源码中选择自己实现一个最小可用的:
1 2 3 4 5 6 7 8
// lib/events.js // ...
functionspliceOne(list, index) { for (var i = index, k = i + 1, n = list.length; k < n; i += 1, k += 1) list[i] = list[k]; list.pop(); }
suite('Remove one element from an array', function () { functionspliceOne (list, index) { for (var i = index, k = i + 1, n = list.length; k < n; i += 1, k += 1) { list[i] = list[k] } list.pop() }
bench('Array#splice', function () { let array = [1, 2, 3] array.splice(1, 1) })
bench('EventEmitter way', function () { let array = [1, 2, 3] spliceOne(array, 1) }) })
结果,好吧,秒了:
1 2 3
Remove one element from an array 4,262,168 op/s » Array#splice 54,829,749 op/s » EventEmitter way
functionemitMany(handler, isFn, self, args) { if (isFn) handler.apply(self, args); else { var len = handler.length; var listeners = arrayClone(handler, len); for (var i = 0; i < len; ++i) listeners[i].apply(self, args); } }
// ... functionarrayClone(arr, i) { var copy = newArray(i); while (i--) copy[i] = arr[i]; return copy; }
你可能会问,我既然已经在 g 函数中的第一行中移除了当前的监听器,为何还要使用 fired 这个 flag ?我个人觉得是因为,在 removeListener 这个同步方法中,会将这个 g 函数暴露出来给 removeListener 事件的监听器,所以该 flag 用来保证 once 注册的函数只会被调用一次。