• PhoneGap源码分析5——cordova/utils


    在导入cordova的过程中,也即在调用cordova的工厂函数中,首先遇到的是导入另一个模块cordova/channel(注:这里由于函数声明提升,实际上是先执行工厂函数内部的其它函数声明,然后再执行下面的语句,但对这里的分析不受影响)

    define("cordova", function(require, exports, module) {
      var channel = require('cordova/channel');
      //其它代码
    });

    然后,我们跟踪到cordova/channel的工厂函数,可以看到,仍然需要先导入cordova/utils这个模块

    define("cordova/channel", function(require, exports, module) {
      var utils = require('cordova/utils');
      // 其它代码
    });

    继续跟踪,cordova/utils这个模块没有再导入其它模块,那我们就从cordova/utils的工厂函数这里开始。
    先看源码(一级展开):

     1 define("cordova/utils", function(require, exports, module) {//将常用的一些工具函数放在一起
     2     var utils = exports;//此时exports仍旧是一个{}空对象
     3     
     4     utils.isArray = function(a) {//判断一个对象是否为Array类型
     5     };
     6     
     7     utils.isDate = function(d) {//判断一个对象是否为Date类型
     8     };
     9     
    10     utils.clone = function(obj) {//深度copy一个对象,包括这个对象的事件等
    11     };
    12     
    13     utils.close = function(context, func, params) {//对一个函数的包装调用
    14     };
    15     
    16     utils.createUUID = function() {//产生随机字符串
    17     };
    18     
    19     utils.extend = (function() {//继承
    20     }());
    21     
    22     utils.alert = function(msg) {//弹出消息,不支持弹出消息时,写日志到控制台
    23     };
    24     
    25     utils.format = function(formatString /* ,... */) {//格式化
    26     };
    27     
    28     utils.vformat = function(formatString, args) {//格式化
    29     };
    30     
    31     function UUIDcreatePart(length) {//内部私有函数,产生随机数
    32     }
    33 
    34     function formatted(object, formatChar) {//内部私有函数,格式化
    35     }
    36 });

     1、首先需要说明的是,由于函数声明提升,在执行这个工厂函数时,首先执行的是UUIDcreatePart和formatted这两个内部的私有函数的声明,强调一下,只是声明函数,没有调用函数。我们顺便看一下这两个函数:

    (1)UUIDcreatePart函数用来随机产生一个16进制的号码,接受一个表示号码长度的参数(实际上是最终号码长度的一半),一般用途是做为元素的唯一ID;

    (2)formatted函数是用来格式化对象的,接受两个参数,被格式化的对象和格式化标志字符,也即是根据标志来格式化对象,具体的说就是传入'j','o'时序列化为JSON字符串,传入'c'时返回空字符串,其它情况返回对象的字符串形式。例如:

    var object = {
      name:'linjisong',
      age:29
    };
    console.info(formatted(object,'j'));//{"name":"linjisong","age":29}
    console.info(formatted(object,'c'));//空字符串
    console.info(formatted(object,'k'));//[object,object]

    这个函数在内部调用了JSON.stringify这个函数,这个是内建对象JSON中的一个方法,用来将对象转变为JSON字符串,另外JSON还有一个parse方法,是将JSON字符串解析为对象的。这两个函数的签名如下:

    parse(text[,reviver])
    stringify(value[,replacer[,space]])

    具体用法这里不再展开。
    2、声明两个私有函数之后,再初始化utils,并填充一些工具函数,这里以判断是否为Array的函数为例:

    utils.isArray = function(a) {
        return Object.prototype.toString.call(a) == '[object Array]';
    };

    (1)在这里不使用instanceof来判断是不是Array类型,主要是考虑到跨域或者多个frame的情况,多个frame时每个frame都会有自己的Array构造函数,从而得出不正确的结论。

    (2)使用'[object Array]'来判断是根据ECMA标准中的返回值来进行的,事实上,这里不需要类型转换,而可以用全等“===”来判断。

    3、utils.clone函数,针对Array和一般对象的复制,实现逻辑比较简单,内部使用递归实现,估计效率不是很好。

    4、utils.close函数,封装函数的调用,将执行环境作为一个参数,调用的函数为第二个参数,调用函数本身的参数为后续参数。

    5、utils.createUUID函数,调用UUIDcreatePart这个内部函数,产生一个随机的类似“bb88a05c-7c66-1355-249f-b9f60e04d368”格式的字符串。

    6、utils.extend函数,是一个立即调用的匿名函数的返回值,是通过原型链实现的一个继承方法:

    (1)需要清楚的是,每一个函数,都有一个属性prototype,指向这个函数的原型对象,每一个函数实例对象,也都有一个指针指向原型对象;

    (2)每一个自动获取的函数原型对象,都有一个内部指针,执行函数对象本身;

    (3)在访问一个实例对象属性时,会首先搜索这个对象本身,如果没有找到,会接着搜索原型对象。

    (4)通过手工更改使得子类的原型对象为一个中间对象实例,而这个中间对象的原型对象指向父类原型对象,从而达到子类有一个指针指向其原型对象,子类原型对象有一个指针指向父类原型对象,因此就构成了实现继承的原型链。比如父类P的原型中有一个属性attr,子类C中没有,那么在调用C[attr]时,会先查找C中是否有attr,没有就查找C的原型对象,也就是中间对象实例,同样没有,继续查找中间对象的原型对象,因为是指向父类原型,从而可以找到并且访问attr。

    参考源代码:

    utils.extend = (function() {
        var F = function() {};
        return function(Child, Parent) {
            F.prototype = Parent.prototype;
            Child.prototype = new F();
            Child.__super__ = Parent.prototype;
            Child.prototype.constructor = Child;
        };
    }());

    7、utils.alert函数比较简单,如果alert有定义,就alert,否则的话,实现了console.log的使用这个打印日志,其他情况下什么都不做。
    8、utils.format和utils.vformat是格式化函数,先看format:

    utils.format = function(formatString /* ,... */) {
        var args = [].slice.call(arguments, 1);//传入参数中的第2个开始组成的数组
        return utils.vformat(formatString, args);
    };

    这里调用了空数组的slice函数,类似的函数有:
    (1)push():接受任意数量的参数,逐个添加到数组末尾,并返回修改后数组长度

    (2)pop():移除最后一项,修改数组长度,返回被移除的项

    (3)shift():移除数组第一项,修改数组长度,返回被移除的项

    (4)unshift():在数组前端添加任意个项,并返回数组长度

    (5)slice():基于当前数组的一个或多个项创建一个新数组,可以接受1或2个参数,即要返回项的起始和结束位置,只有一个参数时,返回从该参数指定位置开始到末尾,有两个参数,返回这两个参数之间项(前闭后开区间),slice不会影响原数组。若参数为负数,则用数组长度加上参数直至为正数,若结束位置小于起始位置,返回空数组。

    (6)splice():返回从原数组中删除的项构成的数组,若没有删除,返回空数组

    • 删除:可以删除任意数量的项,只需指定2个参数,要删除的第一项的位置和要删除的项数,如splice(0,2)会删除前面两项
    • 插入:可以向指定位置插入任意数量的项,只需提供3个参数,起始位置,0(要删除的数),要插入的项,如果要插入多个项,可以传入第四、第五以至任意多个项
    • 替换:可以向指定位置插入任意数量的项,且同时删除任意数量的项

    再看vformat: 

    View Code
     1 utils.vformat = function(formatString, args) {
     2     if (formatString === null || formatString === undefined) return "";//源对象为null或undefined时,直接返回空字符串
     3     if (arguments.length == 1) return formatString.toString();//如果args未传入,直接返回源对象的字符串表示
     4     if (typeof formatString != "string") return formatString.toString();//源对象不是字符串时,直接返回字符串表示
     5 
     6     var pattern = /(.*?)%(.)(.*)/;
     7     var rest    = formatString;
     8     var result  = [];
     9 
    10     while (args.length) {//循环处理,每处理一次,args会减少一项,直至args.length为0,这里个人并不推荐这种用法,因为每次循环都要计算长度
    11         var arg   = args.shift();//移除数组的第一项,返回被移除的项,修改数组长度
    12         var match = pattern.exec(rest);
    13 
    14         if (!match) break;
    15 
    16         rest = match[3];
    17 
    18         result.push(match[1]);
    19 
    20         if (match[2] == '%') {
    21             result.push('%');
    22             args.unshift(arg);
    23             continue;
    24         }
    25 
    26         result.push(formatted(arg, match[2]));
    27     }
    28 
    29     result.push(rest);
    30 
    31     return result.join('');
    32 };

    其中涉及的正则表达式及其用法,在下一篇中再进行分析。

    拣尽寒枝不肯栖,寂寞沙洲冷。
    郴江幸自绕郴山,为谁流下潇湘去?
    欲将心事付瑶琴,知音少,弦断有谁听?
    倩何人,唤取红巾翠袖,揾英雄泪!
    零落成泥碾作尘,只有香如故!
  • 相关阅读:
    有些事情,我们需要坚持到底
    SEO策略与细节:细节决定成败
    织梦DEDECMS更新6月7日补丁后出现版权链接的删除办法
    argparse模块的应用
    多态实现原理剖析
    tensorflow中tf.ConfigProto()用法解释
    tensor flow中summary用法总结
    tensorflow-gpu版本出现libcublas.so.8.0:cannot open shared object file
    python3自带工具2to3.py用法
    公司管理系统之设计
  • 原文地址:https://www.cnblogs.com/linjisong/p/2630049.html
Copyright © 2020-2023  润新知