浅谈CommonJs规范和Es6模块化

前言: 前端模块化一直被我们所提及,无论是前端框架应用还是Node.js,都离不开模块化,而现在最常用的就是CommonJS和ES6规范。

CommonJS

(1)CommonJS规范是一种同步加载模块的方式,也就是说,只有当模块加载完成后,才能执行后面的操作。由于Nodejs主要用于服务器端编程,而模块文件一般都已经存在于本地硬盘,加载起来比较快,
因此同步加载模块的CommonJS规范就比较适用。

(2)CommonJS规范规定,每一个JS文件就是一个模块,有自己的作用域;在一个模块中定义的变量、函数等都是私有变量,对其他文件不可见。

             // number.js

      let num = 1

      function add(x) {

        return num + x

       }      

       module.exports.num = num
       module.exports.add = add

在上面的number.js中,变量num和函数add就是当前文件私有的,其他文件不能访问。

同时CommonJS规定了,每个模块内部都有一个module变量,代表当前模块;这个变量是一个对象,它的exports属性(即module.exports)提供对外导出模块的接口。

module

module代表当前模块,我们打印下module是什么
id:模块的识别符,通常是带有绝对路径的模块文件名
filename:模块的文件名,带有绝对路径。
loaded:返回一个布尔值,表示模块是否已经完成加载。
parent:返回一个对象,表示调用该模块的模块。
children:回一个数组,表示该模块要用到的其他模块。
exports:模块对外输出的对象。
path:模块的目录名称。
paths:模块的搜索路径。

exports

exports和module.exports都是用于导出模块,他们指向的是一个地址,相当于var exports = module.exports,我们在导出的时候可以用诸如 exports.a = ‘1233’ 的形式来导出。

但是有一点需要注意的是不可以用exports = 'a’的形式导出,这种方式会切断exports 和 module.exports之间的联系,module.exports实际上还是指向了默认值(空对象),最终导出的结果也是空对象。

require

require是导入命令,它的基本功能是读取并执行js文件,并返回该模块导出的module.exports对象

以下是打印的require信息。

{
    id: '.',
    path: 'd:\\Users\\72150591\\Desktop\\新建文件夹\\beesapp',
    exports: {},
    parent: null,
    filename: 'd:\\Users\\72150591\\Desktop\\新建文件夹\\beesapp\\b.js',
    loaded: false,
    children: [ [Module] ],
    paths: [
      'd:\\Users\\72150591\\Desktop\\新建文件夹\\beesapp\\node_modules',
      'd:\\Users\\72150591\\Desktop\\新建文件夹\\node_modules',
      'd:\\Users\\72150591\\Desktop\\node_modules',
      'd:\\Users\\72150591\\node_modules',
      'd:\\Users\\node_modules',
      'd:\\node_modules'
    ]
  },
  extensions: [Object: null prototype] {
    '.js': [Function (anonymous)],
    '.json': [Function (anonymous)],
    '.node': [Function (anonymous)]
  },
  cache: [Object: null prototype] {
    'd:\\Users\\72150591\\Desktop\\新建文件夹\\beesapp\\b.js': Module {
      id: '.',
      path: 'd:\\Users\\72150591\\Desktop\\新建文件夹\\beesapp',
      exports: {},
      parent: null,
      filename: 'd:\\Users\\72150591\\Desktop\\新建文件夹\\beesapp\\b.js',
      loaded: false,
      children: [Array],
      paths: [Array]
    },
    'd:\\Users\\72150591\\Desktop\\新建文件夹\\beesapp\\a.js': Module {
      id: 'd:\\Users\\72150591\\Desktop\\新建文件夹\\beesapp\\a.js',
      path: 'd:\\Users\\72150591\\Desktop\\新建文件夹\\beesapp',
      exports: [Object],
      parent: [Module],
      filename: 'd:\\Users\\72150591\\Desktop\\新建文件夹\\beesapp\\a.js',
      loaded: true,
      children: [],
      paths: [Array]
    }
  }
}

requre对象中有多个属性,其中cache属性对于模块缓存具有重大的作用

(1) 模块缓存
当我们在一个项目中多次require同一个模块时,CommonJS并不会多次执行该模块文件;而是在第一次加载时,将模块缓存;以后再加载该模块时,就直接从缓存中读取该模块:

               比如:// num.js

                        console.log('run num')

                        module.exports = { num: 1 }

                          // main.js

                         let number1 = require('./num');

                         let number2 = require('./num');

                         number2.num = 2;

                         let number3 = require('./num')

                         console.log(number3)

运行main.js时发现运行的结果是 // run num // 2

结论:我们多次require加载number模块,但是内部只有一次打印输出;第二次加载时还改变了内部变量的值,第三次加载时内部变量的值还是上一次的赋值,这就证明了后面的require读取的是缓存。

而该现象的出现正是由于require属性的cache实现的。

为了验证,我们可以执行 delete require.cache[d:\Users\72150591\Desktop\新建文件夹\beesapp\b.js]命令,结果就完全不一样,会打印三次run num,且number3的输出为1,
说明require每次执行都是重新执行,和之前的执行结果没有依赖关系。

( 2)加载机制
CommonJS的加载机制是,模块输出的是一个值的复制拷贝;对于基本数据类型的输出,属于复制,对于复杂数据类型,属于浅拷贝。

这两者之间的关系相当于:
(1) var a = 1; b = a,b=2,b的改变并不会导致a的值的改变,属于值的复制;

(2) var a ={a:111};b=a,b={a:222};因为是浅拷贝,两者之间指向的是同一个地址,所以两者之前的值会相互影响。

ES6

与CommonJS规范动态加载不同,ES6模块化的设计思想是尽量的静态化,使得在编译时就能够确定模块之间的依赖关系

export

和CommonJS相同,ES6规范也定义了一个JS文件就是一个独立模块,模块内部的变量都是私有化的,其他模块无法访问;不过ES6通过export关键词来导出变量、函数或者类:

这两种写法是等价的

import

使用export导出模块对外接口后,其他模块文件可以通过import命令加载这个接口

注意: import命令具有提升效果,会提升到整个模块的头部,首先执行。

export和import 都可以用as命令进行重命名

export default

在上面的import和export中都要知道具体的接口名称,当我们只需导出一个接口名称时,则可以用export default命令导出,同时,import导入模块的接口名称可以自定义,不需要和模块文件关联。

加载机制

诸如例子:
正是由于ES6模块只是输出了一个对外的接口,我们可以把这个接口理解为一个引用,实际的值还是在模块中;而且这个引用还是一个只读引用,不论是基本数据类型还是复杂数据类型

和require一样,import也会对导入的模块进行缓存,重复import导入同一个模块,只会执行一次

总结归纳

通过上面我们对CommonJS规范和ES6规范的比较,我们总结一下两者的区别:

(!) CommonJS模块是运行时加载,ES6模块是编译时输出接口
(2) CommonJS模块输出的是一个值的复制,ES6模块输出的是值的引用
(3)CommonJS加载的是整个模块,即将所有的方法全部加载进来,ES6可以单独加载其中的某个方法
(4) CommonJS中this指向当前模块,ES6中this指向undefined
(5) CommonJS默认非严格模式,ES6的模块自动采用严格模式

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值