• js库写法


    前言:

      现在javascript库特别多,其写法各式各样,总结几种我们经常见到的,作为自己知识的积累。而目前版本的 JavaScript 并未提供一种原生的、语言级别的模块化组织模式,而是将模块化的方法交由开发者来实现。因此,出现了很多种 JavaScript 模块化的实现方式,

    以 AMD 为例,该规范使用 define 函数来定义模块。

    define(factory(){
        //模块化
    });

    模块模式:

    模块模式使用了 JavaScript 的一个特性,即闭包(Closures)。现今流行的一些 JS 库中经常见到以下形式的代码:

    ;(function (参数) {
      // 模块代码
      // return something;
    })(参数);

    上面的代码定义了一个匿名函数,并立即调用自己。

    也有一些开发者在函数表达式前面加上一个惊叹号(!)或分号(;),而不是用括号包起来。

    !function (参数) {
      // 代码
      // return something
    }(参数);

    还有些人喜欢用括号将整个 IIFE 围起来,这样就变成了以下的形式:

    (function (参数) {
      // 代码
      // return something
    }(参数));

    参数输入:

    JavaScript 有一个特性叫做隐式全局变量(implied globals),当使用一个变量名时,JavaScript 解释器将反向遍历作用域链来查找变量的声明,如果没有找到,就假定该变量是全局变量。这种特性使得我们可以在闭包里随处引用全局变量,比如 jQuery 或 window。然而,这是一种不好的方式。

    考虑模块的独立性和封装,对其它对象的引用应该通过参数来引入。如果模块内需要使用其它全局对象,应该将这些对象作为参数来显式引用它们,而非在模块内直 接引用这些对象的名字。以 jQuery 为例,若在参数中没有输入 jQuery 对象就在模块内直接引用 $ 这个对象,是有出错的可能的。正确的方式大致应该是这样的:

    ;(function ($, w) {
      // $ is jQuery
      // w is window
      // 局部变量及代码
      // 返回
    })(jQuery, window);

    模块输出(Module Export)

    有时我们不只是要使用全局变量,我们也要声明和输出模块中的对象,这可以通过匿名函数的 return 语句来达成,而这也构成了一个完整的模块模式。

    var klm= (function () {
        var myklm = {},
            modeval= 1;
     
        function privateMethod() {
            // ...
        }
     
        myklm.moduleProperty = 1;
        myklm.moduleMethod = function () {
            // ...
        };
     
        return myklm ;
    }());

    这段代码声明了一个变量 MODULE,它带有两个可访问的属性:moduleProperty 和 moduleMethod,其它的代码都封装在闭包中保持着私有状态。参考以前提过的参数输入,我们还可以通过参数引用其它全局变量。

    输出简单对象:

    使用对象直接量来表达 JavaScript 对象是很常见的。比如:var x = { p1: 1, p2: "2", f: function(){ /*... */ } }

    var Module1 = (function () {
      var private_variable = 1;
      function private_method() { /*...*/ }
     
      var my = {
        property1: 1,
        property2: private_variable,
        method1: private_method,
        method2: function () {
            // ...
        }
      };
      return my;
    }());

    输出函数:

    有时候我们希望返回的并不是一个对象,而是一个函数。有两种需求要求我们返回一个函数,一种情况是我们需要它是一个函数,比如 jQuery,它是一个函数而不是一个简单对象;另一种情况是我们需要的是一个“类”而不是一个直接量,之后我们可以用 "new" 来实例它。目前版本的 JavaScript 并没有专门的“类”定义,但它却可以通过 function 来表达。

    var klm= (function () {
      // 私有成员及代码 ...
     
      return function(name) {
        this.name = name;
        this.bark = function() { /*...*/ }
      };
    }());
     
    var com = new klm("蒯灵敏");
    com.bark();

    前面已经提到一种形式是输出对象直接量(Object Literal Notation),而 Revealing Module Pattern 其实就是这种形式,只是做了一些限定。这种模式要求在私有范围内中定义变量和函数,然后返回一个匿名对象,在该对象中指定要公开的成员。

    var klm= (function () {
      // 私有变量及函数
      var x = 1;
      function f1() {}
      function f2() {}
     
      return {
        public_method1: f1,
        public_method2: f2
      };
    }());

    扩展:

    紧耦合扩展:

    有时我们要求在扩展时调用以前已被定义的方法,这也有可能被用于覆盖已有的方法。这时,对模块的定义顺序是有要求的。

    var klm = (function (my) {
      var old_moduleMethod = my.moduleMethod;
     
      my.moduleMethod = function () {
        // 方法重载
        // 可通过 old_moduleMethod 调用以前的方法...
      };
      return my;
    }(klm));

    克隆与继承:

    var MODULE_TWO = (function (old) {
        var my = {},
            key;
     
        for (key in old) {
            if (old.hasOwnProperty(key)) {
                my[key] = old[key];
            }
        }
     
        var super_moduleMethod = old.moduleMethod;
        my.moduleMethod = function () {
            // override method on the clone, access to super through super_moduleMethod
        };
     
        return my;
    }(MODULE));

    上面代码再精简一下:可以使用 Object.create()

    var MODULE_TWO = (function (old) {
      var my = Object.create(old);
     
      var super_moduleMethod = old.moduleMethod;
      my.moduleMethod = function () {
        // override method ...
      };
     
      return my;
    }(MODULE));

     与其它模块规范或 JS 库的适配:

    模块环境探测:

    现今,CommonJS Modules 与 AMD 有着广泛的应用,如果确定 AMD 的 define 是可用的,我们当然可以使用 define 来编写模块化的代码。然而,我们不能假定我们的代码必然运行于 AMD 环境下,有没有办法可以让我们的代码?

    其实我们只需要在某个地方加上对 CommonJS Modules 与 AMD 的探测并根据探测结果来“注册”自己就可以了,以上那些模块模式仍然有用。AMD 定义了 define 函数,我们可以使用 typeof 探测该函数是否已定义。若要更严格一点,可以继续判断 define.amd 是否有定义。另外,SeaJS 也使用了 define 函数,但和 AMD 的 define 又不太一样。对于 CommonJS,可以检查 exports 或是 module.exports 是否有定义。

    var klm = (function () {
      var my = {};
      // 代码 ...
     
      if (typeof define == 'function') {
        define( function(){ return my; } );
      }else if (typeof module != 'undefined' && module.exports) {
        module.exports = my;
      }
      return my;
    }());

    其它一些 JS 库的做法

    jquery的检测方式

    if ( typeof module === "object" && module && typeof module.exports === "object" ) {
        module.exports = jQuery;
    } else {
        if ( typeof define === "function" && define.amd ) {
            define( "jquery", [], function () { return jQuery; } );
        }
    }
     
    if ( typeof window === "object" && typeof window.document === "object" ) {
        window.jQuery = window.$ = jQuery;
    }

    接下来看看多重匿名函数的做法:

    (function (root, factory) {
      if (typeof exports === "object" && exports) {
        factory(exports); // CommonJS
      } else {
        var mustache = {};
        factory(mustache);
        if (typeof define === "function" && define.amd) {
          define(mustache); // AMD
        } else {
          root.Mustache = mustache; // <script>
        }
      }
    }(this, function (mustache) {
      // 模块主要的代码放在这儿
    });

    这段代码与前面介绍的方式不太一样,它使用了两个匿名函数。后面那个函数可以看作是模块代码的工厂函数,它是模块的主体部分。前面那个函数对运行环境进行 检测,根据检测的结果对模块的工厂函数进行调用。另外,作为一个通用库,它并没使用 window 对象,而是使用了 this,因为在简单的函数调用中,this 其实就是全局对象。

    再看看 doT 的做法

    (function() {
        "use strict";
     
        var doT = {
            version: '1.0.0',
            templateSettings: { /*...*/ },
            template: undefined, //fn, compile template
            compile:  undefined  //fn, for express
        };
     
        if (typeof module !== 'undefined' && module.exports) {
            module.exports = doT;
        } else if (typeof define === 'function' && define.amd) {
            define(function(){return doT;});
        } else {
            (function(){ return this || (0,eval)('this'); }()).doT = doT;
        }
        // ...
    }());

    这段代码里的 (0, eval)('this') 是一个小技巧,这个表达式用来得到 Global 对象,'this' 其实是传递给 eval 的参数,但由于 eval 是经由 (0, eval) 这个表达式间接得到的,因此 eval 将会在全局对象作用域中查找 this,结果得到的是全局对象。若是代码运行于浏览器中,那么得到的其实是 window 对象。

    JavaScript 模块化的未来

    尚在制定中的 ES 6 会对模块作出语言级别的定义。我们来看一个实例,以下的代码段摘自“ES6:JavaScript中将会有的几个新东西

    module Car { 
      // 内部变量
      var licensePlateNo = '556-343'; 
      // 暴露到外部的变量和函数
      export function drive(speed, direction) { 
        console.log('details:', speed, direction); 
      } 
      export module engine{ 
        export function check() { } 
      } 
      export var miles = 5000; 
      export var color = 'silver'; 
    };

    在以后自己写js封装的时候上面是我参考的资料,继续激情的前进着。。。

  • 相关阅读:
    调整数组顺序使奇数位于偶数前面
    网站远程附件存储到 OSS
    平滑升级mariadb
    在Apache服务器上安装SSL证书
    复试-英语
    部署博客(docker)
    远程连接Windows
    Ultimate SLAM?利用事件相机解锁高速运动、高动态范围场景
    LRNNet:轻量级FCB& SVN实时语义分割
    3D目标检测深度学习方法中voxel-represetnation内容综述(三)
  • 原文地址:https://www.cnblogs.com/kuailingmin/p/4208863.html
Copyright © 2020-2023  润新知