Javascript类和模块(二)

  1. 鸭式辩型:像鸭子一样走路,游泳并且呱呱叫的鸟就是鸭子。对于Javascript程序员来说,这句话可以理解为“如果一个对象可以像鸭子一样走路、游泳并且嘎嘎叫,就认为这个对象是鸭子,哪怕它并不是从鸭子类的原型对象继承而来的”。

  2. toJSON()方法:这个方法是由JSON.stringify()自动调用的。JSON格式用于序列化良好的数据结构,而且可以处理Javascript原始值,数组和纯对象。它和类无关,当对一个对象执行序列化操作时,它会忽略对象的原型和构造函数。

  3. 使用闭包来封装的状态一定会比不使用封装的状态变量的等价类运行速度更慢,并占用更多的内存

  4. 模块:是一个独立的Javascript文件,模块文件可以包含一个类定义、一组相关的类,一个使用函数库或者一些待执行的代码。模块化的目标是支持大规模的程序开发,处理分散源中代码代码的组装,并且能让代码正确运行,哪怕包含了作者所不期望出现的模块代码,也可以正确执行代码。为了做到这一点,不同的模块必须避免修改全局执行上下文,因此后续模块应当在它们期望的原始(或接近原始)上下文中执行。这实际上意味着模块应尽可能少地定义全局标识,理想状况是,所有模块都不应当定义超过一个(全局标识)。

  5. 用作命名空间的对象:在模块创建过程中避免污染全局变量的一种方法是使用一个对象作为命名空间。它将函数和值作为空间对象属性存储起来(可以通过全局变量引用),而不是定义全局函数和变量。

  6. 做为私有命名空间函数:将模块定义在某个函数的内部来实现。在一个函数中定义的变量和函数都属于函数的局部成员,在函数的外部是不可见的。实际上,可以将这个函数作用域用作模块的私有命名空间(有时称为“模块函数”)

  7. 立即执行的匿名函数:如果想让代码在一个私有命名空间中运行,只须给这段代码加上前缀”(function(){“和后缀”}())”。开始的左括号确保这是一个函数表达式,而不是函数的定义语句,因此可以给该前缀添加一个函数名来让代码变得更加清晰。一旦模块代码风装进一个函数,就需要一些方法导出其公用API,以便在模块函数外部调用它们。

  8. 一个综合案例:

    varSet=(function invocation(){

     function Set(){

               this.values={};

               this.n=0;

               this.add.apply(this,arguments);

     }

     //Set.prototype定义实例方法

     //这里省略了详细代码

     Set.prototype.contains=function(value){

               returnthis.values.hasOwnProperty(v2s(value));

     };

     Set.prototype.size=function(){return n;};

     //这里是上面的方法用到的一些辅助函数和变量

     function v2s(val){/*...*/}

     var nextId=1;

     //这个模块的共有APISet()构造函数

     //我们需要把这个函数从私有命名空间中导出来

     //以便在外部也可以使用它,在这种情况下,我们通过返回这个构造函数来导出它

     //它变成第一行代码所指的表达式的值

     return Set;

    }());//定义函数后立即执行

在这段代码中我们使用了名字“invocation”,用以强调这个函数应当在定义之后立即执行。名字“namespace”也可以用来强调这个函数被用作命名空间

模块函数返回构造函数,这个构造函数赋值给一个全局变量。将值返回已经清楚地表明API已经导出在函数作用域之外。如果模块API包括多个单元,则它可以返回命名空间对象。对于sets模块来说,可以将代码写成这样:

//定义一个全局变量用来存放集合相关的模块

    varcollections;

    if(!collections)collections={};

    //定义sets模块

    collections.sets=(functionnamespace(){

     //在这里定义多种“集合"类,使用局部变量和函数

     //……这里省略很多代码……

     //通过返回命名空间对象将API导出

     return {

               //导出属性名:局部变量名字

               AbstractSet:AbstractSet,

               NotSet:NotSet

     };

    }());

即最后返回一个对象,来实现导出API多个模块单元

另一种类似的技术是将模块函数当做构造函数,通过new来调用,通过将它们赋值给this来将其导出;

    var collections;

    if(!collections) collections={};

    collections.sets=(new function namespace(){

             //这里省略很多代码

             //API导出至this对象

             this.AbstractSet=AbstractSet;

             this.NotSet=NotSet;

             //......

             //注意,这里没有返回值

    });

作为一种替代方案,如果已经定义了全局命名空间对象,这个模块可以直接设置那个对象的属性,不用返回任何内容:

    var collections;

    if(!collections) collections={};

    collections.sets={};

    (function namespace(){

             //这里省略很多代码

             //将公共API导出到上面创建的命名空间对象上

             collections.sets.AbstractSet=AbstractSet;

             collections.sets.NotSet=NotSet;//……

             //导出操作已经执行了,这里不再需要return语句

    }());

 


你可能感兴趣的:(JavaScript,类和模板)