JavaScript的四种模块化规范

  1. CommonJS规范

    由于ES5没有模块化规范,所以产生了这三种规范。在ES6中又新增了一种公用模块化的方法。

    特点:同步

    CommonJS规范是通过module.exports定义的,在前端浏览器中并不支持此中规范

    浏览器不兼容Common的根本原因也就是缺少四个Node环境的变量

    • module
    • exports
    • require
    • global

    Node以及Webpack是采用CommonJS的形式来写的

    CommonJS定义的模块分为三种:模块引用(require);模块定义(exports);模块标识(module)

    require()用来引入外部模块,exports对象用于导出当前模块,或者当前的模块的方法和变量,module对象代表对象本身

    var foo = require('foo.js');
    var count =1;
    var plus = ()=>{
       foo.add(count);
    };
    module.exports= {
       count,
       plus
    }
    

    特点

    ​ 对于基本数据类型,和语言本身一样属于复制,即会被模块缓存,在另一个模块中可以对该模块输出的变量重新赋值。

    ​ 对于复杂数据类型,例如Array,Object,属于浅拷贝,即同时指向一个内存空间,因此对一个模块的值的改变可以影响另一个模块。详见javascript深拷贝与浅拷贝详解。

    当使用require命令加载同一个模块时,不会再执行该模块,而是取到缓存之中的值。也就是说,CommonJS模块无论加载多少次,都只会在第一次加载时运行一次,以后再加载,就返回第一次运行的结果,除非手动清除系统缓存。

    循环加载

    // a.js
    exports.done = false
    let b = require('./b.js')
    console.log('a.js-1', b.done)
    exports.done = true
    console.log('a.js-2', '执行完毕')
    
    // b.js
    exports.done = false
    let a = require('./a.js')
    console.log('b.js-1', a.done)
    exports.done = true
    console.log('b.js-2', '执行完毕')
    
    // c.js
    let a = require('./a.js')
    let b = require('./b.js')
    
    console.log('c.js-1', '执行完毕', a.done, b.done)
    
    

    运行node c.js

    usr:~ usr$  node c.js
    b.js-1 false
    b.js-2 执行完毕
    a.js-1 true
    a.js-2 执行完毕
    c.js-1 执行完毕 true true
    

    循环加载时,commonjs属于加载时执行,即脚本代码在require时候,就会全部执行。一旦出现某个模块被“循环加载”,只输出已经执行的部分,未执行的部分不输出。

    上述代码很明显的表现出了此类特点

    step1: node c.js

    step2: require(./a.js) //执行完a.js中的内容

    step2.1: export.done = flase; let b = require(./b.js); // 执行b中的代码

    step2.2: export.done = flase; let a= require(./a.js) //由于发生了循环加载,所以只执行a中的第一句,然后继续执行b.js 输出 b.js-1 false b.js-2执行完毕

    step2.3: 继续执行b中的代码 将b.done 赋值为true

    step 2.4 : 执行a的剩余部分 a.js-1 true; a.js-2 执行完毕

    step3:require(./b.js) 由于已经执行过了不会再执行b中的代码 所以直接输出b返回值

    step4: 输出c.js-1 执行完毕 true true


  2. AMD规范

    由于CommonJS的局限性 例如

    var math = require('math');
    
    math.add(2,3);
    

    第二行代码必须要在 require之后运行,如果math加载时间很长,就会陷入空等,整个过程是同步的。

    对于服务器这不是个问题,因为都存储在本地,但是对于客户端,如果由于网络原因,可能会陷入“假死”状态。

    所以客户端,不能采用同步加载,只能使用异步加载。

    AMD规范是require.js对模块化定义的一种规范化产出

    ​ 模块本身和模块之间的引用可以被异步加载

    ​ 先引入模块后使用模块的方法,称之为依赖前置

    优点

    ​ 1.包括异步调用和本身高扩展性

    ​ 2.解耦,模块在代码中也可以通过识别号进行查找

    define(['./package/lib.js'], function(lib) {
          function say(){
               lib.log('this is fn');
           }
           return {
               say:say
           }; 
    });
    

    Tips

    ​ lib.js是我们引入的方法,回调中lib参数表示的是引入的模块的所有方法及属性

    ​ 我们在当前模块定义了say方法,并且return say的执行结果

  3. CMD规范

    SeaJs

    CMD:同步模块定义

    依赖就近原则

    sayHello.js

    define(function(require,exports,module){
       //通过require引入依赖,并不是AMD的依赖前置,而是依赖就近,哪里用,哪里引
       //例如下列引入jquery
       var JQ = require('jquery');
       exports.sayHello = function(){
           $('#hello').toggle('slow')
       }
    })
    

    main.js

    define(function(require){
       var CMD = require('sayHello');
       var temp = new CMD();
       temp.sayHello();
    })
    
  4. ES6模块化规范

    ES6在语言标准上面实现了模块功能。设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入输出变量。CommonJS以及AMD都只能在运行时确定

    ES6的模块并不是对象,而是通过export显示指定输出的代码,再通过import命令导入

    //commonJs
    // CommonJS模块
    let { stat, exists, readFile } = require('fs');
    
    // 等同于
    let _fs = require('fs');
    let stat = _fs.stat;
    let exists = _fs.exists;
    let readfile = _fs.readfile;
    //创建了一个对象
    
    //ES6
    import { stat, exists, readFile } from 'fs';
    

    上面代码的实质是从fs模块加载 3 个方法,其他方法不加载。这种加载称为“编译时加载”或者静态加载,即 ES6 可以在编译时就完成模块加载,效率要比 CommonJS 模块的加载方式高。当然,这也导致了没法引用 ES6 模块本身,因为它不是对象。

    export输出的接口,是变量实时的值,与commonjs输出的是值的缓存不相同

    export与import要出现在模块顶层

    如果想要重新命名,需要使用as关键字

    也可以引入整个模块

    import * as bb from './xxx'

    也可以匿名导出 这样可以给导出的模块任意指定名字

    export default

    import anyname from 'xx.js'

你可能感兴趣的:(JavaScript的四种模块化规范)