
在上一节的事件系统源码中,我们留下了一个函数createEventNamespace( this, event )
今天我们来分析这个函数:
首先,我将这个函数摘取出来,放到一个utils.js文件中:
function getEvents( source ) {
if ( !source._events ) {
Object.defineProperty( source, '_events', {
value: {}
} );
}
return source._events;
}
function makeEventNode() {
return {
callbacks: [],
childEvents: []
};
}
function createEventNamespace( source, eventName ) {
const events = getEvents( source );
// First, check if the event we want to add to the structure already exists.
if ( events[ eventName ] ) {
// If it exists, we don't have to do anything.
return;
}
// In other case, we have to create the structure for the event.
// Note, that we might need to create intermediate events too.
// I.e. if foo:bar:abc is being registered and we only have foo in the structure,
// we need to also register foo:bar.
// Currently processed event name.
let name = eventName;
// Name of the event that is a child event for currently processed event.
let childEventName = null;
// Array containing all newly created specific events.
const newEventNodes = [];
// While loop can't check for ':' index because we have to handle generic events too.
// In each loop, we truncate event name, going from the most specific name to the generic one.
// I.e. foo:bar:abc -> foo:bar -> foo.
while ( name !== '' ) {
if ( events[ name ] ) {
// If the currently processed event name is already registered, we can be sure
// that it already has all the structure created, so we can break the loop here
// as no more events need to be registered.
break;
}
// If this event is not yet registered, create a new object for it.
events[ name ] = makeEventNode();
// Add it to the array with newly created events.
newEventNodes.push( events[ name ] );
// Add previously processed event name as a child of this event.
if ( childEventName ) {
events[ name ].childEvents.push( childEventName );
}
childEventName = name;
// If `.lastIndexOf()` returns -1, `.substr()` will return '' which will break the loop.
name = name.substr( 0, name.lastIndexOf( ':' ) );
}
if ( name !== '' ) {
// If name is not empty, we found an already registered event that was a parent of the
// event we wanted to register.
// Copy that event's callbacks to newly registered events.
for ( const node of newEventNodes ) {
node.callbacks = events[ name ].callbacks.slice();
}
// Add last newly created event to the already registered event.
events[ name ].childEvents.push( childEventName );
}
return events;
}
module.exports = {
createEventNamespace
}
为了理解这个方法的执行,我写了一个测试类utils.test.js
import {createEventNamespace } from '../src/utils';
test('createEventNamespace', ()=>{
const events = createEventNamespace({},'foo:bar:des');
expect(events.foo).toEqual({callbacks: [], childEvents: [ 'foo:bar'] });
expect(events.foo.childEvents).toEqual([ 'foo:bar'])
expect(events['foo:bar']).toEqual({ callbacks: [], childEvents: [ 'foo:bar:des' ] });
expect(events['foo:bar'].childEvents).toEqual([ 'foo:bar:des' ])
expect(events['foo:bar:des']).toEqual({ callbacks: [], childEvents: [] });
expect(events['foo:bar:des'].childEvents).toEqual([])
});
test('events keys', ()=>{
const events = createEventNamespace({},'foo:bar:des');
const keys = Object.keys(events);
console.log('keys:',keys)
expect(keys).toEqual(['foo:bar:des','foo:bar','foo']);
});
各位注意了:
1、在createEventNamespace方法中,首先获取当前emitter对应的事件对象,如果在事件对象上已经存在对应的事件,那么直接返回。
2、然后看看事件是否包含命名空间的分隔符 :
注意,这里在创建的时候,会创建一个数据结构:
{
callbacks: [],
childEvents: []
};
也就是每个事件的值对象是一个包含callbacks和childEvents两个属性的结构。
3、然后没迭代一次,分割符就会少一个,具体情况是这样的,foo:bar:des
,第一步创建key为foo:bar:des
的结构,然后是foo:bar
,最后是foo
,
4、最后是设置childEvents属性,还是用第三点的例子来说,foo:bar:des
这个key对象的childEvents属性为空组数,而foo:bar
key对应的childEvents属性就是foo:bar:des
,而foo
key对象的childEvents对应的属性则是foo:bar
。
5、最终产生的数据结构
events: {
'foo:bar:des': { callbacks: [], childEvents: [] },
'foo:bar': { callbacks: [], childEvents: [ 'foo:bar:des' ] },
foo: { callbacks: [], childEvents: [ 'foo:bar' ] }
}
好了,大家应该理解了这个函数的作用了吧