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);

 

 

 

你可能感兴趣的:(PhoneGap)