刀耕火种时代

举例说明

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <meta http-equiv="X-UA-Compatible" content="ie=edge">
  7. <title>Document</title>
  8. <script src="./a.js"></script>
  9. <script src="./b.js"></script>
  10. </head>
  11. <body>
  12. Hello, 请打开控制台查看效果
  13. <script>
  14. console.log(a)
  15. </script>
  16. </body>
  17. </html>
  1. // a.js:
  2. var a = 1
  3. // b.js:
  4. var a = 2

命名空间

命名空间模式:对象封装

  • 作用:减少全局变量,解决命名冲突
  • 问题:数据不安全(外部可以直接修改模块内部的数据)

举例说明

  1. // demo1
  2. let yingzi.dada.utils = {
  3. weight: 100
  4. showWeight: function(){
  5. console.log(this.weight)
  6. }
  7. }
  8. // demo2
  9. if (org.cometd.Utils.isString(response)) {
  10. return org.cometd.JSON.fromJSON(response);
  11. }
  12. if (org.cometd.Utils.isArray(response)) {
  13. return response;
  14. }

IIFE匿名函数自调用

函数自调用实现的方式:(func)()

原理:JavaScript中,函数拥有自己的作用域,一个函数里的变量,不会被声明在全局变量中

举例说明

index.html

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <meta http-equiv="X-UA-Compatible" content="ie=edge">
  7. <title>Document</title>
  8. <script src="./jquery.js"></script>
  9. <script src="./utils.js"></script>
  10. </head>
  11. <body>
  12. <script>
  13. dadaUtils.changeBodyBackgournd()
  14. </script>
  15. </body>
  16. </html>

utils.js

  1. (function(window, jQuery) {
  2. let weight = 100
  3. //操作数据的函数
  4. function showWeight() {
  5. //用于暴露有函数
  6. console.log(`your weight is ${weight}`)
  7. }
  8. function changeBodyBackgournd() {
  9. // $('body').css('background', 'red')
  10. jQuery('body').css('background', 'red')
  11. }
  12. //暴露行为
  13. window.dadaUtils = { showWeight, changeBodyBackgournd } //ES6写法
  14. })(window, jQuery)

IIFE的问题

  • 使用模块之间的依赖关系变得明显,否则跑不通
  • 引入多个script,导致请求过多

以上两种原因就导致了很难维护,很可能出现牵一发而动全身的情况导致项目出现严重的问题。

Commonjs、AMD、CMD

Commonjs

概述

Node 应用由模块组成,采用 CommonJS 模块规范。每个文件就是一个模块,有自己的作用域。在一个文件里面定义的变量、函数、类,都是私有的,对其他文件不可见。在服务器端,模块的加载是运行时同步加载的;在浏览器端,模块需要提前编译打包处理。

特点

  • 所有代码都运行在模块作用域,不会污染全局作用域。
  • 模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,
  • 模块加载的顺序,按照其在代码中出现的顺序。
  • commonJS模块的加载机制是一旦输出一个值,模块内部的变化就影响不到这个值
  • 它是属于服务端的实现,想在浏览器运行,需要借助browserify

基本语法

  • 暴露模块:module.exports = value 或 exports.xx = value
  • 引入模块:require(xx),如果是第三方模块,xx为模块名;如果是自定义模块,xx为模块文件路径

举例说明

index.js

  1. // 引入第三方库,应该放置在最前面
  2. let uniq = require('uniq')
  3. let module1 = require('./module1')
  4. let module2 = require('./module2')
  5. let module22 = require('./module2')
  6. let module3 = require('./module3')
  7. module1.foo() //module1
  8. module3.foo() //foo() module3
  9. console.log(uniq(module3.arr)) //[ 1, 2, 3 ]
  10. console.log('-------------------------')
  11. console.log(module2.counter)
  12. module2.incCounter()
  13. console.log(module2.counter)
  14. // load module2 未输出两次,代表仅加载一次,缓存结果
  15. // module1
  16. // foo() module3
  17. // [ 1, 2, 3 ]
  18. // -------------------------
  19. // 3
  20. // 3 值未变化,说明内部变化不会影响值的改变

module1.js

  1. module.exports = {
  2. msg: 'module1',
  3. foo() {
  4. console.log(this.msg)
  5. }
  6. }

module2.js

  1. console.log('load module2')
  2. var counter = 3
  3. function incCounter() {
  4. counter++
  5. }
  6. module.exports = {
  7. counter: counter,
  8. incCounter: incCounter
  9. }

