[AngularJS面面观] 10. scope事件机制 - $emit,$broadcast以及
在上一篇文章中,介绍了事件机制背后的订阅-发布模式以及angular事件的生命周期。 本文继续介绍和事件机制相关的几个重要组成部分: 事件对象的组成在调用事件回调函数的时候,事件对象会被构建出来并传入到回调函数中去。那么这个事件对象包含了哪些字段呢?由于事件的触发入口只有下面将会介绍的 // $emit
$emit: function(name,args) {
var empty = [],namedListeners,scope = this,stopPropagation = false,event = {
name: name,targetScope: scope,stopPropagation: function() {stopPropagation = true;},preventDefault: function() {
event.defaultPrevented = true;
},defaultPrevented: false
},listenerArgs = concat([event],arguments,1),i,length;
// ...
}
// $broadcast
$broadcast: function(name,args) {
var target = this,current = target,next = target,targetScope: target,defaultPrevented: false
};
if (!target.$$listenerCount[name]) return event;
var listenerArgs = concat([event],listeners,length;
// ...
}
两者的构建方法略有差别,但是共通的部分也不少,列举如下: 另外在 在构建完成基本的事件对象后,还会根据该对象和实际传入到 这个concat函数定义在Angular.js中: // 其中的slice对象就是Array类型上的slice方法
function concat(array1,array2,index) {
return array1.concat(slice.call(array2,index));
}
因此 除了在event对象构建之初就能够确定的 DOM事件:事件发生的DOM节点为 这样对比一下是不是一目了然呢。 事件与scope继承树为了继续讨论后面的内容,我画了一张scope树形结构图作为例子(还是请忽略我的绘图技术,随手画的。囧)。 该结构由5个节点组成:其中4是一个隔离scope(其中的I表示Isolated)。 向上传递的$emit创建好了事件对象,下面来看看事件是如何向上传递的,在传递的过程中有哪些值得留意的行为: $emit: function(name,args) {
// 创建事件对象以及各种变量的声明
// ......
// 遍历开始
do {
namedListeners = scope.$$listeners[name] || empty;
event.currentScope = scope;
for (i = 0,length = namedListeners.length; i < length; i++) {
// 如果存在被注销的回调函数,则整理回调函数数组以消除null元素
if (!namedListeners[i]) {
namedListeners.splice(i,1);
i--;
length--;
continue;
}
try {
// 执行当前scope上注册的所有name对应的回调函数
namedListeners[i].apply(null,listenerArgs);
} catch (e) {
$exceptionHandler(e);
}
}
// 如果任何回调设置了stopPropagation,那么终止冒泡过程
if (stopPropagation) {
event.currentScope = null;
return event;
}
// 向上遍历
scope = scope.$parent;
} while (scope);
event.currentScope = null;
return event;
}
值得留意的有以下几个地方: 拿之前的scope继承结构作为例子,当在4号隔离scope上调用$emit时,遍历的顺序是4->2->1。 向下广播的$broadcast
$broadcast: function(name,args) {
// 创建事件对象以及各种变量的声明
// ......
if (!target.$$listenerCount[name]) return event;
while ((current = next)) {
event.currentScope = current;
listeners = current.$$listeners[name] || [];
for (i = 0,length = listeners.length; i < length; i++) {
// 如果存在被注销的回调函数,则整理回调函数数组以消除null元素
if (!listeners[i]) {
listeners.splice(i,1);
i--;
length--;
continue;
}
try {
listeners[i].apply(null,listenerArgs);
} catch (e) {
$exceptionHandler(e);
}
}
// 和digest循环中一样的深度优先遍历(DFS)
// 不同点:会检查$$listenerCount
if (!(next = ((current.$$listenerCount[name] && current.$$childHead) ||
(current !== target && current.$$nextSibling)))) {
while (current !== target && !(next = current.$$nextSibling)) {
current = current.$parent;
}
}
}
event.currentScope = null;
return event;
}
值得留意的有以下几点: 事件的停止传播以及阻止默认行为关于这一点,其实在上面介绍 至此关于angular中的事件机制就介绍完毕了。 下一篇文章会介绍事件机制在angular框架中的应用。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |