zoukankan      html  css  js  c++  java
  • 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);
    拣尽寒枝不肯栖,寂寞沙洲冷。
    郴江幸自绕郴山,为谁流下潇湘去?
    欲将心事付瑶琴,知音少,弦断有谁听?
    倩何人,唤取红巾翠袖,揾英雄泪!
    零落成泥碾作尘,只有香如故!
  • 相关阅读:
    Saltstack module apache 详解
    Saltstack module ip 详解
    Saltstack module iosconfig 详解
    Saltstack module introspect 详解
    Saltstack module inspector 详解
    Saltstack module ini 详解
    Saltstack module incron 详解
    Modbus 指令 RS485指令规则
    停车系统对接第三方在线支付平台(二)
    停车系统对接第三方在线支付平台
  • 原文地址:https://www.cnblogs.com/linjisong/p/2639666.html
Copyright © 2011-2022 走看看