进入正文前的说明:本文中的示例代码并非AngularJs源码,而是来自书籍<<Build Your Own AngularJs>>, 这本书的作者仅依赖jquery和lodash一步一步构建出AngularJs的各核心模块,对全面理解AngularJs有非常巨大的帮助。若有正在使用AngulaJs攻城拔寨并且希望完全掌握手中武器的小伙伴,相信能对你理解AngularJs带来莫大帮助,感谢作者。
在这篇文章中,希望能让您理清楚以下几项与scope相关的功能:
1.dirty-checking(脏检测)核心机制,主要包括:$watch 和 $digest;
2.几种不同的触发$digest循环的方式:$eval, $apply, $evalAsync, $applyAsync;
3.scope的继承机制以及isolated scope;
4.依赖于scope的事件循环:$on, $broadcast, $emit.
现在开始我们的第一部分:scope和dirty-checking
dirty-checking(脏检测)原理简述:scope通过$watch方法向this.$$watchers数组中添加watcher对象(包含watchFn, listenerFn, valueEq, last 四个属性)。每当$digest循环被触发时,它会遍历$$watchers数组,执行watcher中的watchFn,获取当前scope上某属性的值(一个watcher对应scope上一个被监听属性),然后去同watcher中的last(上一次的值)做比较,若两值不相等,就执行listenerFn。
function Scope() { this.$$watchers = []; // 监听器数组 this.$$lastDirtyWatch = null; // 每次digest循环的最后一个脏的watcher, 用于优化digest循环 this.$$asyncQueue = []; // scope上的异步队列 this.$$applyAsyncQueue = []; // scope上的异步apply队列 this.$$applyAsyncId = null; //异步apply信息 this.$$postDigestQueue = []; // postDigest执行队列 this.$$phase = null; // 储存scope上正在做什么,值有:digest/apply/null this.$root = this; // rootScope this.$$listeners = {}; // 存储包含自定义事件键值对的对象 this.$$children = []; // 存储当前scope的儿子Scope,以便$digest循环递归}
实际上scope就是一个普通的javascript对象,一个类构造函数,可以通过new进行实例化。根据脏检测的原理,接下来,我们一起看看scope的$watch方法的实现。
/* $watch方法:向watchers数组中添加watcher对象,以便对应调用 */Scope.prototype.$watch = function(watchFn, listenerFn, valueEq) { var self = this; watchFn = $parse(watchFn); // watchDelegate: 针对watch expression是常量和 one-time-binding的情况,进行优化。在第一次初始化之后删除watch if(watchFn.$$watchDelegate) { return watchFn.$$watchDelegate(self, listenerFn, valueEq, watchFn); } var watcher = { watchFn: watchFn, listenerFn: listenerFn || function() {}, valueEq: !!valueEq, last: initWatchVal }; this.$$watchers.unshift(watcher); this.$root.$$lastDirtyWatch = null; return function() { var index = self.$$watchers.indexOf(watcher); if(index >= 0) { self.$$watchers.splice(index, 1); self.$root.$$lastDirtyWatch = null; } };};
新闻热点
疑难解答
图片精选