刀耕火种时代
举例说明
<!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
命名空间
命名空间模式:对象封装
- 作用:减少全局变量,解决命名冲突
- 问题:数据不安全(外部可以直接修改模块内部的数据)
举例说明
// demo1
let yingzi.dada.utils = {
weight: 100,
showWeight: function(){
console.log(this.weight)
}
}
// demo2
if (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() //module1
module3.foo() //foo() module3
console.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 = 3
function 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 = 3
export function incCounter() {
counter++
}