念奴娇 赤壁怀古
     [北宋]苏轼
大江东去,浪淘尽,千古风流人物。
故垒西边,人道是,三国周郎赤壁。
乱石穿空,惊涛拍岸,卷起千堆雪。
江山如画,一时多少豪杰。

遥想公瑾当年,小乔初嫁了,雄姿英发。
羽扇纶巾,谈笑间,樯橹灰飞烟灭。
故国神游,多情应笑我,早生华发。
人生如梦,一樽还酹江月。

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)

 

 

 

posted @ 2012-08-13 16:51  linjisong  阅读(3903)  评论(0编辑  收藏  举报