01 模块化演化过程和规范

本文详细介绍了JavaScript模块化的概念及其演化过程,包括全局function模式、namespace模式、IIFE模式以及IIFE的增强。讨论了模块化过程中遇到的问题,如命名冲突和数据安全性,并展示了CommonJS、AMD和CMD等模块化规范。最后,提到了ES6的模块化实现及其在浏览器中的应用。文章旨在帮助读者理解JavaScript模块化的原理和实践。
摘要由CSDN通过智能技术生成

什么是模块化

什么是模块?

​ 模块 = 数据(内部属性) + 操作数据的行为(内部函数)

  • 将一个复杂的程序依据一定的规范封装成几个模块(文件),并组合在一起
  • 模块的内部数据/实现是私有的
  • 需要向外部暴露一些接口(方法),来与外部其他模块通信

什么是模块化?

​ 编码时按照模块依次编码,整个项目就是一个模块化的项目。

模块化演化过程

全局 function 模式

  • 编码:全局变量,将不同的功能封装成全局函数

  • 问题:污染全局命名空间,容易引起命名冲突,数据不安全

  • module1.js

    // 数据
    let data = 'hello world';
    
    // 操作数据的函数
    function foo() {
        console.log(`foo() ${data}`);
    }
    function bar() {
        console.log(`bar() ${data}`);
    }
    
  • module2.js

    let data2 = 'other data';
    
    // 与module1模块中的函数冲突了
    function foo() { 
    	console.log(`foo() ${data2}`)
    }
    
  • test1.html

    <script type="text/javascript" src="module1.js"></script>
    <script type="text/javascript" src="module2.js"></script>
    <script type="text/javascript">
    	let data = "修改后的数据";
        foo();
        bar();
    </script>
    

namespace 模式

  • 编码:简单对象封装,将数据、行为封装到对象中

  • 解决:命名冲突(减少了全局变量)

  • 问题:数据不安全(外部可以直接修改模块内部的数据)

  • module1.js

    let mymodule = {
        data: 'hello world 1',
        foo() {
            console.log(`foo() ${this.data}`);
        },
        bar() {
            console.log(`bar() ${this.data}`);
        }
    }
    
  • module2.js

    let myModule2 = {
      data: 'hello world 2',
      foo() {
        console.log(`foo() ${this.data}`)
      },
      bar() {
        console.log(`bar() ${this.data}`)
      }
    }
    
  • test2.html

    <script type="text/javascript" src="module1.js"></script>
    <script type="text/javascript" src="module2.js"></script>
    <script type="text/javascript">
      myModule.foo()
      myModule.bar()
    
      myModule2.foo()
      myModule2.bar()
    
      myModule.data = 'other data' //能直接修改模块内部的数据
      myModule.foo()
    </script>
    

IIFE 模式

  • IIFE : immediately-invoked function expression(立即调用函数表达式)

  • IIFE:立即调用函数表达式 → 匿名函数自调用(闭包)

  • 作用: 数据是私有的, 外部只能通过暴露的方法操作

  • module3.js

    (function(window) {
        // 数据
     	let data = 'hello world 3';
        
        // 操作数据的函数
     	funtion foo() { 		 // 用于暴露的函数
        	console.log(`foo() ${data}`);
    	};
    	function bar() {		 // 用于暴露的函数 
            console.log(`bar() ${data}`);
            otherFun(); 		 // 内部调用
        };
        
      	function otherFun() {    // 内部私有的函数
            console.log('otherFun()');
        }
        
        // 暴露行为
    	window.myModule = {foo, bar};
     })(window)
    
  • test3.html

    <script type="text/javascript" src="module3.js"></script>
    <script type="text/javascript">
      myModule.foo()
      myModule.bar()
      //myModule.otherFun()  //myModule.otherFun is not a function
      console.log(myModule.data) //undefined 不能访问模块内部数据
      myModule.data = 'xxxx' //修改的不是模块内部的data
      myModule.foo() //没有改变
    </script>
    
  • 问题: 如果当前这个模块依赖另一个模块怎么办?

    • IIFE 增强

IIFE 增强

  • 编码:将数据和行为封装到一个函数内部,通过给window添加属性来向外暴露接口

  • 引入依赖:通过函数形式来引入依赖模块

  • 这就是现代模块实现的基石

  • module4.js

    (function(window, module2){
      // 数据
      let data = 'hello world 4';
      
      // 操作数据的函数
      function foo() {		// 用于暴露的函数
         module2.xxx()
         console.log('foo()'+data)
      }
      
      function bar() {      // 用于暴露的函数
         console.log('bar()'+data)
         otherFun()			// 内部调用
      }
        
      function otherFun() { // 内部私有的函数
        console.log('otherFun()')
      }
      
      // 暴露行为
      window.module = {foo, bar};
    })(window, module2)
    

页面加载多个 js 的问题

  • 页面:

    <script type="text/javascript" src="module1.js"></script>
    <script type="text/javascript" src="module2.js"></script>
    <script type="text/javascript" src="module3.js"></script>
    <script type="text/javascript" src="module4.js"></script>
    
  • 说明

    • 一个页面需要引入多个 js 文件
    • 问题:
      • 请求过多
      • 依赖模糊
      • 难以维护
    • 这些问题可以通过现代模块化编码和项目构建来解决

模块化规范

CommonJS

  • Node.js:服务器端

  • Browserify:浏览器端,也称为 js 的打包工具

  • http://wiki.commonjs.org/wiki/Modules/1.1

  • 基本语法:

    • 定义暴露模块:exports

      exports.xxx = value;
      module.exports = value;
      
    • 引入使用模块:require

      var module = require('模块名/模块相对路径')
      
  • 何时引入模块

    • Node.js:运行时,动态同步引入。
    • Browserify:
      • 运行前,对模块进行编译/转译/打包的处理,将依赖的模块包含进来;
      • 运行时,运行的时打包生成的 js,不再需要从远程引入依赖模块。

AMD

  • Asynchronous Module Definition(异步模块定义):专门用于浏览器端, 模块的加载是异步的

  • https://github.com/amdjs/amdjs-api/wiki/AMD

  • 基本语法

    • 定义暴露模块

      define([依赖模块名],function() {return 模块对象})
      
    • 引入使用模块

      require(['模块1','模块2','模块3'],function(m1,m2) {//使用模块对象})
      
    • 配置

      require.config({
        //基本路径
        baseUrl : 'js/',
        //标识名称与路径的映射
        paths : {
          '模块1' : 'modules/模块1',
          '模块2' : 'modules/模块2',
          'angular' : 'libs/angular',
          'angular-messages' : 'libs/angular-messages'
        },
        //非AMD的模块
        shim : {
          'angular' : {
              exports : 'angular'
          },
          'angular-messages' : {
              exports : 'angular-messages',
              deps : ['angular']
          }
        }
      })
      

CMD

  • Common Module Definition(通用模块定义):专门用于浏览器端, 模块的加载是异步的

  • sea.js

  • https://github.com/seajs/seajs/issues/242

  • 基本语法

    • 定义暴露模块

      define(function(require,module,exports) {
          通过require引入依赖模块
          通过module/exports来引入模块
          exports.xxx = value
      })
      
    • 引入使用模块

      seajs.use(['模块1''模块2'])
      

ES6

  • ES6内置了模块化的实现

  • http://es6.ruanyifeng.com/#docs/module

  • 基本语法

    • 定义暴露模块:export

      • 暴露一个对象:

        export default 对象;
        
      • 暴露多个:

        export var xxx = value1;
        export let yyy = value2;
        
        var xxx = value1;
        let yyy = value2;
        export {xxx, yyy};
        
    • 引入使用模块:import

      • default 模块:

        import xxx from '模块路径/模块名';
        
      • 其他模块:

        import {xxx, yyy} from '模块路径/模块名';
        import * as module1 from '模块路径/模块名';
        
  • 问题: 所有浏览器还不能直接识别ES6模块化的语法

    • 解决:
      • 使用Babel将ES6转换成ES5(使用了CommonJS) ----浏览器还不能直接运行
      • 使用Browserify进行打包处理----浏览器可以运行
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

漂亮疯了

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值