JS模块化入门

不知道有多少同窗像我同样写了好久的JS代码😅,可是对JS的模块化仍是不清楚,这篇文章比较适合新手朋友,通读以后能对模块化有一个简单的概念。前端

什么是模块化

模块化,其实就是在项目中定义若干个相对独立的可复用的模块,模块中包含特定的功能,当你想要用这个功能的时候,只须要引入对应的模块便可。npm

  • 定义模块:每一个模块内部的执行逻辑是不被外界感知的,只是暴露出部分方法和数据
  • 引入模块:同步或异步加载待引入的代码,执行并获取到其暴露的方法和数据

其实模块化主要是为了规避和全局属性的冲突,同时也但愿写模块的时候能本身维护本身的模块,多人项目开发的时候不用担忧和别人变量冲突。编程

模块化其实也就是某种意义上面的全局,或者和封装代码,只不过是封装的稍微好一点而已,其实也不是全局变量。设计模式

模块化的好处

  • 避免命名冲突(减小命名空间污染)
  • 更好的分离,按需加载
  • 更高的复用性
  • 高可维护性,下降维护成本

刀耕火种的时代

在早期的时代,若是咱们想要导出一个模块经常使用的方法就是经过闭包,加持一点设计模式来实现模块化,比较经典的就是JQuery实现模块的方式:前端工程化

(function(window) {
    ///   代码
    
    window.jQuery = window.$ = jQuery; // 经过给window添加属性而暴露到全局
})(window)
复制代码

这种封装模式被不少人模仿,经过匿名函数包装代码,所依赖的外部变量传给这个函数,这个函数内部可使用这些依赖,而后再将模块自身暴露给window。浏览器

虽然这种模式看似很方便,可是其实仍是增长了全局变量,并且当咱们要引用的时候也一样须要注意引用模块的顺序,否则极可能致使页面报错。bash

AMD && CMD

这部分因为年代久远,并且如今在项目基本不多会使用,因此这里做者就只把一些浅显的知识点加以整理,算是简单的了解一下吧。 AMD和CMD主要是解决在浏览器端的异步模块化编程的需求,其中AMD和CMD最具表明的两个做品分别是require.js和sea.js,有兴趣的能够详细了解一下。闭包

这二者用法基本相同,区别就是AMD崇尚依赖前置,CMD崇尚的是按需加载,也就是在须要用到某个模块的时候再进行加载。异步

AMD && CMD基本用法

  • 定义模块:
defined('模块名称', ['依赖的模块1', '依赖的模块2', ], function (moudle1, moudle2) {
    
    class item {}
    return item 或者  module.exports = item
});
复制代码
  • 引入模块:引入方法和common.js相同,都是用require方法
const moudle = require('./moudle');
复制代码

这里须要注意的是,若是当前文件是AMD一个模块能够像下面这样写:模块化

defined('test', function () {
        var a = require('a'); // 按需加载,在用到的地方在加载
    });
复制代码

若是不是,按照AMD规范须要定义成局部require

var a = require(['a'], function () {
        代码
    });
复制代码

由于这里require多是一个异步的形式,这是就须要用回调,若是直接var a = require('a');这样使用也成功了的话,只能证实这个模块加载或定义过,不过在全局的话最稳妥的方式仍是上面的那种形式。

AMD和CMD是怎么实现依赖分析的?????

能够思考一下当咱们在用的时候:

var utils = require('./utils');
    utils.xxx();
复制代码

这样写的话,JS指定是来不及加载的,可是这个语句是同步执行的,他们是怎么作的呢????

其实他会先把咱们的函数先执行一下toString方法,拿到函数的字符串,而后用正则一下,看看哪里用到了require

var functionStr = function.toString();
    var regx = /require\(['"][^)*]['"]\)/ 复制代码

这样就能够拿到咱们项目中须要require的模块,而后就能够在使用以前进行加载模块了。

CommonJS

2009 年 ry 发布 Node.js 的第一个版本,CommonJS 做为其中最核心的特性之一,适用于服务端下的场景;历年来的考察和时间的洗礼,以及前端工程化对其的充分支持,CommonJS 被普遍运用于 Node.js 和浏览器:

用法:

  • 引入方法:require方法,得到目标模块导出值,能够加载内置模块,npm模块和自定义模块,在commonJS中每个文件都是一个模块,都是独立封装的。
  • 导出方法:module.exports或者export.xxx = xxx;

注意:这里不要直接修改exports的值

// a.js
console.log(require('./b'));    // {}
// b.js
exports = { name: 'changan' }
复制代码

上面代码能够看出,这样用的话咱们require的是一个空对象,这是为何呢?

咱们能够经过exports的伪代码来看一下:

// 当咱们要导出模块的时候,内核帮忙声明
var exports = moudle.exports = { };
复制代码

想象一下,当咱们进行例子中的操做的时候,至关于咱们会把exports变量覆盖掉,可是moudle.exports并无改变,当咱们require模块的时候仍是会找module.exports对象,因此咱们会拿到一个空对象,这部分须要注意一下。

require的是一个单例

//a.js
    var obj = {
        name: 1
    }
    module.exports = {
        getName: function () {
            return obj.name
        },
        setName: function (name) {
            obj.name = name;
        }
    }
    // ==================
    // b.js
    var a = require('./a');
    console.log('a-1: ', a.getName());  // a-1:1
    a.setName(2222);
    console.log('a-2: ', a.getName());  // a-2:2222
    // ==================
    // c.js
    var a = require('./a');
    var b = require('./b');
    
    console.log('a-3: ', a.getName()); // a-3:2222
复制代码

这里仍是值得注意一下的,由于commonJS引入的模块是单例的,因此这也就解释了为何当两个文件引用同一个模块以后,一个文件改变了这个模块的值的时候,另外一个模块的值也是会改变。在开发中经常会由于不知道这个原理而焦头烂额,当一个同事改变了模块的一个值的时候,另外一个引用该模块的地方也会改变,避免的最好办法就是不要更改模块内部的值。

ES6模块化

ES Module 是语言层面的模块化方案,由 ES 2015 提出,其规范与 CommonJS 比之 ,导出的值均可以当作是一个具有多个属性或者方法的对象,能够实现互相兼容。

  • ES Module 会对静态代码分析,即在代码编译时进行模块的加载,在运行时以前就已经肯定了依赖关系(可解决循环引用的问题);
  • ES Module 关键字:import export 以及独有的default关键字,肯定默认的导出值;
  • ES Module 中导出的值是一个只读的值的引用 ,不管基础类型和复杂类型,而在 CommonJS 中 require 的是值的拷贝,其中复杂类型是值的浅拷贝;
// a.js
export let a = 1;
export function caculate() {
  a++;
};

// b.js
import { a, caculate } from 'a.js';

console.log(a); // 1
caculate();
console.log(a); // 2

a = 2; // Syntax Error: "a" is read-only
复制代码
相关文章
相关标签/搜索