首先啥子也不说先写一个
在es5中是没有块级作用域额概念的,,我们一般都是这么来进行模拟的
var long = (function (){
var baobao = 1,
bao=2;
function long1(){}
function long2(){}
return {
fn1 : long1,
fn2 :long2
}
})()
这样的写法,我们在模块的外部无法修改我们没有暴露出来的变量,函数,上面的做法就是我们进行模块化开发的基础,
目前来说通用的js模块化开发的规范主要有commmon.js 和AMD
common.js
因为在网页端没有模块化变成只是页面js逻辑复杂,但是也是可以工作下去,有过或者了解服务器端的都知道,在服务器端一定要有模块化,所以虽然js在web端发展第一个流行的模块化规范却是由服务端的js应用带来的,common.js规范是有node.js发扬光大的。这也说明了js正式进入了模块化编程的舞台。
一 定义模块
根据common.js规范,一个单独的文件就是一个模块,每一个模块都是一个单独的作用域。通俗一点就是说,在这个模块内部定义的变量,无法被其他模块读取,除非定义为一个全局即global的对象的属性。
二 模块输出
模块只有一个出口 。 module.exports对象,我们需要把模块希望输出的内容放入到该对象
三 加载模块
加载模块使用的就是require方法,该方法读取一个文件并执行,返回文件内部的module.exports对象
例子 : //模块定义为 long。js
var name = "longbaoboao";
function pritne(){
console.log(name):
}
function longs(){
console.log("long"+name);
}
module.exports = {
pritne : pritne,
longs : longs
}
然后我们就可以加载模块了
var longbb = require("long.js")
longbb.pritne();
不同的实现对require的路径要求不同 ,一般情况下可以省略js的扩展名,可以使用相对路径,,也可以使用绝对路径,,甚至可以直接省去路径直接写模块的名字(前提是该模块是系统内置的模块)。
尴尬的浏览器
仔细看上面的代码,会发现require是同步的。模块系统需要同步读取模块文件的内容,并编译执行以得到模块的内容,这个在服务器端很自然,但是要想在浏览器端实现就有很多的问题,在浏览器端,加载js最佳,最容易的方式是在document中插入script标签,但是脚本的标签天生异步,传统的common.js在浏览器环境中无法正常加载。
解决的思路之一是: 开发一个服务器端组件,对模块代码做静态分析,将模块与他的依赖列表一起返回给浏览器,这样很好使,但是但是需要服务器安装额外的组件,并因此要调整一系列的底层结构
另一个就是,用标准的末班来封装模块定义,但是对于模块应该怎么定义和怎么加载,又产生分歧。
AMD
中文 : 异步模块定义 。 他是一个在浏览器端模块化开发的规范,由于不是js原生支持,使用AMD规范进行页面开发需要用到对应的库函数,也就是require.js,实际上AMD是require.js在推广过程中对模块定义的规范化产生的。
require.js 主要解决了两个问题 :
一 多个js文件可能有依赖关系,被依赖的文件需要早于依赖他的文件加载到浏览器。
二 js加载的时候浏览器会停止页面的渲染,加载文件越多,页面失去响应的时间越长。
例子
//定义为myModule.js
define(['dependency'],function(){
var name = "long“;
function printname(){
console.log(name);
};
return {
printname : printname
}
})
//加载模块
require(["myModule],function(my){
my.printname();
})
其语法就是 :
require.js 定义一个函数define 他是一个全局变量,用来定义模块。
define(id , dependencies,factory);
id 可选参数 用来定义模块的标示,如果没有提供该参数,脚本文件名(去掉扩展名)
dependcies 是一个当前模块依赖的模块名称数组
factory 工厂方法 , 模块初始化要执行的函数或对象,如果为函数,他应该只被执行一次,如果是对象,此对象应该是模块的输出值
上面 使用require加载模块;
require([dependencies],function(){})
require()函数接受两个参数
第一个参数是一个数组 ,表示所以来的模块
第二个参数是一个回调函数 当前面制定的模块都加载成功后,他将被调用,加载的模块会以参数的形式传入该函数,从而在毁掉函数的内部就可以使用这些模块。
require() 函数在加载依赖的函数的时候是异步加载的,这样浏览器不会失去响应,他指定的回调函数,只有前面的模块第一加载成功后,才会执行,解决了依赖性的问题
CMD 通用模块定义
CMD规范是在国内发展出来的, 使用的是sea.js,sea.js要解决的问题和require.js 一样,只不过在模块定义方式额模块加载(可以说运行,解析)时机上有所不同通。
在语法上
sea.js 崇尚一个模块一个文件,遵循统一的写法
define
define(id , deoendcies ,factory)
因为CMD崇尚一个文件一个模块,所以经常就用文件名作为模块id;CMD崇尚模块依赖就近,所以一般不再define的参数中写依赖,而是在factory中写。
factory有三个参数
factory(require , exports , module){}
一 require 是factory函数的第一各参数,require是一个方法,接受模块表示作为唯一参数,用来获取其他模块童工的接口。
二 exports
exports是一个对象,用来向外提供模块的接口。
三 module
module 是一个对象,上面积存储与当前模块相关联的一些属性和方法、
//定义模块 myModule.js
define(function(require,exports,module){
var $ = require("jquery.js");
})
//加载模块
seajs.use(["myModule.js"],function(my){})
AMD 和 CMD 的区别
AMD推崇的是依赖前置 在定义模块的时候就要声明其依赖的模块
CMD 推崇的就近依赖 ,只有在用到某个模块的时候再去require
AMD 和 CMD 最大的区别是对依赖模块的执行时机处理不同,注意不是加载的时机或者方式不同。
很多人说require.js是异步加载模块,sea.js是同步加载模块,这么理解实际上是不准确的,其实加载模块都是异步的,只不过AMD是依赖前置,js可以方便知道依赖模块是谁。立即加载,而CMD就近依赖,需要使用把模块变为字符串解析一遍才知道依赖了那些模块,这也是很多人诟病的CMD 的 点,牺牲性能来带来开发的便利性,实际上解析模块用的时间短到可以忽略。
为什么说两个的区别是依赖模块执行时机不同,为什么很多人认为AMD 是异步的 , CMD 是同步的。
同样是异步加载模块,AMD在加载模块完成后就会执行该模块,所有的模块都加载执行完后会进入require的回调函数中,执行主逻辑,这样的效果就是依赖模块的执行顺序和书写顺序不一定一致,看网络速度,哪个先下载下来,哪个先执行,但是主逻辑一定在所有依赖加载完成后才执行。
CMD加载完某个依赖模块后并不执行,只是下载而已,在所有依赖模块加载完成后进入主逻辑,遇到require语句的时候才执行对应的模块,这样模块的执行顺序和书写顺序是完全一致的。
这也是很多人说AMD用户体验好,因为没有延迟,依赖模块提前执行了,CMD性能好,因为只有用户需要的时候才执行的原因。