Javascript模块化编程演变进程
模块好比一个高度独立的功能类,需要时可以随时引入,不需要时可以随时移除而不会影响到整个系统。使用模块有这样几个好处:
直接定义依赖
最早的时候,我们可能是这样编写代码的
这种方式会在全局对象中加入多个变量,在多人协作时容易造成命名冲突,在代码量多时不利于维护。
命名空间模式
这种模式的思路就是将变量定义在一个规定的对象内,然后这个对象再挂靠在全局对象内。实现方式如下
通过开发者工具可以看到window对象下创建了nav对象,对象内又包含index对象,一次类推。由于就是在全局上定义,所有人都可以对这个对象进行修改,一点也不安全。
闭包模块化模式
闭包函数拥有独立的作用域,其中声明的变量只在该作用域里,可以通过暴露一些方法来访问和操作闭包内的变量。
同是,不同模块之间的应用可以通过参数的形式传递。
这些方案虽然解决了依赖关系的问题,但是没有解决如何管理这些模块,或者说在使用时清晰描述出依赖关系。有兴趣的小伙伴可以去了解下LABjs和YUI,这些都是优秀的模块管理工具。
CommonJS
nodejs的出现使得前端开发者可以使用js开发服务端,并且随着nodejs的普及,CommonJS方案也被人们熟知。CommonJs是一套同步的方案。每个文件都是一个模块,有自己的作用域。在一个文件里面定义的变量、函数、类都是私有的,对其他文件是不见的。
由于服务器上通过require加载资源是直接读取文件的,因此中间所需的时间可以忽略不计,但是在浏览器这种需要依赖HTTP获取资源的就不行,资源的获取所需时间的不确定,这就导致必须使用移除机制。
AMD
与Module/1.0(CommonJS)的差异:
1执行时机的差异
2.书写风格的差异
CMD
CMD模式的代表就是SeaJS。seaJS的书写风格类似CommonJS,与RequireJS有不少不同点。
1.遵循的规范不同
RequireJS遵循的是Modules/AMD规范。
SeaJS遵循的是Modules/wrappings规范的define形式。
2.factory的执行时机不同
ES6
ES6模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。
export default命令用于指定模块的默认输出。显然,一个模块只能有一个默认输出,因此export default命令只能使用一次。所以,import命令后面才不用加大括号,因为只可能唯一对应export default命令。
ES6模块和CommonJS模块的差异
CommonJS模块输出的是值的拷贝,ES6模块输出的是值的引用
CommonJS 模块输出的是值的拷贝,如果这个值类型是简单数据类型,模块内部的变化就影响不到这个值;如果这个是类型是引用数据类型,模块内部的变化是会影响到这个值的。
ES6 模块的运行机制与CommonJS不一样。JS引擎对脚本静态分析的时候,遇到模块加载命令import,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。换句话说,ES6的import有点像Unix系统的“符号连接”,原始值变了,import加载的值也会跟着变。因此,ES6模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。
CommonJS模块是运行时加载,ES6模块是编译时输出接口
CommonJS加载的是一个对象(即module.exports属性),该对象只有在脚本运行完才会生成。而ES6模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。