zoukankan      html  css  js  c++  java
  • PhoneGap源码分析7——cordova/channel

      分析完了cordova/utils之后,回到cordova/channel这个模块来,这个模块是实现事件监听的基础,当然,我们的焦点是它的构造函数,源码中是匿名的,这里为了行文方便,姑且称之为factory。

      要分析一个函数,从外部来说,知道怎么调用它就行了,这也就是通常所说的暴露在外的API,我们知道,factory是作为一个参数来传递给define函数的,并在第一次require中实际调用的,之后就清除了这个构造函数,回过头来看看这个调用的代码:

    1 function build(module) {
    2         var factory = module.factory;
    3         module.exports = {};
    4         delete module.factory;  
    5         factory(require, module.exports, module);
    6         return module.exports;
    7 }

      实际调用在第5行,传入三个参数,没有使用返回值,调用之后返回的是module.exports,而这正是其中一个传入参数,我们可以由此判断,在factory这个函数内部,module.exports被修饰,因此,下面在分析factory时,需要重点关注module.exports这个参数(channel)的变更。

      从外部调用角度来分析函数,虽然也是一个不错的方法,尤其是对不关注具体实现的时候,但是,也可能会有以偏概全的不利影响,毕竟,很多时候外部调用很难穷尽所有用法。现在,我们再从内部代码来分析一下factory。

     1 function(require, exports, module) {
     2 var utils = require('cordova/utils');
     3 
     4 var Channel = function(type, opts) {
     5 },
     6     channel = {
     7      };
     8 
     9 function forceFunction(f) {
    10 }
    11 
    12 Channel.prototype.subscribe = function(f, c, g) {
    13 };
    14 
    15 Channel.prototype.subscribeOnce = function(f, c) {
    16  };
    17 
    18 Channel.prototype.unsubscribe = function(g) {
    19  };
    20 
    21 Channel.prototype.fire = function(e) {
    22 };
    23 
    24 channel.create('onDOMContentLoaded');
    25 
    26 channel.create('onNativeReady');
    27 
    28 channel.create('onCordovaReady');
    29 
    30 channel.create('onCordovaInfoReady');
    31 
    32 channel.create('onCordovaConnectionReady');
    33 
    34 channel.create('onDeviceReady');
    35 
    36 channel.create('onResume');
    37 
    38 channel.create('onPause');
    39 
    40 channel.create('onDestroy');
    41 
    42 channel.waitForInitialization('onCordovaReady');
    43 channel.waitForInitialization('onCordovaConnectionReady');
    44 
    45 module.exports = channel;
    46 
    47 }

    1、从返回值上看,这里没有明确的返回,但是根据前面的分析,我们知道,第45行的module.exports = channel;就相当于是将channel这个变量作为返回值返回了。
    2、从结构上看,这个构造函数,先是定义了三个内部变量utils、Channel、channel,然后是修改Channel的原型,接着使用channel创建一系列事件并最终返回。

    3、再从动态执行的角度来看一下:

    (1)当然,由于函数声明提升,首先是定义第9行开始的函数forceFunction:

    function forceFunction(f) {
        if (f === null || f === undefined || typeof f != 'function') throw "Function required as first argument!";
    }

    这个函数很简单,就是判断传入的参数f是不是一个函数,如果不是就抛出异常。这里稍微提一下等号“==”和全等号“===”的区别,等号在比较之前,如果需要,会自动转换数据类型,而全等号不会自动转换类型,比如'2'==2而'2' !==2。
    (2)第2-7行是定义三个内部变量并初始化,utils就是前面两篇文章分析的工具模块;Channel是一个普通的构造器方法;channel则是最终返回的结果,是通过对象字面量定义的。

    4、将Channel的定义及其原型的修改放在一起,我们可以看到一个典型的创建对象的方法:通过构造器初始化内部变量,从而让各个实例互相独立,通过修改函数原型共享实例方法。原型中定义了subscribe、unsubscribe、subscribeOnce、fire四个方法:

    (1)、subscribe(向事件通道注入事件处理函数)

     1 Channel.prototype.subscribe = function(f, c, g) {
     2     // need a function to call
     3     forceFunction(f);
     4 
     5     var func = f;
     6     if (typeof c == "object") { func = utils.close(c, f); }
     7 
     8     g = g || func.observer_guid || f.observer_guid;
     9     if (!g) {
    10         // first time we've seen this subscriber
    11         g = this.guid++;
    12     }
    13     else {
    14         // subscriber already handled; dont set it twice
    15         return g;
    16     }
    17     func.observer_guid = g;
    18     f.observer_guid = g;
    19     this.handlers[g] = func;
    20     this.numHandlers++;
    21     if (this.events.onSubscribe) this.events.onSubscribe.call(this);
    22     if (this.fired) func.call(this);
    23     return g;
    24 };

    subscribe是向通道注入事件处理函数并返回一个自增长的ID(可以用来反注入或内部查找),多次注入会直接返回。在第8行有一个用法:g = g || func.observer_guid || f.observer_guid,也就是取第一个为true的值(null、undefined、false、0都视为false),第一次会将函数注入通道,并且触发注入事件(如果有),然后如果需要立即触发,则再触发注入的函数。
    (2)unsubscribe(解除事件处理函数,反注入)

    Channel.prototype.unsubscribe = function(g) {
        // need a function to unsubscribe
        if (g === null || g === undefined) { throw "You must pass _something_ into Channel.unsubscribe"; }
    
        if (typeof g == 'function') { g = g.observer_guid; }
        var handler = this.handlers[g];
        if (handler) {
            if (handler.observer_guid) handler.observer_guid=null;
            this.handlers[g] = null;
            delete this.handlers[g];
            this.numHandlers--;
            if (this.events.onUnsubscribe) this.events.onUnsubscribe.call(this);
        }
    };

    反注入比较简单,就是将前面注入的处理函数给删除,并且触发反注入事件(如果有),可以传入注入的函数,或者传入注入函数时返回的ID。
    (3)subscribeOnce(注入事件处理,但是只调用一次,然后就自动解除和之间的关联关系)

    Channel.prototype.subscribeOnce = function(f, c) {
        // need a function to call
        forceFunction(f);
    
        var g = null;
        var _this = this;
        var m = function() {
            f.apply(c || null, arguments);
            _this.unsubscribe(g);
        };
        if (this.fired) {//立即触发,就直接调用
            if (typeof c == "object") { f = utils.close(c, f); }
            f.apply(this, this.fireArgs);
        } else {//注入“调用并反注入”的函数
            g = this.subscribe(m);
        }
        return g;
    };

    注入只调用一次的函数,如果需要立即触发,实际上只需要触发而不需要注入,不需要立即触发,将原函数调用和反注入原函数作为一个新的事件处理函数注入。
    (4)fire(触发所有注入的函数)

    Channel.prototype.fire = function(e) {
        if (this.enabled) {
            var fail = false;
            this.fired = true;
            for (var item in this.handlers) {
                var handler = this.handlers[item];
                if (typeof handler == 'function') {
                    var rv = (handler.apply(this, arguments)===false);
                    fail = fail || rv;
                }
            }
            this.fireArgs = arguments;
            return !fail;
        }
        return true;
    };

    就是在当前状态可用的情况下调用所有注入的事件处理函数。
    在这里复习一下函数调用的两种方式:

    A、使用括号调用:functionName(args);

    B、使用函数实例方法apply或call方法调用:如functionName.apply(scope,args)或functionName.call(scope, arg1, arg2,...,argn)。这里关键的是scope可用改变functionName这个函数的作用域。而apply和call的区别就是后面的参数,前者传入数组或类数组(如arguments),后者需要将参数一一列举。

    5、返回值channel

    先是通过对象字面量定义,然后创建一系列事件,注意的是,Channel的subscribe方法是同一个事件的多次处理方法,而这里创建的则是初始启动的一系列事件。这些事件分别是:onDOMContentLoaded         DOM文档已经加载并解析
      onNativeReady              Cordova本地已经就绪
      onCordovaReady             Cordova中所有Javascript对象已经创建完毕,开始运行插件
      onCordovaInfoReady         设备属性可以访问
      onCordovaConnectionReady   Cordova连接已经设置好
      onDeviceReady            Cordova已经加载完成
      onResume                  启动/恢复
      onPause                     暂停
      onDestroy                   应用销毁(通常会使用window.onload)

    拣尽寒枝不肯栖,寂寞沙洲冷。
    郴江幸自绕郴山,为谁流下潇湘去?
    欲将心事付瑶琴,知音少,弦断有谁听?
    倩何人,唤取红巾翠袖,揾英雄泪!
    零落成泥碾作尘,只有香如故!
  • 相关阅读:
    ES6学习--函数剩余参数 (rest参数)
    ES6学习 --函数参数默认值与解构赋值默认值
    ES6学习--Array.from()方法
    02ython基础知识(一)
    01 Python初探
    c#利用IronPython调用python的过程种种问题
    Android 对话框(Dialogs)
    不可不知的安卓屏幕知识
    C#中的Split用法以及详解
    关于XML文档操作类
  • 原文地址:https://www.cnblogs.com/linjisong/p/2636137.html
Copyright © 2011-2022 走看看