angular scope创建、继承和销毁以及scope生命周期(源码分析)

在js中有函数作用域和全局作用域(es5),angular也存在作用域,它是如何创建的呢?

1.scope的创建和继承

首先ng-app指令会查找作用范围,在它上面会有个根作用域($rootScope):

  function Scope() {this.$id = nextUid();// uid = 0 function nextUid() { return ++uid ;}this.$$phase = this.$parent = this.$$watchers =this.$$nextSibling = this.$$prevSibling =this.$$childHead = this.$$childTail = null;this.$root = this;this.$$destroyed = false;this.$$suspended = false;this.$$listeners = {};this.$$listenerCount = {};this.$$watchersCount = 0;this.$$isolateBindings = null;}

var $rootScope = new Scope();
此时开始创建了根作用域,分析下那些变量的含义
1.$id: 创建一个作用域id就增1(初始id为1)。

2.$$phase: 这个是脏检查的一个阶段变量(如果不知道,可以看下之前的文章,点击这里)。

3.$parent: 父级作用域。

4.$$nextSibling: 上一个兄弟作用域。

5.$$prevSibling: 下一个兄弟作用域。

6.$$childHead: 第一个子作用域。

7.$$childTail: 最后个子作用域。

8.$$watchers: 此作用域下的监听对象(是一个数组来的)。

9.$$destroyed : 初始值为false,当摧毁的时候为true。

10.$$listeners: 此作用域下的注册的监听器事件。

11.$$listenerCount:此作用域下(包括子作用域)的注册监听器事件总数。

12.$$watchersCount:此作用域下(包括子作用域)注册的watchers对象对应的总数。

13.$root: 所有的作用域通过这个属性可以关联到$rootScope。

知道了这些属性,那么作用域是什么创建的呢(总不能一个标签一个作用域?)我们先说简单的继承作用域,那么angular哪些指令会创建子作用域且继承自父作用域:
1、 ng-repeat。
2、 ng-include。
3、 ng-switch。
4、 ng-controller。
5、 directive (scope: true)。
6、 directive(transclude: true)。
这些指令都是自己创建作用域,并且继承父作用域,这里我说的继承和js的原型继承是类似的(ChildScope.prototype = parent),如果不是很懂,可以点击这里。还有一个隔离作用域,我们在写指令过程中,应该经常用到“directive(scope: {…})”,创建子作用域,但并不继承自父作用域。这种情况就和原型继承毫无关系了,知道了这些知识,我们再来看下源码怎么创建作用域的:

 $new: function (isolate, parent) {var child;parent = parent || this;if (isolate) {child = new Scope();child.$root = this.$root;} else {//判断是否有if (!this.$$ChildScope) {//子构造函数的创建this.$$ChildScope = createChildScopeClass(this);}//子作用域的创建child = new this.$$ChildScope();}//把自己的父,兄弟姐妹,儿子整明白(要不然乱套了)child.$parent = parent;// 孩子的前一个兄弟节点为父亲的最后一个孩子child.$$prevSibling = parent.$$childTail;if (parent.$$childHead) {parent.$$childTail.$$nextSibling = child;parent.$$childTail = child;} else {parent.$$childHead = parent.$$childTail = child;}if (isolate || parent !== this) child.$on('$destroy', destroyChildScope);return child;}function createChildScopeClass(parent) {function ChildScope() {this.$$watchers = this.$$nextSibling =this.$$childHead = this.$$childTail = null;this.$$listeners = {};this.$$listenerCount = {};this.$$watchersCount = 0;this.$id = nextUid();this.$$ChildScope = null;this.$$suspended = false;}ChildScope.prototype = parent;return ChildScope;
}

angular是通过指令和$new方法来创建作用域,我们来分析里面源码的:
首先有2个参数,一个是个布尔值,用于指定创建的作用域是否为一个隔离作用域,另外一个scope对象。传入的scope对象会被指定为当前正在创建的scope的父亲,那么我们如果什么都不传会怎么样,先判断是否已经创建了子scope,如果没有创建就通过createChildScopeClass构造函数new出子scope并继承父(ChildScope.prototype = parent),然后要把它的爸爸啊,儿子啊,兄弟姐妹,要整明白,要不然乱套了,这段代码比较好理解,不理解就画个图会很容易理解,最后,当子scope为隔离scope或者子scope的父亲不是当前scope时,声明一个回调函数用于销毁事件。这是因为在上述两种情况下,原型继承并没有发生作用。原因是压根就没有对原型继承链进行设置,即没有调用:ChildScope.prototype = parent。从上面的代码来看,scope的创建过程并不复杂。主要是设置好原型继承链并将新创建的scope和已经存在的scope树形继承结构进行关联。

2.scope的销毁过程

那么scope的销毁过程又是如何进行的呢?废话不说,直接上源代码:

$destroy: function () {// 判断是否已销毁,避免重复销毁.if (this.$$destroyed) return;var parent = this.$parent;//向子scope广播销毁事件this.$broadcast('$destroy');//默认值为false改为truethis.$$destroyed = true;if (this === $rootScope) {//当销毁的对象为根scope时,销毁整个应用$browser.$$applicationDestroyed();}//销毁了之后把Watcher对象监听记数信息减少(包括父scope上的记数信息),incrementWatchersCount(this, -this.$$watchersCount);//销毁了之后把监听事件记数信息减少(包括父scope上的记数信息)for (var eventName in this.$$listenerCount) {decrementListenerCount(this, this.$$listenerCount[eventName], eventName);}//既然销毁了是不是也要把自己的父,兄弟姐妹,儿子整明白(要不然乱套了)if (parent && parent.$$childHead === this) parent.$$childHead = this.$$nextSibling;if (parent && parent.$$childTail === this) parent.$$childTail = this.$$prevSibling;if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling;if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling;//然后把定义在scope上的方法全部销毁this.$destroy = this.$digest = this.$apply = this.$evalAsync = this.$applyAsync = noop;//销毁所有的回调事件this.$on = this.$watch = this.$watchGroup = function () {return noop;};this.$$listeners = {};// Disconnect the next sibling to prevent `cleanUpScope` destroying those toothis.$$nextSibling = null;//IE9中就会出现内存泄漏,写了个兼容IE9方法cleanUpScope(this);
},

上面源代码我注释已经写得很清楚了,销毁的过程,其实是对当前正被销毁的scope的计数信息进行修正,还需要修正它所有的父scope的计数信息。这一点从上面两个函数的while循环中看出来的,然后把自己的父,兄弟姐妹,儿子整明白(要不然乱套了),最后对被销毁scope上各种方法设置为noop,同时也销毁scope的各种回调,目的都是防止误操作。scope生命周期中最重要的创建和销毁就说完了,下一篇文件我会讲下angular的事件机制和源码分析,好了,欢乐的时光总是过得特别快,又到时候和大家讲拜拜!!


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部