1
1
import type { EventData , ListenerEntry , Observable } from '../observable/index' ;
2
- import { MutationSensitiveArray } from '../mutation-sensitive-array' ;
3
2
4
3
// This file contains some of Core's hot paths, so attention has been taken to
5
4
// optimise it. Where specified, optimisations made have been informed based on
@@ -13,7 +12,7 @@ const timeOrigin = Date.now();
13
12
* optional accesses, so reusing the same one and treating it as immutable
14
13
* avoids unnecessary allocations on a relatively hot path of the library.
15
14
*/
16
- const emptyArray = new MutationSensitiveArray < ListenerEntry > ( ) ;
15
+ const emptyArray : ListenerEntry [ ] = [ ] ;
17
16
18
17
/**
19
18
* Recycling the event path array rather than allocating a new one each time
@@ -62,7 +61,7 @@ export class DOMEvent implements Event {
62
61
Object . defineProperty ( DOMEvent . prototype , 'currentTarget' , { value : null , writable : true } ) ;
63
62
Object . defineProperty ( DOMEvent . prototype , 'target' , { value : null , writable : true } ) ;
64
63
Object . defineProperty ( DOMEvent . prototype , 'propagationState' , { value : EventPropagationState . resume , writable : true } ) ;
65
- Object . defineProperty ( DOMEvent . prototype , 'listenersLive ' , { value : emptyArray , writable : true } ) ;
64
+ Object . defineProperty ( DOMEvent . prototype , 'listeners ' , { value : emptyArray , writable : true } ) ;
66
65
Object . defineProperty ( DOMEvent . prototype , 'listenersLazyCopy' , { value : emptyArray , writable : true } ) ;
67
66
}
68
67
@@ -152,8 +151,7 @@ export class DOMEvent implements Event {
152
151
// lazily - i.e. only take a clone if a mutation is about to happen.
153
152
// This optimisation is particularly worth doing as it's very rare that
154
153
// an event listener callback will end up modifying the listeners array.
155
- private declare listenersLive : MutationSensitiveArray < ListenerEntry > ;
156
- private declare listenersLazyCopy : ListenerEntry [ ] ;
154
+ private declare listeners : ListenerEntry [ ] ;
157
155
158
156
/**
159
157
* Returns the event's timestamp as the number of milliseconds measured
@@ -267,7 +265,7 @@ export class DOMEvent implements Event {
267
265
*/
268
266
// Taking multiple params rather than a single property bag saves about 100
269
267
// nanoseconds per call.
270
- dispatchTo ( target : Observable , data : EventData , getGlobalEventHandlersPreHandling ?: ( ) => MutationSensitiveArray < ListenerEntry > , getGlobalEventHandlersPostHandling ?: ( ) => MutationSensitiveArray < ListenerEntry > ) : boolean {
268
+ dispatchTo ( target : Observable , data : EventData , getGlobalEventHandlersPreHandling ?: ( ) => ListenerEntry [ ] , getGlobalEventHandlersPostHandling ?: ( ) => ListenerEntry [ ] ) : boolean {
271
269
if ( this . eventPhase !== DOMEvent . NONE ) {
272
270
throw new Error ( 'Tried to dispatch a dispatching event' ) ;
273
271
}
@@ -308,7 +306,7 @@ export class DOMEvent implements Event {
308
306
// event. This keeps behaviour as consistent with DOM Events as
309
307
// possible.
310
308
311
- this . listenersLazyCopy = this . listenersLive = getGlobalEventHandlersPreHandling ?.( ) || emptyArray ;
309
+ this . listeners = getGlobalEventHandlersPreHandling ?.( ) || emptyArray ;
312
310
this . handleEvent ( data , true , DOMEvent . CAPTURING_PHASE , removeGlobalEventListener , target . constructor ) ;
313
311
314
312
const eventPath = this . getEventPath ( target , 'capture' ) ;
@@ -321,7 +319,7 @@ export class DOMEvent implements Event {
321
319
this . currentTarget = currentTarget ;
322
320
this . eventPhase = this . target === this . currentTarget ? DOMEvent . AT_TARGET : DOMEvent . CAPTURING_PHASE ;
323
321
324
- this . listenersLazyCopy = this . listenersLive = currentTarget . getEventList ( this . type ) || emptyArray ;
322
+ this . listeners = currentTarget . getEventList ( this . type ) || emptyArray ;
325
323
this . handleEvent ( data , false , DOMEvent . CAPTURING_PHASE , currentTarget . removeEventListener , currentTarget ) ;
326
324
327
325
if ( this . propagationState !== EventPropagationState . resume ) {
@@ -336,7 +334,7 @@ export class DOMEvent implements Event {
336
334
const currentTarget = eventPath [ i ] ;
337
335
this . eventPhase = this . target === this . currentTarget ? DOMEvent . AT_TARGET : DOMEvent . BUBBLING_PHASE ;
338
336
339
- this . listenersLazyCopy = this . listenersLive = currentTarget . getEventList ( this . type ) || emptyArray ;
337
+ this . listeners = currentTarget . getEventList ( this . type ) || emptyArray ;
340
338
this . handleEvent ( data , false , DOMEvent . BUBBLING_PHASE , currentTarget . removeEventListener , currentTarget ) ;
341
339
342
340
if ( this . propagationState !== EventPropagationState . resume ) {
@@ -357,41 +355,21 @@ export class DOMEvent implements Event {
357
355
this . eventPhase = DOMEvent . BUBBLING_PHASE ;
358
356
}
359
357
360
- this . listenersLazyCopy = this . listenersLive = getGlobalEventHandlersPostHandling ?.( ) || emptyArray ;
358
+ this . listeners = getGlobalEventHandlersPostHandling ?.( ) || emptyArray ;
361
359
this . handleEvent ( data , true , DOMEvent . BUBBLING_PHASE , removeGlobalEventListener , target . constructor ) ;
362
360
363
361
this . resetForRedispatch ( ) ;
364
362
return ! this . defaultPrevented ;
365
363
}
366
364
367
- // Creating this upon class construction as an arrow function rather than as
368
- // an inline function bound afresh on each usage saves about 210 nanoseconds
369
- // per run of dispatchTo().
370
- //
371
- // Creating it on the prototype and calling with the context instead saves a
372
- // further 125 nanoseconds per run of dispatchTo().
373
- //
374
- // Creating it on the prototype and binding the context instead saves a
375
- // further 30 nanoseconds per run of dispatchTo().
376
- private beforeCurrentListenersMutation ( ) {
377
- // Cloning the array via spread syntax is
17AE
up to 180 nanoseconds
378
- // faster per run than using Array.prototype.slice().
379
- this . listenersLazyCopy = [ ...this . listenersLive ] ;
380
- this . listenersLive . beforeMutation = null ;
381
- }
382
-
383
365
// Taking multiple params instead of a single property bag saves 250
384
366
// nanoseconds per dispatchTo() call.
385
367
private handleEvent ( data : EventData , isGlobal : boolean , phase : 0 | 1 | 2 | 3 , removeEventListener : ( eventName : string , callback ?: any , thisArg ?: any , capture ?: boolean ) => void , removeEventListenerContext : unknown ) {
386
- // Set a listener to clone the array just before any mutations.
387
- //
388
- // Lazy-binding this (binding it at the time of calling, rather than
389
- // eagerly) unexpectedly seems to slow things down - v8 may be applying
390
- // some sort of optimisation or something.
391
- this . listenersLive . beforeMutation = this . beforeCurrentListenersMutation . bind ( this ) ;
368
+ // Clone the array just before any mutations.
369
+ const listeners = [ ...this . listeners ] ;
392
370
393
- for ( let i = this . listenersLazyCopy . length - 1 ; i >= 0 ; i -- ) {
394
- const listener = this . listenersLazyCopy [ i ] ;
371
+ for ( let i = listeners . length - 1 ; i >= 0 ; i -- ) {
372
+ const listener = listeners [ i ] ;
395
373
396
374
// Assigning variables this old-fashioned way is up to 50
397
375
// nanoseconds faster per run than ESM destructuring syntax.
@@ -417,7 +395,7 @@ export class DOMEvent implements Event {
417
395
// MutationSensitiveArray called afterRemoval, similar to
418
396
// beforeMutation) to allow O(1) lookup, but it went 1000 ns slower
419
397
// in practice, so it stays!
420
- if ( ! this . listenersLive . includes ( listener ) ) {
398
+ if ( ! this . listeners . includes ( listener ) ) {
421
399
continue ;
422
400
}
423
401
@@ -458,7 +436,6 @@ export class DOMEvent implements Event {
458
436
459
437
// Make sure we clear the callback before we exit the function,
460
438
// otherwise we may wastefully clone the array on future mutations.
461
- this . listenersLive . beforeMutation = null ;
462
439
}
463
440
464
441
/**
@@ -472,7 +449,6 @@ export class DOMEvent implements Event {
472
449
this . target = null ;
473
450
this . eventPhase = DOMEvent . NONE ;
474
451
this . propagationState = EventPropagationState . resume ;
475
- this . listenersLive = emptyArray ;
476
- this . listenersLazyCopy = emptyArray ;
452
+ this . listeners = emptyArray ;
477
453
}
478
454
}
0 commit comments