JS模块化

完全不封装时

最开始的js是很简单的,它并不承担很复杂的功能,于是所有的变量和函数都写在全局作用域中(Global)。这样一开始没有什么问题,但是当JS的代码越来越多,后来慢慢出现插件或多个JS文件时就有问题了,变量和函数的名称很容易冲突。

function foo(){
    //...
}
function bar(){
    //...
}

利用JS对象模拟命名空间

利用JS对象,将相关的方法和属性封装一层,这样可以避免很多同功能函数的冲突。

var MYAPP = {
    foo: function(){},
    bar: function(){}
}

MYAPP.foo();

但是这样只能做到简单的减少全局中变量的冲突,关于安全方面完全没有帮助,还是可以随便覆盖和访问。

利用函数的块级作用域

隐藏局部变量

块级作用域里的变量外面是访问不到的,利用这一点可以将我们的功能封装在一个匿名函数中,只通过返回值暴露出我们想暴露出的接口。

var Module = (function(){
    var _private = "safe now";
    var foo = function(){
        console.log(_private)
    }

    return {
        foo: foo
    }
})()

Module.foo();
Module._private; // undefined

利用函数传参添加依赖

就是酱咯,这种封装的方式已经把现代模块化代码上的要素基本都包含了。
独立性是模块的重要特点,模块内部最好不与程序的其他部分直接交互。
为了在模块内部调用全局变量,必须显式地将其他变量输入模块。

var Module = (function($){
    var _$body = $("body");     // we can use jQuery now!
    var foo = function(){
        console.log(_$body);    // 特权方法
    }

    // Revelation Pattern
    return {
        foo: foo
    }
})(jQuery)

Module.foo();

继承或拓展模块

利用传参的特性,可以继承已有的模块:

    var module1 = (function (mod){
    mod.m3 = function () {
      //...
    };
    return mod;
  })(module1);

模块管理

当我们有多个js的时候我们会写很多的script标签,但是这样不仅增加了HTTP请求,而且难以维护,因为执行顺序是从上至下的,所以被依赖的需要放在前面,文件多了就比较尴尬了。

CommonJS

定义模块

上下文提供了exports对象用于导出当前模块的方法或者变量,并且它是唯一的导出的出口。在模块中,还在 一个module对象,它代表模块自身,exports是module的属性。将方法挂载在exports上作为属性即可。

exports.add = function () {   
    var sum = 0,   
    i = 0,     
    args = arguments,     
    l = args.length;   
    while (i < l) { 
        sum += args[i++]; 
      }   
    return sum; 
};

使用模块

使用时就在要使用的文件中引用:

var selfModule = require('selfModule');
console.log(selfModule.add(13,3));

在引用的过程中,根据具体实现的不同,使用的模块标识也不同,绝对路径是一般都支持的。

同步加载

在你require的时候,是同步加载这个模块的,也就是说,这个模块中的代码执行完才会继续执行主文件中下面的代码。
可以试下下面这个例子:

var EXE_TIME = 2;

(function(second){
    var start = +new Date();
    while(start + second*1000 > new Date()){}
})(EXE_TIME)

console.log("2000ms executed");
var timeout = require('E:\\Git\\NodeTest\\timeout.js');
console.log('done!');

这种阻塞式的加载在服务器端是没有问题的,比如node。但是在浏览器端,阻塞式加载并不是很理想。

AMD

定义模块

在这里,一个磁盘文件应该只定义一个模块
如果一个模块仅含值对,没有任何依赖,则在define()中定义这些值对就好了:

define({
    color: "black",
    size: "unisize"
});

如果一个模块没有任何依赖,但需要一个做setup工作的函数,则在define()中定义该函数,并将其传给define():

define(function () {
    //Do setup work here

    return {
        color: "black",
        size: "unisize"
    }
});

如果模块存在依赖:则第一个参数是依赖的名称数组;第二个参数是函数,在模块的所有依赖加载完毕后,该函数会被调用来定义该模块,因此该模块应该返回一个定义了本模块的object。依赖关系会以参数的形式注入到该函数上,参数列表与依赖名称列表一一对应。

define(["./cart", "./inventory"], function(cart, inventory) {
        //return an object to define the "my/shirt" module.
        return {
            color: "blue",
            size: "large",
            addToCart: function() {
                inventory.decrement(this);
                cart.add(this);
            }
        }
    }
);

使用模块

require(['moduleA', 'moduleB', 'moduleC'], function (moduleA, moduleB, moduleC){
    // some code here
});

模块的加载

加载模块时默认要加载的模块和加载它们的文件在同一个目录下,但是如果不是你可以通过require.config来配置。

CMD

CMD的定义模块部分与AMD有区别,更加接近于CommonJS。

define(function(require, exports, module){
    var a = require("a");
    a.doSomething();
    var b = require("b");
    b.doSomething();    // 依赖就近,延迟执行
})

兼容模块规范

为了能将一个模块可以同时运行在使用不同包管理的地方,我们可以这样定义模块:

(function (name, definition) {   
    // 检测上文环境是否为AMDCMD   
    var hasDefine = typeof define === 'function',     
    // 检查上文环境是否为Node     
    hasExports = typeof module !== 'undefined' && module.exports;  
    if (hasDefine) {    
        // AMD环境CMD环境     
        define(definition);   
    } else if (hasExports) {     
        // 定义为通用Node模块     
        module.exports = definition();   
    } else {     
        // 将模块的执行结果挂在window变量中,在浏览器中this指向window对象     
        this[name] = definition();   
    } 
})('math', function () {   
    var interface = {};
    interface.add=function () {   
        var sum = 0,   
        i = 0,     
        args = arguments,     
        l = args.length;   
        while (i < l) { 
            sum += args[i++]; 
          }   
        return sum; 
    };
    return interface; 
});
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值