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

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

PhoneGap源码分析10——cordova/builder

  这一篇我们分析cordova/builder这个模块。

  在具体看这个模块之前,先复习一下Object类型。

(1)Object类型是所有它的实例的基础,所有的内置类型都是通过原型继承的方式继承了Object类。

(2)Object的每个实例都有一个Constructor属性,指向创建这个实例的函数。

(3)Object的每个实例都有下面的方法:

A、hasOwnProperty(propertyName)检查propertyName是否在当前实例中(在实例原型中同样返回false)。

B、propertyIsEnumerable(propertyName):检查propertyName是否可以使用for-in语句访问(只要属性没有特别标识,在实例原型中的属性也可以访问)。

C、isPorpertyOf(object):检查object是否为另一个对象的原型。

D、toLocalString()、toString()、valueOf()。

  因此可以使用下面的结构来遍历实例中的属性:

for(var propertyName in object){
  if(object.hasOwnPorperty(propertyName)){
    //循环处理
  }
}

  现在再来看cordova/builder这个模块:

 1 define("cordova/builder", function(require, exports, module) {
 2 var utils = require('cordova/utils');//导入工具类,由于前面模块中已经创建过,所以这里不会再次创建,而是直接返回
 3 
 4 function each(objects, func, context) {
 5 }
 6 
 7 function include(parent, objects, clobber, merge) {
 8 }
 9 
10 function recursiveMerge(target, src) {
11 }
12 
13 //返回值
14 module.exports = {
15 };
16 
17 });

有了前面的经验,我们知道,第14行的赋值实际上就是整个构造函数的返回值,其它的则是声明了三个内部函数,用于构建返回值。
1、展开第4行的函数each,这正是开始分析的一个循环遍历实例属性的结构:

function each(objects, func, context) {
    for (var prop in objects) {
        if (objects.hasOwnProperty(prop)) {//循环遍历objects实例的属性
            func.apply(context, [objects[prop], prop]);//在context环境中调用func处理属性,参数是属性值和属性名
} } }

2、再展开第10行的函数recursiveMerge(递归合并),这里也是一个循环遍历实例属性的结构,只是遍历处理复杂一些:

//递归合并对象属性(会覆盖目标对象中原有属性)
function recursiveMerge(target, src) {
    for (var prop in src) {
        if (src.hasOwnProperty(prop)) {//循环遍历源对象src的实例属性
            if (typeof target.prototype !== 'undefined' && target.prototype.constructor === target) {
                // 如果目标对象是一个构造函数,将属性赋值到其原型对象上,使得所有通过target构建的实例都可以共享src的实例属性
                // 这里实际上有一个隐患,如果src有一个引用类型的实例属性ref,然后通过target创建了两个实例A、B,修改A的ref会也就修改了B的ref
                target.prototype[prop] = src[prop];
            } else {
                // 简单类型直接赋值,object类型递归赋值
                target[prop] = typeof src[prop] === 'object' ? recursiveMerge(target[prop], src[prop]) : src[prop];
            }
        }
    }
    return target;
}

3、再来看第7行的函数include:

function include(parent, objects, clobber, merge) {
    each(objects, function (obj, key) {    
    });
}

在这里,调用了开始声明的each函数,传入对象objects和处理函数fn,但是并没有传入context,也就是说,在each内部使用apply调用函数处理属性时,是在全局环境下执行的,从each函数的源码中,我们知道,这里的处理函数fn的两个参数是属性值和属性名。具体展开fn的代码看看:

function (obj, key) {
        try {
          var result = obj.path ? require(obj.path) : {};//根据属性值中的路径,导入相应模块

          if (clobber) {
                if (typeof parent[key] === 'undefined') {
                  parent[key] = result;//如果目标对象不存在该属性,直接赋值为导入的模块
              } else if (typeof obj.path !== 'undefined') {
                  //目标对象已有该属性,并且已导入属性值对应模块
                  if (merge) {
                      recursiveMerge(parent[key], result);//要求合并,以新导入模块为准进行合并
                  } else {
                      parent[key] = result;
                  }
              }
              result = parent[key];//将已经处理好的属性反写至中间对象,当目标对象有子对象时,再递归处理
          } else {
            if (typeof parent[key] == 'undefined') {//如果目标对象不存在该属性,直接赋值为导入的模块
              parent[key] = result;
            } else if (merge && typeof obj.path !== 'undefined') {
              // 目标对象已有该属性,并且要求合并,则以原属性为准进行合并,并将合并后的属性
              recursiveMerge(result, parent[key]);
              parent[key] = result;
            } else {//目标对象已有该属性,使用该属性作为下一次递归处理的参数
              result = parent[key];
            }
          }

          if (obj.children) {//递归处理
            include(result, obj.children, clobber, merge);
          }
        } catch(e) {
          utils.alert('Exception building cordova JS globals: ' + e + ' for key "' + key + '"');
        }
    }

(1)最外层的try{}catch(e){}结构是捕获异常,并根据utils中的alert警告或写日志。
(2)根据代码中的注释,可以反过来推出include的参数parent就是需要处理属性的目标对象、objects就是需要处理的属性集、clobber则是表示以哪个为基准,为true时以objects中属性为准,false时以原对象parent中属性(如果parent未定义该属性,则仍以objects为准)为准、merge则表示是否递归合并属性及其子属性,为true时递归合并,为false时则只是简单赋值当前属性,不递归。

(3)对于objects中的属性,递归处理。

4、分析完了三个内部函数,再来看第14行的返回值:

//返回值
module.exports = {
    build: function (objects) {
        return {
            intoButDontClobber: function (target) {//以target中原属性为准,并且不递归合并子属性
                include(target, objects, false, false);
            },
            intoAndClobber: function(target) {//以objects中属性为准,但不递归合并子属性
                include(target, objects, true, false);
            },
            intoAndMerge: function(target) {//以objects属性为准并且递归合并子属性
                include(target, objects, true, true);
            }
        };
    }
};

返回值是一个对象字面量,只有一个属性build,而这个build是一个函数,接受一个属性修饰参数,返回一个包含了三种不同修饰方式函数的对象。源码中函数内嵌比较多,不易理解,直接返回include好理解一点(当然,调用方式也需要相应修改):

module.exports = {build: include}

调用方式做如下修改:

var builder = require('cordova/builder'),
    base = require('cordova/common');
builder.build(base.objects).intoButDontClobber(window);
// 修改为
builder.build(window, base.objects, false, false);

 

 

 

posted @ 2012-08-15 12:48  linjisong  阅读(2056)  评论(1编辑  收藏  举报