JS中ESModule和commonjs介绍及使用区别

目录
  • ES Module
    • 导出
      • 仅导出
      • 重导出(re-exporting / aggregating)
      • 嵌入式脚本
    • 引入
      • 语法
      • 嵌入式脚本
    • 特点
  • commonJS
    • 导出
    • 引入
    • 特点
      • 值拷贝
  • ES Module和 commonJS区别

ES Module

导出

仅导出

  • named exports: 命名导出,每次可以导出一个或者多个。
  • default exports: 默认导出,每次只能存在一个。

以上两者可以混合导出:

    // 命名导出
    export const b = 'b'
    // 默认导出
    export default {
      a: 1
    };

    const c = 'c'
    export { c }

    // 以上内容会合并导出,即导出为: {b:'b', c:'c', default: {a:1}}

更多示例可以直接去看mdn

重导出(re-exporting / aggregating)

算是一个导入再导出的一个语法糖吧。

  export {
    default as function1,
    function2,
  } from 'bar.js';

  // 等价于
  import { default as function1, function2 } from 'bar.js';
  export { function1, function2 };

然而这种语法是会报错的:

export DefaultExport from 'bar.js'; // Invalid

正确的语法应该是:

export { default as DefaultExport } from 'bar.js'; // valid

我猜是因为export 本身支持的export xxx这种语法必须是要导出一个对象,然而import xxx可能是任意类型,两者冲突了,所以从编译层面就不让这种语法生效会更好。

嵌入式脚本

嵌入式脚本不可以使用export。

引入

语法

  • import all exports: import * as allVar,所有导出内容,包含命名导出及默认导出。allVar会是一个对象,默认导出会作为allVar的key名为default对应的值。
  • import named exports: import {var1, var2},引入命名导出的部分。没找到,对应的值就为undefined。个人觉得可以看做是"import all exports"的解构语法。
  • import default exports: import defaultVar,引入默认导出的部分。
  • import side effects: import "xxx./js",仅运行这个js,可能是为了获取其副作用。
    // test.js
    export const b = 'b'     // 命名导出
    export default {    // 默认导出
      a: 1
    };

    // index.js
    import { b, default as _defaultModule } from './test.js'
    import defaultModule from './test.js'
    import * as allModule from './test.js'

    console.log('name export', b) // 'b'
    console.log('default export', defaultModule) // {a:1}
    console.log(_defaultModule === defaultModule) // true
    console.log('all export', allModule) // {b:'b', default: {a:1}}

一个之前老记错的case

    // test.js
    export default {    // 默认导出
      a: 1
    };

    // index.js
    import { a } from './test.js'
    console.log('name export', a) // undefined

    // index.js
    import defaultModule from './test.js'
    import * as allModule from './test.js'
    console.log('default export', defaultModule) // {a:1}
    console.log('all export', allModule) // {default: {a:1}}

嵌入式脚本

嵌入式脚本引入modules时,需要在script上增加 type="module"。

特点

live bindings:

  • 通过export在mdn上的解释,export导出的是live bindings,再根据其他文章综合判断,应该是引用的意思。即export导出的是引用
  • 模块内的值更新了之后,所有使用export导出值的地方都能使用最新值。

read-only:通过import在mdn上的解释,import使用的是通过export导出的不可修改的引用

strict-mode:被引入的模块都会以严格模式运行。

静态引入、动态引入

import x from这种语法有syntactic rigid,需要编译时置于顶部且无法做到动态引入加载。如果需要动态引入,则需要import ()语法。有趣的是,在mdn上,前者分类到了 Statements & declarations, 后者分类到了 Expressions & operators。这俩是根据什么分类的呢?

  true && import test from "./a.js";
  // SyntaxError: import can only be used in import() or import.meta
  // 这里应该是把import当成了动态引入而报错

