理解前端模块概念:CommonJs与ES6Module
前言
JavaScript里的模块
- 需要手动维护JavaScript的加载顺序。因为通常script之间会有很多依赖关系,但这种关系都是隐式的,除非一个个去查看注释(如果没有注释,那就...),否则很难指明谁依赖谁。
- 命名冲突。所有script文件所定义的所有内容都由全局作用域共享。一个人开发还好,碰上多人协作开发,那就是灾害。
- 如果script数量太多,这也会影响页面加载。因为script标签都需要向服务器请求资源,过多的请求会严重降低渲染的速度。
何为模块
- 模块自动运行在严格模式下。
- 在模块的顶层作用域创建的变量,不会被自动添加在共享的全局作用域中,它们之后在各自的模块顶层作用域下生存。
- 通过导入导出的语句,可以非常清晰的指明模块的依赖关系。
模块的发展
CommonJs
- require函数,用于导入;
- module.exports变量,用于导出;
模块
// foo.js
var name = 'foo'; // bar.js
var name = 'bar';
require('./foo.js');
console.log(name); // bar
导出
module.exports = {
name: 'foo'
}
var module = {};
// ...
module.exports = {};
exports.name = 'foo'
var module = {
exports: {}
}
var exports = module.exports;
exports = {
name: 'foo'
}
导入
// foo.js
module.exports = {
sayname: function () {
console.log('foo');
}
}; // bar.js
var sayname = require('./foo.js').sayname;
sayname(); // foo
- 模块是第一次被require加载。这时会首先执行该模块,然后导出内容。
- 模块是曾经被require加载过。这时会直接导出执行得到的结果。
// foo.js
console.log('running foo.js')
exports.name = 'foo'; // bar.js
var firstname = require('./foo').name;
console.log('firstname:', firstname); var lastname = require('./foo').name;
console.log('lastname:', lastname);
running foo.js
fistname:foo lastname:foo
var path = ['foo.js', 'bar.js'];
path.forEach(name => {
require('./' + name);
})
ES6Module
模块
// foo.js
export default {
sayname: function () {
console.log('foo');
}
}; // bar.js
import foo from './foo.js'
foo.sayname(); // foo
- 命名导出
- 默认导出
// 1
export const name = 'foo'; // 2
const name = 'foo';
export { name }
const name = 'foo';
export { name as nickname }
export default {
name: 'foo'
}
导入
// foo.js
export const name = 'foo';
// bar.js
import { name } from './foo'
console.log(name)
// foo.js
export const name = 'foo';
// bar.js
import { name as nickname } from './foo'
console.log(nickname) // 也可以通过整体导入的方法
import * as name from './foo'
console.log(name.name)
// foo.js
export default {
name: 'foo'
} // bar
import name from './foo'
console.log(name.name)
CommonJs与ES6Module的区别
对模块依赖的处理区别
- 动态:模块依赖关系的建立是发生在代码运行阶段;
- 静态:模块依赖关系的建立是发生在代码编译阶段;
导入模块值的区别
// foo
var count = 0;
module.exports = {
count: count,
add: function (a, b) {
count++;
return a+b;
}
} // bar
var count = require('./foo').count;
var add = require('./foo').add; console.log(count); // 0
add(2,3);
console.log(count); // 0 count += 1;
console.log(count); // 1(拷贝值可以更改)
// foo
let count = 0;
const add = function (a,b) {
count++;
return a+b;
}
export { count, add } // bar
import { count, add } from './foo';
console.log(count); // 0
add(2,3);
console.log(count); // 1(实时反映foo中count的值) count++; // 报错 count is read-only
循环依赖的区别
// foo
const bar = require('./bar')
console.log('来自bar:', bar);
module.exports = 'foo'; // bar
const foo = require('./foo');
console.log('来自foo:', foo);
module.exports = 'bar; // index
require('./foo')
来自foo:()
来自bar:bar
- index文件中引入了foo,此时开始执行foo中的代码;
- foo第一句导入了bar,这是foo不会继续向下执行,而是进入了bar的内部。
- 在bar中又引入了foo,这里产生了循环依赖。但并不会再次执行foo,而是直接导出返回值,也就是module.exports。但由于foo未执行完,导出值是默认的空对象,因此当bar执行到console.log时,打印出来的是空对象。
- bar执行完毕,foo继续向下执行直到流程结束。
// foo
import bar from './bar';
console.log('来自bar:', bar);
export default 'foo' // bar
import foo from './foo'
console.log('来自foo:', foo);
export default 'bar' // index
import foo from './foo'
来自foo: undefined
来自bar:bar
结尾
作者:zhangwinwin来源:github
理解前端模块概念:CommonJs与ES6Module的更多相关文章
- 转:深入理解JavaScript闭包概念
闭包向来给包括JavaScript程序员在内的程序员以神秘,高深的感觉,事实上,闭包的概念在函数式编程语言中算不上是难以理解的知识.如果对作用域,函数为独立的对象这样的基本概念理解较好的话,理解闭包的 ...
- 前端模块与CMS结合
前端模块与CMS结合 在<FIS官方技术群>经常看到一些讨论,这次是 前端组件化与CMS的相关讨论,主要观点来自群里 漂流瓶(张云龙前辈). CMS是运营人员直接操作,我们往往需求各种各样 ...
- webpack前言:前端模块系统的演进
前端开发和其他开发工作的主要区别,首先是前端是基于多语言.多层次的编码和组织工作,其次前端产品的交付是基于浏览器,这些资源是通过增量加载的方式运行到浏览器端,如何在开发环境组织好这些碎片化的代码和资源 ...
- 前端模块化(CommonJs,AMD和CMD)
前端模块规范有三种:CommonJs,AMD和CMD. CommonJs用在服务器端,AMD和CMD用在浏览器环境 AMD 是 RequireJS 在推广过程中对模块定义的规范化产出. CMD 是 S ...
- 后端技术杂谈11:十分钟理解Kubernetes核心概念
本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 本文转自 https://github.com/h2pl/Java-Tutorial 喜欢的 ...
- 理解 Keystone 核心概念 - 每天5分钟玩转 OpenStack(18)
作为 OpenStack 的基础支持服务,Keystone 做下面这几件事情: 管理用户及其权限 维护 OpenStack Services 的 Endpoint Authentication(认证) ...
- (转)深入理解JavaScript 模块模式
深入理解JavaScript 模块模式 (原文)http://www.cnblogs.com/starweb/archive/2013/02/17/2914023.html 英文:http://www ...
- 深入理解JavaScript 模块模式
http://www.adequatelygood.com/JavaScript-Module-Pattern-In-Depth.html 模块模式是JavaScript一种常用的编码模式.这是一般的 ...
- Java IO 理解流的概念
Java IO 理解流的概念 @author ixenos 在理解流时首先理解以下概念 1.流的来源和去向一般在构造器指出 2.方法中的形参一般是将流输出到某个位置,读取(INPUT)流从流读出数据( ...
随机推荐
- 浅入kubernetes(1):Kubernetes 入门基础
目录 Kubernetes 入门基础 Introduction basic of kubernetes What Is Kubernetes? Components of Kubernetes Kub ...
- SpringBoot默认首页配置
@Configuration public class DefaultView extends WebMvcConfigurerAdapter { @Override public void addV ...
- C语言实现的多线程定时器
目录 1. 大致功能介绍 2. API库介绍 3. 一个例子 4. 库文件源码 注意事项 1. 大致功能介绍 实现任务列表,定时器会间隔一段时间遍历列表发现要执行的任务 任务列表中的所有任务并行执行 ...
- webservcie学习之webservice是什么
之前写代码,只是用到的时候才去看相关技术,用过后也没有再回头特别 去看,现在突然发现对一些技术的了解不够深刻,故现在准备再从头对用到的技术深入的学习下.就从webservice开始.首先对我不解的地方 ...
- noip2016自测报告
随便测了一下2016的提高组,发现自己还是太菜了... day1 T1:玩具谜题 一个模拟题,打挂了,只有95分,错的很傻逼 code: #include<bits/stdc++.h> u ...
- 浅谈.NET技术公司的实习生培养
浅谈.NET技术公司的实习生培养 背景 近几年.NET开发者市场的越发不景气,一毕业就选择.NET技术的开发者更是少之又少.一方面是公司效益的日益提高,一方面却是招聘优秀人才的速度总是赶不上公司发展的 ...
- Socket粘包问题终极解决方案—Netty版(2W字)!
上一篇我们讲了<Socket粘包问题的3种解决方案>,但没想到评论区竟然炸了.介于大家的热情讨论,以及不同的反馈意见,本文就来做一个扩展和延伸,试图找到问题的最优解,以及消息通讯的最优解决 ...
- 【详细】Python基础(一)
@ 目录 前言 1. Python环境的搭建 1.1 python解释器的安装 1.2 pycharm的安装 2. Python基础语法 2.1 基本语法 2.2 数据类型 2.3 标识符与关键字 2 ...
- 【JavaWeb】现代 JavaScript 教程
js_model_tutorial !!待更新 前言 现代 JavaScript 教程的学习笔记,它是一份不错的学习资源,感谢开源. 中文链接 基础 函数 代码示例 函数的声明方式 function ...
- LeetCode590. N叉树的后序遍历
题目 1 class Solution { 2 public: 3 vector<int>ans; 4 vector<int> postorder(Node* root) { ...