zoukankan      html  css  js  c++  java
  • 深入理解AngularJs-scope(二)

    深入理解AngularJs-scope(一)中,我们对AngularJs的脏检测及其触发、异步任务队列进行了学习。紧接上一篇文章 深入理解AngularJs-scope(一),我们来看看scope对以下两个特性的实现。

    1. scope的继承机制和 isolated scope;
    2. 依赖于scope的事件系统: $on, $broadcast, $emit;

    scope的继承机制

      在上一篇文章中,我们创建了scope类,并在scope.prototype上实现了脏检测和异步任务相关的各个方法。

      现在,我们来看看AngularJs中,scope之间是如何通过继承联系在一起的,如何从parentScope上获取properties。实际上,得益于javascript的原型继承机制,要实现scope的继承相当的简单,代码如下:

     1 Scope.prototype.$new = function(isolated, parent) {
     2     var child;
     3     parent = parent || this;
     4 
     5     if(isolated) {
     6         child = new Scope();
     7         child.$root = parent.$root;
     8         child.$$asyncQueue = parent.$$asyncQueue;
     9         child.$$postDigestQueue = parent.$$postDigestQueue;
    10         child.$$applyAsyncQueue = parent.$$applyAsyncQueue;
    11     } else {
    12         var ChildScope = function() {};
    13         ChildScope.prototype = this;
    14         child = new ChildScope();
    15     }
    16             
    17     parent.$$children.push(child);
    18 
    19     child.$$watchers = []; // shadow这个prop,使成为每个scope独立拥有这个prop
    20     child.$$listeners = {}; // shadow这个prop, 存储包含自定义事件键值对的对象 
    21     child.$$children = []; // shadow这个prop,使成为每个scope独立拥有这个prop
    22     child.$parent = parent; // 缓存parentScope, 以便让scope上的其他method能够使用它,比如$destroy
    23 
    24     return child;
    25 };

      在我们使用AngularJs进行开发时,$new方法的调用无处不在。大部分情况下并不需要我们手动调用,只是指令自己做了创建scope的工作。

      $new方法有两个参数:

        isolated-布尔值,表示新建的scope是不是 isolated scope(孤立的scope)。

        parent-支持传入一个其他scope来作为 AngularJs scope机制中的parentScope。    

      AngularJs中的scope分为两种,一种是普通scope,如上面代码13行所示,普通scope的prototype指向parentScope, 故能够通过原型链获取到parentScope上的properties。

                      另一种是isolated(孤立) scope, isolated是通过 Scope构造函数创建,它的protorype是指向scope构造函数的,并不是parentScope,所以不能从原型链访问parentScope的properties。

      代码19行至22对新scope的$$watchers、$$listeners、$$children、$parent进行了初始化,因为这些属性是每个scope实例自己拥有、自己维护的。

      scope的事件系统:订阅/发布

      AngularJs 也为开发者提供了一套事件系统供开发者进行事件的绑定和触发,基于 publish/subscribe 设计模式。其中包含3个核心方法:$on, $emit, $broadcast。

      $on: 在scope上绑定自定义事件,即向scope的$$listeners数组中插入listener回调函数。返回事件销毁函数。

     1 Scope.prototype.$on = function(eventName, listener) {
     2     var listeners = this.$$listeners[eventName];
     3 
     4     if(!listeners) {
     5         this.$$listeners[eventName] = listeners = [];
     6     }
     7 
     8     listeners.push(listener);
     9 
    10     return function(eventName) {
    11         var index = listeners.indexOf(listener);
    12                 
    13         if(index >= 0) {
    14             listeners[index] = null;
    15         }
    16     };
    17 };

      $emit: 沿着scope -> parentScope 向上发射事件,执行对应的回调函数。

    Scope.prototype.$emit = function(eventName) {
                var propagationStopped = false;
                var event = {
                    name: eventName,
                    targetScope: this,
                    stopPropagation: function() {
                        propagationStopped = true;
                    },
                    preventDefault: function() {
                        event.defaultPrevented = true;
                    }
                };
    
                //  把event和additionalArgs拼接成新数组,通过apply方法传入listener, 使参数获取方式正常
                var listenerArgs = [event].concat(_.tail(arguments));
                var scope = this;
    
                do {
                    event.currentScope = scope;
                    scope.$$fireEventOnScope(eventName, listenerArgs);
                    scope = scope.$parent;  // 通过改变scope引用 实现向上传播的关键代码
                } while (scope && !propagationStopped);
    
                event.currentScope = null;
    
                return event;
            };

       $broadcast: 向下广播事件,并触发对应的回调函数。$broadcast有一点特殊,一旦开始向下广播,就不能中断。

     1         Scope.prototype.$broadcast = function(eventName) {
     2             var event = {
     3                 name: eventName,
     4                 targetScope: this,
     5                 preventDefault: function() {
     6                     event.defaultPrevented = true;
     7                 }
     8             };
     9 
    10             //  把event和additionalArgs拼接成新数组,通过apply方法传入listener, 使参数获取方式正常
    11             var listenerArgs = [event].concat(_.tail(arguments));
    12 
    13             this.$$everyScope(function(scope) {
    14                 event.currentScope = scope;
    15                 scope.$$fireEventOnScope(eventName, listenerArgs);
    16                 return true;
    17             });
    18 
    19             event.currentScope = null;
    20 
    21             return event;
    22         };    

    这两篇用到的工具函数我都放在后面,由于事件系统的代码比较简单,就不再做过多说明。

    工具方法1: $$fileEventOnScope

     1 /*  $emit 和 $broadcast中 提取出的 fire event listener函数
     2     angularjs源码没有这个方法,其中只是重复了这些代码, 本书作者提出了重复代码
     3  */
     4         Scope.prototype.$$fireEventOnScope = function(eventName, listenerArgs) {
     5 
     6             var listeners = this.$$listeners[eventName] || [];
     7             var i = 0;
     8 
     9             while(i < listeners.length) {
    10                 if(listeners[i] === null) {
    11                     listeners.splice(i, 1);
    12                 } else {
    13                     try {
    14                         listeners[i].apply(null, listenerArgs);
    15                     } catch(e) {
    16                         console.error(e);
    17                     }
    18                     i++;
    19                 }
    20             }
    21 
    22             return event;
    23         };

    工具方法2: $$everyScope

     1 /* 为使$digest循环能够递归child scope上的watchers的工具方法 
     2     这个方法还用于实现$broadcast
     3     */
     4 Scope.prototype.$$everyScope = function(fn) {
     5         if(fn(this)) {
     6             return this.$$children.every(function(child) {
     7                 return child.$$everyScope(fn);
     8             });
     9         } else {
    10             return false;
    11         }
    12 };    

      总结:

      这两篇文章提供了与scope相关的脏检测,$watch, 异步任务,继承机制,事件系统的代码及一点补充分析。弄明白了这些机制是如何实现的,当你在开发工作中用到这些东西时,一定会多一份自信,多一份游刃有余。希望这两篇文章能够帮助到正在使用angular1.x开发的朋友。如有错误,请不吝指出~!谢谢~

  • 相关阅读:
    1.线程是什么?
    eclipse新手知识点1
    《图解Java多线程设计模式》UML用什么软件画?
    Microsoft SQL Server 2008完整+差异备份及还原实战
    xcopy
    导入EXCEL到数据库表中的步骤:
    js判断一个值是空的最快方法是不是if(!value){alert("这个变量的值是null");}
    Pytorch1.6.0 Nvidia Jetson TX2
    python所有的print就会自动打印到log.txt
    python import numpy right but import caffe wrong
  • 原文地址:https://www.cnblogs.com/soccerloway/p/7009389.html
Copyright © 2011-2022 走看看