module3.js

  1. exports.foo = function() {
  2. console.log('foo() module3')
  3. }
  4. exports.arr = [1, 2, 3, 3, 2]

AMD(Requirejs)

概述

Requirejs是AMD(Asynchronous Module Definition)规范的代表,AMD加载模式非同步,一般用于浏览器端

RequireJS的基本思想是,通过define方法,将代码定义为模块;通过require方法,实现代码的模块加载

特点

  • 模块定义清晰,清楚显示依赖关系
  • 依赖前置

举例说明

index.html

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>Modular Demo</title>
  5. </head>
  6. <body>
  7. <!-- 引入require.js并指定js主文件的入口 -->
  8. <script data-main="./main" src="./lib/require.js"></script>
  9. </body>
  10. </html>

main.js

  1. (function() {
  2. require.config({
  3. baseUrl: './', //基本路径 出发点在根目录下
  4. paths: {
  5. //映射: 模块标识名: 路径
  6. alerter: './modules/alerter', //此处不能写成alerter.js,会报错
  7. dataService: './modules/dataService'
  8. }
  9. })
  10. require(['alerter'], function(alerter) {
  11. alerter.showMsg()
  12. })
  13. })()

alerter.js

  1. // 定义有依赖的模块
  2. define(['dataService'], function(dataService) {
  3. let name = 'Tom';
  4. function showMsg() {
  5. alert(dataService.getMsg() + ', ' + name);
  6. }
  7. // 暴露模块
  8. return { showMsg };
  9. });

dataService.js

  1. // 定义没有依赖的模块
  2. define(function() {
  3. let msg = 'www.baidu.com';
  4. function getMsg() {
  5. return msg.toUpperCase();
  6. }
  7. return { getMsg }; // 暴露模块
  8. });

CMD(Seajs)

概述

Seajs是CMD(Common Module Definition)规范的代表,CMD加载模式也是异步

SeaJS规范整合了CommonJS和AMD规范的特点

特点

  • 模块定义清晰,清楚显示依赖关系
  • CMD推崇依赖就近和延迟执行

举例说明

index.html

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <meta http-equiv="X-UA-Compatible" content="ie=edge">
  7. <title>Document</title>
  8. </head>
  9. <body>
  10. <script type="text/javascript" src="js/libs/sea.js"></script>
  11. <script type="text/javascript">
  12. seajs.use('./js/modules/main')
  13. </script>
  14. </body>
  15. </html>

main.js

  1. define(function(require) {
  2. var m1 = require('./module1');
  3. var m4 = require('./module4');
  4. m1.show();
  5. m4.show();
  6. });

module1.js

  1. define(function(require, exports, module) {
  2. //内部变量数据
  3. var data = 'atguigu.com';
  4. //内部函数
  5. function show() {
  6. console.log('module1 show() ' + data);
  7. }
  8. //向外暴露
  9. exports.show = show;
  10. });

module2.js

  1. define(function(require, exports, module) {
  2. module.exports = {
  3. msg: 'I Will Back',
  4. };
  5. });

module3.js

  1. define(function(require, exports, module) {
  2. const API_KEY = 'abc123';
  3. exports.API_KEY = API_KEY;
  4. });

module4.js

  1. define(function(require, exports, module) {
  2. //引入依赖模块(同步)
  3. var module2 = require('./module2');
  4. function show() {
  5. console.log('module4 show() ' + module2.msg);
  6. }
  7. exports.show = show;
  8. //引入依赖模块(异步)
  9. require.async('./module3', function(m3) {
  10. console.log('异步引入依赖模块3 ' + m3.API_KEY);
  11. });
  12. });

ES6 Module

CommonJS适用于服务端,AMD/CMD适用于浏览器端,各有各自的局限性,为了打破这种局面,ECMAScript6 Module诞生了

ES6模块与CommonJS模块差异

  • CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
  • CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。

举例说明

index.html

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <meta http-equiv="X-UA-Compatible" content="ie=edge">
  7. <title>Document</title>
  8. </head>
  9. <body>
  10. <script type="module">
  11. import { counter, incCounter } from './lib.js'
  12. console.log(counter)
  13. incCounter()
  14. console.log(counter)
  15. </script>
  16. </body>
  17. </html>

lib.js

  1. export let counter = 3
  2. export function incCounter() {
  3. counter++
  4. }

参考文章