示例:

  // a.js
  const test = {
    a: 1
  };
  export default test;
  // 改动模块内部的值
  setTimeout(() => {
    test.a = 2;
  }, 1000);

  // index.js
  import test from './index.js'

  /* live bindings */
  console.log(test) // {a:1}
  setTimeout(()=>{
    console.log(test) // {a:2}
  }, 2000)

  /* read-only */
  test= { a: 3 } // 报错, Error: "test" is read-only.

  /* syntactically rigid */
  if(true){
    import test from './index.js' // 报错, SyntaxError: 'import' and 'export' may only appear at the top level
  }

commonJS

导出

在 Node.js 模块系统中,每个文件都被视为独立的模块。模块导入导出实际是由nodejs的模块封装器实现,通过为module.exports分配新的值来实现导出具体内容。

module.exports有个简写变量exports,其实就是个引用复制。exports作用域只限于模块文件内部。 原理类似于:

// nodejs内部
exports = module.exports

console.log(exports, module.exports) // {}, {}
console.log(exports === module.exports) // true

注意:nodejs实际导出的是module.exports,以下几种经典case单独看一下:

case1:

// 使用exports
exports.a = xxx
console.log(exports === module.exports) // true

// 等价于
module.exports.a = xxx

case2:

// 这么写可以导出,最终导出的是{a:'1'}
module.exports = {a:'1'}

console.log(exports, module.exports) // {}, {a:'1'}
console.log(exports === module.exports) // false

// 不会将{a:'1'}导出,最终导出的是{}
exports = {a:'1'}

console.log(exports, module.exports) // {a:'1'}, {}
console.log(exports === module.exports) // false

引入

通过require语法引入:

// a是test.js里module.exports导出的部分
const a = require('./test.js')

原理伪代码:

function require(/* ... */) {
  const module = { exports: {} };
  ((module, exports) => {
    // Module code here. In this example, define a function.
    function someFunc() {}
    exports = someFunc;
    // At this point, exports is no longer a shortcut to module.exports, and
    // this module will still export an empty default object.
    module.exports = someFunc;
    // At this point, the module will now export someFunc, instead of the
    // default object.
  })(module, module.exports);
  return module.exports;
}

特点

值拷贝

// test.js
let test = {a:'1'}
setTimeout(()=>{
  test = {a:'2'}
},1000)
module.exports = test

// index.js
const test1 = require('./test.js')
console.log(test1) // {a:1}
setTimeout(()=>{
  console.log(test1) // {a:1}
},2000)

ES Module和 commonJS区别

语法:

exportsmodule.exportsrequire 是Node.js模块系统关键字。

exportexport defaultimport 则是ES6模块系统的关键字:

原理:

exportsmodule.exports导出的模块为值复制。

exportexport default为引用复制。

时机:

ES Module静态加载是编译时确定,ES Module动态加载是运行时确定。

CommonJS是运行时确定。

