刀耕火种时代
举例说明
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title><script src="./a.js"></script><script src="./b.js"></script></head><body>Hello, 请打开控制台查看效果<script>console.log(a)</script></body></html>
// a.js:var a = 1// b.js:var a = 2
命名空间
命名空间模式:对象封装
- 作用:减少全局变量,解决命名冲突
- 问题:数据不安全(外部可以直接修改模块内部的数据)
举例说明
// demo1let yingzi.dada.utils = {weight: 100,showWeight: function(){console.log(this.weight)}}// demo2if (org.cometd.Utils.isString(response)) {return org.cometd.JSON.fromJSON(response);}if (org.cometd.Utils.isArray(response)) {return response;}
IIFE匿名函数自调用
函数自调用实现的方式:(func)()
原理:JavaScript中,函数拥有自己的作用域,一个函数里的变量,不会被声明在全局变量中
举例说明
index.html
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title><script src="./jquery.js"></script><script src="./utils.js"></script></head><body><script>dadaUtils.changeBodyBackgournd()</script></body></html>
utils.js
(function(window, jQuery) {let weight = 100//操作数据的函数function showWeight() {//用于暴露有函数console.log(`your weight is ${weight}`)}function changeBodyBackgournd() {// $('body').css('background', 'red')jQuery('body').css('background', 'red')}//暴露行为window.dadaUtils = { showWeight, changeBodyBackgournd } //ES6写法})(window, jQuery)
IIFE的问题:
- 使用模块之间的依赖关系变得明显,否则跑不通
- 引入多个script,导致请求过多
以上两种原因就导致了很难维护,很可能出现牵一发而动全身的情况导致项目出现严重的问题。
Commonjs、AMD、CMD
Commonjs
概述
Node 应用由模块组成,采用 CommonJS 模块规范。每个文件就是一个模块,有自己的作用域。在一个文件里面定义的变量、函数、类,都是私有的,对其他文件不可见。在服务器端,模块的加载是运行时同步加载的;在浏览器端,模块需要提前编译打包处理。
特点
- 所有代码都运行在模块作用域,不会污染全局作用域。
- 模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,
- 模块加载的顺序,按照其在代码中出现的顺序。
- commonJS模块的加载机制是一旦输出一个值,模块内部的变化就影响不到这个值
- 它是属于服务端的实现,想在浏览器运行,需要借助browserify
基本语法
- 暴露模块:module.exports = value 或 exports.xx = value
- 引入模块:require(xx),如果是第三方模块,xx为模块名;如果是自定义模块,xx为模块文件路径
举例说明
index.js
// 引入第三方库,应该放置在最前面let uniq = require('uniq')let module1 = require('./module1')let module2 = require('./module2')let module22 = require('./module2')let module3 = require('./module3')module1.foo() //module1module3.foo() //foo() module3console.log(uniq(module3.arr)) //[ 1, 2, 3 ]console.log('-------------------------')console.log(module2.counter)module2.incCounter()console.log(module2.counter)// load module2 未输出两次,代表仅加载一次,缓存结果// module1// foo() module3// [ 1, 2, 3 ]// -------------------------// 3// 3 值未变化,说明内部变化不会影响值的改变
module1.js
module.exports = {msg: 'module1',foo() {console.log(this.msg)}}
module2.js
console.log('load module2')var counter = 3function incCounter() {counter++}module.exports = {counter: counter,incCounter: incCounter}
module3.js
exports.foo = function() {console.log('foo() module3')}exports.arr = [1, 2, 3, 3, 2]
AMD(Requirejs)
概述
Requirejs是AMD(Asynchronous Module Definition)规范的代表,AMD加载模式非同步,一般用于浏览器端
RequireJS的基本思想是,通过define方法,将代码定义为模块;通过require方法,实现代码的模块加载
特点
- 模块定义清晰,清楚显示依赖关系
- 依赖前置
举例说明
index.html
<!DOCTYPE html><html><head><title>Modular Demo</title></head><body><!-- 引入require.js并指定js主文件的入口 --><script data-main="./main" src="./lib/require.js"></script></body></html>
main.js
(function() {require.config({baseUrl: './', //基本路径 出发点在根目录下paths: {//映射: 模块标识名: 路径alerter: './modules/alerter', //此处不能写成alerter.js,会报错dataService: './modules/dataService'}})require(['alerter'], function(alerter) {alerter.showMsg()})})()
alerter.js
// 定义有依赖的模块define(['dataService'], function(dataService) {let name = 'Tom';function showMsg() {alert(dataService.getMsg() + ', ' + name);}// 暴露模块return { showMsg };});
dataService.js
// 定义没有依赖的模块define(function() {let msg = 'www.baidu.com';function getMsg() {return msg.toUpperCase();}return { getMsg }; // 暴露模块});
CMD(Seajs)
概述
Seajs是CMD(Common Module Definition)规范的代表,CMD加载模式也是异步
SeaJS规范整合了CommonJS和AMD规范的特点
特点
- 模块定义清晰,清楚显示依赖关系
- CMD推崇依赖就近和延迟执行
举例说明
index.html
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title></head><body><script type="text/javascript" src="js/libs/sea.js"></script><script type="text/javascript">seajs.use('./js/modules/main')</script></body></html>
main.js
define(function(require) {var m1 = require('./module1');var m4 = require('./module4');m1.show();m4.show();});
module1.js
define(function(require, exports, module) {//内部变量数据var data = 'atguigu.com';//内部函数function show() {console.log('module1 show() ' + data);}//向外暴露exports.show = show;});
module2.js
define(function(require, exports, module) {module.exports = {msg: 'I Will Back',};});
module3.js
define(function(require, exports, module) {const API_KEY = 'abc123';exports.API_KEY = API_KEY;});
module4.js
define(function(require, exports, module) {//引入依赖模块(同步)var module2 = require('./module2');function show() {console.log('module4 show() ' + module2.msg);}exports.show = show;//引入依赖模块(异步)require.async('./module3', function(m3) {console.log('异步引入依赖模块3 ' + m3.API_KEY);});});
ES6 Module
CommonJS适用于服务端,AMD/CMD适用于浏览器端,各有各自的局限性,为了打破这种局面,ECMAScript6 Module诞生了
ES6模块与CommonJS模块差异
- CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
- CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。
举例说明
index.html
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title></head><body><script type="module">import { counter, incCounter } from './lib.js'console.log(counter)incCounter()console.log(counter)</script></body></html>
lib.js
export let counter = 3export function incCounter() {counter++}
