JavaScript模块化的演变
JavaScript模块化的演变
前情回顾:
自执行函数(IIFE):
作用:马上执行这个函数,自执行函数(IIFE),不易读
(function(x){console.log(x);})(3);
易读版本:
(function(x){ return x *x; })(3);
闭包引申:
回顾:
function create_counter(initial){ var x = initial || 0; //如果initial没有值,那么采用后面的0 return { //对象 inc:function(){ x+=1; return x; } } }var c1 = create_counter();console.log(c1.inc());console.log(c1.inc());console.log(c1.inc());var c2 = create_counter(10);console.log(c2.inc());console.log(c2.inc());console.log(c2.inc()); */
箭头函数:
function (x){ return x * x; }
上述代码等价于下面:
x => x*x;
箭头函数的无参、单参、双参、多参的格式:
//无参()=>3;//单参x =>{ if(x > 0){ return x*x; }else{ return -x*x; } }//双参(x,y) => x*x+y*y;//多参(x,y,...rest)=>{ }
this指向的引入以及发展:
this的指向在有无use strict
会不同的,我们通过几段不同的代码段引入this以及this指向的发展。
'use strict';//1版-->正常使用var xiaoming = { name:'小明', birth:2000, age:function(){ var y = new Date().getFullYear(); return y - this.birth; } };console.log(xiaoming.age());
//2版:'use strict';/* 严格模式: xiaoming.age() 可以得到结果, getAge()显示报错--> Cannot read property 'birth' of undefined */function getAge(){ var y = new Date().getFullYear(); return y - this.birth; }var xiaoming = { name:'小明', birth:2000, age:getAge };console.log(xiaoming.age());console.log(getAge());
前两个版本暂时还是没有引出this以及指向,下面看第三个版本,这个版本会有较大的改变,在不在strict中的结果也会不一样。
'use strict';/* 严格模式下:会报错,Cannot read property 'birth' of undefined 非严格模式下:NaN not a number,浏览器不会爆红,可能指向window */var obj = { birth:2000, getAge:function(){ function GetAgeFormBirth(){ var y = new Date().getFullYear(); return y - this.birth; } return GetAgeFormBirth(); } };console.log(obj.getAge());
这是js的遗留问题,为了解决这个问题,我们可以使用变量保存this的指向,如版本四:
'use strict';var obj = { birth:2000, getAge:function(){ //保存thisz指向 var that = this; function GetAgeFormBirth(){ var y = new Date().getFullYear(); return y - that.birth; } return GetAgeFormBirth(); } };console.log(obj.getAge());
但是这种表述方式比较麻烦,代码量也有些多,我们可以采用箭头函数来实现:
//这是个对象'use strict';var obj = { birth:2000, getAge:function(){ var b = this.birth; var fn = ()=> new Date().getFullYear()-this.birth; return fn(); } };console.log(obj.getAge());
模块化演变:
模块化演变是为了初学者一步一步实现和解决代码的冗余、复用性、命名污染问题。
模块演变1:
缺点已在代码块中标明
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title></head><body> <!-- html --》"" --> <!-- js --》'' --> <!-- 使用内嵌脚本 --> <!-- 缺点: 复用性 很低 1.缺乏依赖解析 2.全局命名空间污染的问题 --> <h1> the answer is <span id="answer"></span> </h1> <script> function add(a,b){ return a+b; } function reduce(arr,iteratee){ var index=0,length = arr.length,memo = arr[index]; index+=1; for(index =0;index < length;index++){ memo = iteratee(memo,arr[index]); } return memo; } function sum(arr){ return reduce(arr,add); } var values = [1,2,3,4,5,6,7,8,9]; var answer = sum(values); document.getElementById("answer").innerHTML = answer; </script></body></html>
模块演变2:
add.js
function add(a,b){ return a+b; }
reduce.js
function reduce(arr,iteratee){ var index=0,length = arr.length,memo = arr[index]; index+=1; for(index =0;index < length;index++){ memo = iteratee(memo,arr[index]); } return memo; }
sum.js
function sum(arr){ return reduce(arr,add); }
main.js
var values = [1,2,3,4,5,6,7,8,9];var answer = sum(values);document.getElementById("answer").innerHTML = answer;
index.html
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title></head><body> <h1> the answer is <span id="answer"></span> </h1> <!-- script标签引入JavaScript --> <!-- 缺点: 必须保证js的引用顺序正确 同样缺乏依赖解析 同样有命名冲突的问题 --> <script src = "./add.js"></script> <script src = "./reduce.js"></script> <script src="./sum.js"></script> <script src = "./main.js"></script></body></html>
模块演变3:
myApp.js
// 空对象var myApp = { };
add.js
// 立即执行函数(function (){ // 将add放入myApp中 myApp.add = function(a,b){ return a+b; } })();
reduce.js
// 立即执行函数IIFE(function (){ // 将reduce放入myApp中 myApp.reduce = function(arr,iteratee){ var index=0,length = arr.length,memo = arr[index]; index+=1; for(;index < length;index++){ memo = iteratee(memo,arr[index]); } return memo; } })();
sum.js
// 立即执行函数(function (){ // 将sum放入myApp中 myApp.sum = function(arr){ return myApp.reduce(arr,myApp.add); } })();
main.js
/** * @description: * @param {*} * @return {*} *//* (function(){ var values = [1,2,3,4,5,6,7,8,9]; var answer = myApp.sum(values); document.getElementById("answer").innerHTML = answer; }); */(function(app){ var values = [1,2,3,4,5,6,7,8,9]; var answer = myApp.sum(values); document.getElementById("answer").innerHTML = answer; })(myApp);
index.html
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title></head><body> <h1> the answer is <span id="answer"></span> </h1> <script src="./myapp.js"></script> <script src="./reducec.js"></script> <script src="./sum.js"></script> <script src="./add.js"></script> <script src="./main.js"></script> </body></html>
模块演变4:
采用require.js来实现。
为什么要使用require.js来实现:
在正式开发的项目中,随着js的外部引用文件越来越多,网页失去响应的时间就会越长;其次,由于js文件之间存在依赖关系,因此必须严格保证加载顺序,依赖性最大的模块一定要放到最后加载,当依赖关系很复杂的时候,代码的编写和维护都会变得困难。
在开发中,有一个网页三秒原则
;
网页三秒原则:
当用户的在请求一个网站的时候,响应时间超过三秒钟,大部分用户将关闭或者重新刷新网页,用户体验很不爽。
所以require.js就解决上述问题:
实现js文件的异步加载,避免网页失去响应;
管理模块之间的依赖性,便于代码的编写和维护。
require.js的AMD格式规范:
// main.jsdefine(['moduleA', 'moduleB', 'moduleC'], function (moduleA, moduleB, moduleC){ // some code here});
define()函数接受的参数。
第一个参数是一个数组,表示所依赖的模块,上例就是['moduleA', 'moduleB', 'moduleC'],即主模块依赖这三个模块;
第二个参数是一个回调函数,当前面指定的模块都加载成功后,它将被调用。加载的模块会以参数形式传入该函数,从而在回调函数内部就可以使用这些模块。
模块必须采用特定的define()函数来定义。如果一个模块不依赖其他模块,那么可以直接定义在define()函数之中
如果这个模块还依赖其他模块,那么define()函数的第一个参数,必须是一个数组,且指明该模块的依赖性。
define()异步加载moduleA,moduleB和moduleC,浏览器不会失去响应;它指定的回调函数,只有前面的模块都加载成功后,才会运行,解决了依赖性的问题。
以下是模块演变4的实现代码:
index.js
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title></head><body> <h1> the answer is <span id="answer"></span> </h1> <!-- 入口点是main --> <!-- require加载了main.js的依赖,也会加载它依赖的依赖 --> <!-- 带来的问题: 1. 降低一些性能 --> <script data-main="main" src = "require.js"></script></body></html>
data-main属性的作用是,指定网页程序的主模块
main.js
define(['sum'],function(sum){ var value = [1,2,3,4,5,6,7,8,9]; var answer = sum(value); document.getElementById('answer').innerHTML = answer; })
sum.js
define([ 'add', 'reduce'], function(add, reduce) { var sum = function(arr){ return reduce(arr,add); } return sum; });
reduce.js
define([], function(arr,iteratee){ var reduce = function(arr,iteratee){ var index=0,length = arr.length,memo = arr[index]; index+=1; for(;index < length;index++){ memo = iteratee(memo,arr[index]); } return memo; } return reduce; });
add.js
define([],function(){ var add = function(a,b){ return a+b; } return add; })
分析上述代码的执行流程:
先加载index.html以及require.js文件,找到模块的主入口(main.js)
加载main.js,由于main中依赖sum.js
再加载sum.js,sum中依赖add.js以及reduce.js
再加载add.js以及reduce.js
最后全部依赖执行完成后,回调得到的结果
执行效果以及文件加载顺序的观察:
模块演变5:
基于commonJS规范以及browserify浏览器端的模块演变
什么是browserify:
browserify是专注于解决按照CommonJS规范书写的模块能够在浏览器中使用问题的构建工具。
CommonJS规范:
每个文件就是一个模块,有自己的作用域。在一个文件里面定义的变量、函数、类,都是私有的,对其他文件不可见
每个模块内部,
module
变量代表当前模块。这个变量是一个对象,它的exports
属性(即module.exports
)是对外的接口。加载某个模块,其实是加载该模块的module.exports
属性。
下载方式:
npm install -g browserify
代码如下:
index.js
<!DOCTYPE html><html> <head> <meta charset="utf-8"> <title></title> </head> <body> <h1> The Answer is <span id="answer"></span> </h1> <!-- browserify .\main.js -o bundle.js 打包--> <script src="bundle.js"></script> </body></html>
注意点:引用的js的文件是与我们打包命令时 -o后面名字对应的。
main.js
var sum = require('./sum');var values = [1,2,3,4,5,6,7,8,9];var answer = sum(values);document.getElementById("answer").innerHTML = answer;
sum.js
var reduce = require('./reduce');var add = require('./add');module.exports = function(arr){ return reduce(arr,add); };
reduce.js
module.exports = function reduce(arr,iteratee){ var index=0,length = arr.length,memo = arr[index]; index+=1; for(;index < length;index++){ memo = iteratee(memo,arr[index]); } return memo; };
add.js
module.exports = function add(a,b){ return a+b; };
打包方式:
browserify .\main.js -o bundle.js //window中
效果图:
运行查看加载信息:
可以与require.js的加载方式做对比,理解两者的不同以及相同的地方。
扩展:
require.js VS browserify
require.js是模块加载器;browserify是预编译工具
require.js遵循的是AMD规范;browserify遵循的是CommonJS规范
require.js是在浏览器端运行期分析依赖;browserify在服务器端编译期就进行了依赖分析,通过每个模块的外部接口获取信息
结束:
如果你看到这里或者正好对你有所帮助,希望能点个关注或者推荐,感谢;
有错误的地方,欢迎在评论指出,作者看到会进行修改。
__EOF__
本文作者:YLJ
本文链接:https://www.cnblogs.com/xbhog/p/15001650.html