CommonJS与ES6模块化语法

CommonJS

概述

CommonJS定义一个文件就是一个模块,有自己的作用域。在一个文件里面定义的变量、函数、类,都是私有的,对其他文件不可见。在服务器端,模块的加载是运行时同步加载的;在浏览器端,模块需要提前编译打包处理。在node.js的实现中,也给每个文件赋予了一个module对象,这个对象包括了描述当前模块的所有信息,我们尝试打印module对象。

在CommonJS里面,模块是用对象来表示

我们通过“循环加载”的例子进行来加深了解。


我们的理论依据是模块对象,根据该依据我们进行如下分析
例子里面还出现了一个保留字exports。其实exports是指向module.exports的一个引用。

举个例子可以说明他们两个之间的关系。

特点

所有代码都运行在模块作用域,不会污染全局作用域。

模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接* * 读取缓存结果。要想让模块再次运行,必须清除缓存。

模块加载的顺序,按照其在代码中出现的顺序。

基本语法

暴露模块:module.exports = value或exports.xxx = value

引入模块:require(xxx),如果是第三方模块,xxx为模块名;如果是自定义模块,xxx为模块文件路径

此处我们有个疑问:CommonJS暴露的模块到底是什么? CommonJS规范规定,每个模块内部,module变量代表当前模块。这个变量是一个对象,它的exports属性(即module.exports)是对外的接口。加载某个模块,其实是加载该模块的module.exports属性。


上面代码通过module.exports输出变量x和函数addX。  
require命令用于加载模块文件。

require命令的基本功能是,读入并执行一个JavaScript文件,然后返回该模块的exports对象。如果没有发现指定模块,会报错。

模块的加载机制

CommonJS模块的加载机制是,输入的是被输出的值的拷贝。也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。这点与ES6模块化有重大差异,请看下面这个例子:

上面代码输出内部变量counter和改写这个变量的内部方法incCounter。  
上面代码说明,counter输出以后,lib.js模块内部的变化就影响不到counter了。

这是因为counter是一个原始类型的值,会被缓存。除非写成一个函数,才能得到内部变动后的值。

ES6 module

ES6模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS和AMD模块,都只能在运行时确定这些东西。比如,CommonJS模块就是对象,输入时必须查找对象属性。

ES6模块化语法

export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。

如上例所示,使用import命令的时候,用户需要知道所要加载的变量名或函数名,否则无法加载。

为了给用户提供方便,让他们不用阅读文档就能加载模块,就要用到export default命令,为模块指定默认输出。

模块默认输出, 其他模块加载该模块时,import命令可以为该匿名函数指定任意名字。

从保留字对比下ES6和CommonJS

除了require两个都可以用之外,其他实际上还是有明显差别的。那么问题来了,既然require两个都可以用,那这两个在require使用上,有差异吗?

先对比下ES6 module和CommonJS之间的差异

通过例子来介绍一下值拷贝和引用的区别

静态解析,什么是的静态解析呢?区别于CommonJS的模块实现,ES6的模块并不是一个对象,而只是代码集合。也就是说,ES6不需要和CommonJS一样,需要把整个文件加载进去,形成一个对象之后,才能知道自己有什么,而是在编写代码的过程中,代码是什么,它就是什么。

工具时代

webpack

webpack兴起之后,什么 AMD、CMD、CommonJS、UMD,似乎都变得不重要了。

webpack在定义模块上,可以支持CommonJS、AMD和ES6的模块声明方式,换句话说,就是你的模块如果是使用CommonJS、AMD或ES6的语法写的,webpack都支持!我们看下例子:


不仅如此,webpack识别了你的模块之后,可以将其打包成UMD、AMD等等规范的模块重新输出。例如上文提及到的你需要把Date模块封装成UMD格式。只需要在webpack的output中添加libraryTarget: 'UMD'即可。

你可能感兴趣的:(CommonJS与ES6模块化语法)