到此这篇关于JS中ESModule和commonjs介绍及使用区别的文章就介绍到这了,更多相关JS ESModule和commonjs 内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 详谈commonjs模块与es6模块的区别

    到目前为止,已经实习了3个月的时间了.最近在面试,在面试题里面有题目涉及到模块循环加载的知识.趁着这个机会,将commonjs模块与es6模块之间一些重要的的区别做个总结.语法上有什么区别就不具体说了,主要谈谈引用的区别. commonjs 对于基本数据类型,属于复制.即会被模块缓存.同时,在另一个模块可以对该模块输出的变量重新赋值. 对于复杂数据类型,属于浅拷贝.由于两个模块引用的对象指向同一个内存空间,因此对该模块的值做修改时会影响另一个模块. 当使用require命令加载某个模块时,就会运

  • webpack4.x CommonJS模块化浅析

    先看下webpack官方文档中对模块的描述: 在模块化编程中,开发者将程序分解成离散功能块(discrete chunks of functionality),并称之为模块. 每个模块具有比完整程序更小的接触面,使得校验.调试.测试轻而易举. 精心编写的模块提供了可靠的抽象和封装界限,使得应用程序中每个模块都具有条理清楚的设计和明确的目的. webpack 的核心概念之一就是一切皆模块,webpack 在项目中的作用就是,分析项目的结构,找到 JavaScript 模块以及其他一些浏览器不能直接

  • 浅谈Webpack是如何打包CommonJS的

    目录 一.书写代码 二.使用webpack打包编译 三.解析 CommonJS 是 Node 中的一种模块化规范,其是一种运行在 Node 环境下的代码,这种代码是不能直接运行到浏览器环境中的.但是在日常使用 webpack 的项目中不用做额外的处理,我们也能使用 CommonJS 来书写代码,那么 webpack 在这背后做了什么呢? 我们这里不看编译时,只看运行时 一.书写代码 使用yarn init -y命令初始化一个package.json文件. 接着yarn add webpack安装

  • ES6与CommonJS中的模块处理的区别

    ES6和CommonJS都有自己的一套处理模块化代码的措施,即JS文件之间的相互引用. 为了方便两种方式的测试,使用nodejs的环境进行测试 CommonJS的模块处理 使用require来引入其他模块的代码,使用module.exports来引出 // exportDemo.js count = 1; module.exports.count = count; module.exports.Hello = function() { var name; this.setName = funct

  • 利用webpack理解CommonJS和ES Modules的差异区别

    前言 问: CommonJS 和 ES Modules 中模块引入的区别? CommonJS 输出的是一个值的拷贝:ES Modules 生成一个引用,等到真的需要用到时,再到模块里面去取值,模块里面的变量,绑定其所在的模块. 我相信很多人已经把这个答案背得滚瓜烂熟,好,那继续提问. 问:CommonJS 输出的值是浅拷贝还是深拷贝? 问:你能模拟实现 ES Modules 的引用生成吗? 对于以上两个问题,我也是感到一脸懵逼,好在有 webpack 的帮助,作为一个打包工具,它让 ES Mod

  • 读懂CommonJS的模块加载

    叨叨一会CommonJS Common这个英文单词的意思,相信大家都认识,我记得有一个词组common knowledge是常识的意思,那么CommonJS是不是也是类似于常识性的,大家都理解的意思呢?很明显不是,这个常识一点都不常识.我最初认为commonJS是一个开源的JS库,就是那种非常方便用的库,里面都是一些常用的前端方法,然而我错得离谱,CommonJS不仅不是一个库,还是一个看不见摸不着的东西,他只是一个规范!就像校纪校规一样,用来规范JS编程,束缚住前端们.就和Promise一样是

  • 浅析AMD CMD CommonJS规范--javascript模块化加载学习心得总结

    这是一篇关于javascript模块化AMD,CMD,CommonJS的学习总结,作为记录也给同样对三种方式有疑问的童鞋们,有不对或者偏差之处,望各位大神指出,不胜感激. 本篇默认读者大概知道require,seajs的用法(AMD,CMD用法),所以没有加入使用语法. 1.为何而生: 这三个规范都是为javascript模块化加载而生的,都是在用到或者预计要用到某些模块时候加载该模块,使得大量的系统巨大的庞杂的代码得以很好的组织和管理.模块化使得我们在使用和管理代码的时候不那么混乱,而且也方便

  • JS中ESModule和commonjs介绍及使用区别

    目录 ES Module 导出 仅导出 重导出(re-exporting / aggregating) 嵌入式脚本 引入 语法 嵌入式脚本 特点 commonJS 导出 引入 特点 值拷贝 ES Module和 commonJS区别 ES Module 导出 仅导出 named exports: 命名导出,每次可以导出一个或者多个. default exports: 默认导出,每次只能存在一个. 以上两者可以混合导出: // 命名导出 export const b = 'b' // 默认导出 e

  • JS中type="button"和type="submit"的区别

    Submit是专门用于提交表单的Button,与Button的区别主要有两点: type=button 就单纯是按钮功能 type=submit 是发送表单 (1)Submit将表单提交(form.submit())作为其onclick后的默认事件,Button并非如此 (2)表单提交时,所有具有name属性的html输入元素(包括input标签.button标签.select标签等)都将作为键值对提交,除了Submit对象.Submit对象只有在自己被单击后的提交中才会作为键值对被提交. 但是

  • js中let和var定义变量的区别

    javascript 严格模式 第一次接触let关键字,有一个要非常非常要注意的概念就是"javascript 严格模式",比如下述的代码运行就会报错: let hello = 'hello world.'; console.log(hello); 错误信息如下: let hello = 'hello world.'; ^^^ SyntaxError: Block-scoped declarations (let, const, function, class) not yet sup

  • JS中type="button"和type="submit"的区别

    Submit是专门用于提交表单的Button,与Button的区别主要有两点: type=button 就单纯是按钮功能 type=submit 是发送表单 (1)Submit将表单提交(form.submit())作为其onclick后的默认事件,Button并非如此 (2)表单提交时,所有具有name属性的html输入元素(包括input标签.button标签.select标签等)都将作为键值对提交,除了Submit对象.Submit对象只有在自己被单击后的提交中才会作为键值对被提交. 但是

  • 详解JS中continue关键字和break关键字的区别

    目录 1.框架 2.简单介绍 3.代码演示 4.演示break 1.框架 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> </head> <body> <script> </script> </body> </html> 2.简单介绍 1.在javascr

  • 详谈js中数组(array)和对象(object)的区别

    •object 类型: ◦ 创建方式: /*new 操作符后面Object构造函数*/ var person = new Object(); person.name = "lpove"; person.age = 21; /*或者用对象字面量的方法*/ var person = { name: "lpove"; age : 21; } •array类型 ◦ 创建方式: `var colors = new Array("red","blu

  • Node.js中 __dirname 的使用介绍

    前言 本文主要给大家介绍的是关于Node.js中 __dirname 使用的相关内容,分享出来供大家参考学习,下面来一起看看详细的介绍: 方法如下 新建个文件 app.js 里面的内容如下: console.log(__dirname + '/example.db'); console.log('example.db'); 如果将app.js放在一个根目录下面 执行node app.js 分别输出如下内容: /Users/durban/nodejs/koa-mysql-orm-model/exa

  • js中prototype用法详细介绍

    prototype 是在 IE 4 及其以后版本引入的一个针对于某一类的对象的方法,而且特殊的地方便在于:它是一个给类的对象添加方法的方法!这一点可能听起来会有点乱,别急,下面我便通过实例对这一特殊的方法作已下讲解: 首先,我们要先了解一下类的概念,JavaScript 本身是一种面向对象的语言,它所涉及的元素根据其属性的不同都依附于某一个特定的类.我们所常见的类包括:数组变量(Array).逻辑变量(Boolean).日期变量(Date).结构变量(Function).数值变量(Number)

  • js中递归函数的使用介绍

    下面我们就做一个10以内的阶乘试试看吧: js中递归函数的使用 function f(num){ if(num alert("10!的结果为:"+f(10)); [Ctrl+A 全选 注:如需引入外部Js需刷新才能执行] 递归函数的调用就说这么多了 js递归函数调用自身时的保险方式. 来自js高级程序设计 一个典型阶乘递归函数: 复制代码 代码如下: function fact(num){ if (num<=1){ return 1; }else{ return num*fact

  • node.js中fs.stat与fs.fstat的区别详解

    前言 fs.stat和fs.fstat他们的方法功能是一样的,都是获取文件的状态信息,本文主要介绍的是关于node.js中fs.stat与fs.fstat区别的相关内容,分享出来供大家参考学习,下面来看看详细的介绍: fs.stat用法: fs.stat('./aa.js', function(err, stats) { if (err) { throw err; } console.log(stats); }); fs.fstat用法: fs.open('./aa.js', 'a', func

随机推荐