资讯专栏INFORMATION COLUMN

ES6指北【3】——5000字长文带你彻底搞懂ES6模块

ygyooo / 386人阅读

摘要:模块什么是模块什么是模块化玩过游戏的朋友应该知道,一把装配完整的步枪,一般是枪身消音器倍镜握把枪托。更重要的是,其它大部分语言都支持模块化。这一点与规范完全不同。模块输出的是值的缓存,不存在动态更新。

1.模块 1.1 什么是模块?什么是模块化?

玩过FPS游戏的朋友应该知道,一把装配完整的M4步枪,一般是枪身+消音器+倍镜+握把+枪托

如果把M4步枪看成是一个页面的话,那么我们可以做如下类比
枪身 ->


消音器 ->

倍镜 ->
握把 ->
枪托 ->

OK,你刚才做了一件事情,就是把m4步枪拆成了五个部分,你拆分的每一个部分就是一个模块【module】,你拆分的这个过程就是模块化【modularization】

模块化是一种编程思想,其核心就是拆分任务,把复杂问题简单化,这样一来既方便多人分工协作,又可以帮助我们迅速定位问题

方便多人分工协作 —— 可以不同的人开发不同的模块,再组合,大大增加团队效率

帮助我们迅速定位问题 —— 后坐力太大,那八成是枪托或握把的问题;声音过大,那八成是消音器的问题。

1.2 模块化的血泪史

下面用一个小栗子讲一讲模块化的发展史

龚先生和棚先生一起接了一个项目,他们俩需要分别实现一些功能,很简单,就是Console出来自己的变量a

于是他们俩一合计,安排龚先生的代码多带带放在script1.js里写,棚先生的代码多带带放在script2.js里写,然后用script标签分别引入

// script1.js文件

var a = 1
console.log(a)
// script2.js文件

var a = 2
console.log(a)



很快他们遇到了第一个问题 —— 变量命名冲突
尤其是包含了异步的时候,会出现如下情况

// script1.js文件

var a = 1
setTimeout(()=>{
  console.log(a)  // 我们想console出来1,却console出了2
},1000)
// script2.js文件

var a = 2
console.log(a)

上面的问题明显是由于a是一个全局变量导致的,所以解决思路也很明确——造一个局部变量呗

局部变量
ES5时代使用立即执行函数制造局部变量
// script1.js文件
!function(){
    var a = 1
    setTimeout(()=>{
      console.log(a)  // 这下是2了
    },1000)
}()
// 下面有5000行代码
// script2.js文件

console.log(2)
ES6时代直接使用块级作用域+let
// script1.js文件
{
    let a = 1
    setTimeout(()=>{
      console.log(a)  // 这下是2了
    },1000)
}
// script2.js文件
{
    let a = 2
    console.log(a)
}
通过window连接各个模块

后来公司招了一个前端大佬,说现在只能由他来控制什么时候console变量,于是他新建了一个control.js文件
通过window对象连接script1.js和scirpt2.js

// script1.js文件
{
    let a = 1
    window.module1 = function() {
        console.log(a)
    }
}
// script2.js文件
{
    let a = 2
    window.module2 = function() {
        console.log(a)
    }
}
// control.js文件
setTimeout(()=>{
    window.module1()
},1000)

window.module2()
这个时候,非常重要的一点就是window是一个全局变量并且充当了一个公用仓库,这个仓库有两个关键作用,存【导出】取【依赖】
// script1.js文件
{
    let a = 1
    // 把这个函数存放进window,就是导出到window
    window.module1 = function() {
        console.log(a)
    }
}
// control.js文件
setTimeout(()=>{
    // 我们从window里取出module1函数进行调用,就是依赖了script1.js文件
    window.module1()
},1000)

window.module2()
依赖加载的顺序

烦人的产品对需求又进行了更改,给了一个name.js文件

// name.js文件
window.names = ["gongxiansheng","pengxiansheng"]

要求现在龚先生和棚先生需要Console出自己的名字
这还不简单?几秒钟写好

// script1.js文件
{
    window.module1 = function() {
        console.log(window.names[0])
    }
}
// script2.js文件
{
    window.module2 = function() {
        console.log(window.names[1])
    }
}
// control.js文件
setTimeout(()=>{
    window.module1()
},1000)

window.module2()





但很快他们发现,console出来的都是undefined
前端大佬一眼看出了问题,对他们俩说
你们依赖的代码一定要在你们自己的代码前引入,不然是取不到值的;你看我的control.js是不是在你们俩的代码后面引入的,因为我用到了你们俩的代码了呀
噢噢,原来是js文件加载顺序问题,改一下吧






但是在人多了以后,我们到时候会搞不清楚到底谁依赖了谁,保险起见只能全部都加载,性能浪费了太多,前端大佬摇头叹息道

2. ES6的模块 2.1 ES6之前模块化的痛点

变量冲突

要用window连接各个模块

依赖需要全部加载

还要TMD注意加载顺序

模块化是ES6的最大的亮点之一,因为在ES6之前的语法里从未有过模块化的体系这对开发大型的、复杂的项目形成了巨大障碍。因为我们无法对项目进行拆分,无法更好地进行多人协作开发。更重要的是,其它大部分语言都支持模块化

既然语言不支持,那么如何将模块化引入JS呢?

前端社区就自己制定了一些模块加载方案——这也是CommonJS【服务器】和AMD、CMD【浏览器】的由来。

但是现在ES6引入了模块化的功能,实现起来非常简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。
2.2 import和export的用法
import和export语法较为简单,大家去MDN可以看非常详细的讲解,笔者在这里知识用注释简单介绍一下
export语法
// 命名导出
export { name1, name2, …, nameN };
export { variable1 as name1, variable2 as name2, …, nameN };
export let name1, name2, …, nameN; // also var
export let name1 = …, name2 = …, …, nameN; // also var, const
export function FunctionName() {...}
export class ClassName {...}

// 默认导出
export default expression;
export default function (…) { … } // also class, function*
export default function name1(…) { … } // also class, function*
export { name1 as default, … };

// 将其它模块内的导出作为当前文件的导出
export * from …;
export { name1, name2, …, nameN } from …;
export { import1 as name1, import2 as name2, …, nameN } from …;
import用法
import defaultExport from "module-name"; // 导入默认默认变量
import * as name from "module-name"; // 将模块内所有变量导出,并挂载到name下【name是一个module对象】。什么要有as——为了防止export出来的变量命名冲突
import { export } from "module-name"; // 导入某一个变量
import { export as alias } from "module-name"; // 导入某一个变量并重命名
import { export1 , export2 } from "module-name"; // 导入两个变量
import { export1 , export2 as alias2 , [...] } from "module-name"; // 导入多个变量,同时可以给导入的变量重命名
import defaultExport, { export [ , [...] ] } from "module-name"; // 导入默认变量和多个其它变量
import defaultExport, * as name from "module-name"; // 导入默认变量并重新命名
import "module-name"; // 导入并加载该文件【注意文件内的变量必须要通过export才能被使用】
var promise = import(module-name); // 异步的导入
使用import和export改写第一节的代码
// name.js文件
let names = ["gongxiansheng","pengxiansheng"]
export default names
// script1.js
import names from "./name.js"

let module1 = function () {
  console.log(names[0])
}
export default module1
// script2.js
import names from "./name.js"

let module2 = function() {
  console.log(names[1])
}
export default module2
// control.js
import module1 from "./script1.js"
import module2 from "./script2.js"

setTimeout(() => {
  module1()
}, 1000)
module2()



2.3 拓展:import和export的一些运行原理 2.3.1 ES6 模块输出的是值的引用,输出接口会动态绑定
其实就是按照数据类型里的引用类型的概念去理解。
这一点与 CommonJS 规范完全不同。
CommonJS 模块输出的是值的缓存,不存在动态更新。
// module1.js
export var foo = "bar";
setTimeout(() => foo = "baz", 500);
// module2.js
import {foo} from "./module1.js"
console.log(foo)
setTimeout(() => console.log(foo), 1000);

// console的结果
// bar
// baz
2.3.2 export可以出现在模块内的任何位置,但不能处于块级作用域内
// 报错
{
  export let foo = "bar";
}
2.3.3 import具有提升效果(类似于var),会提升到整个模块的头部,首先执行
console.log(foo)
import {foo} from "./script1.js"
参考资料:ECMAScript 6 入门
本文纯属原创,为了方便大家理解,小故事,小栗子都是笔者自己想的。如果您觉得对你有帮助,麻烦给个赞,给作者灰暗的生活挥洒挥洒积极向上的正能量,谢谢啦^_^。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/96770.html

相关文章

  • 深入理解Node.js 进程与线程(8000长文彻底搞懂)

    摘要:在单核系统之上我们采用单进程单线程的模式来开发。由进程来管理所有的子进程,主进程不负责具体的任务处理,主要工作是负责调度和管理。模块与模块总结无论是模块还是模块,为了解决实例单线程运行,无法利用多核的问题而出现的。 前言 进程与线程是一个程序员的必知概念,面试经常被问及,但是一些文章内容只是讲讲理论知识,可能一些小伙伴并没有真的理解,在实际开发中应用也比较少。本篇文章除了介绍概念,通过...

    Harpsichord1207 评论0 收藏0
  • JavasScript重难点知识

    摘要:忍者级别的函数操作对于什么是匿名函数,这里就不做过多介绍了。我们需要知道的是,对于而言,匿名函数是一个很重要且具有逻辑性的特性。通常,匿名函数的使用情况是创建一个供以后使用的函数。 JS 中的递归 递归, 递归基础, 斐波那契数列, 使用递归方式深拷贝, 自定义事件添加 这一次,彻底弄懂 JavaScript 执行机制 本文的目的就是要保证你彻底弄懂javascript的执行机制,如果...

    forsigner 评论0 收藏0
  • javascript知识点

    摘要:模块化是随着前端技术的发展,前端代码爆炸式增长后,工程化所采取的必然措施。目前模块化的思想分为和。特别指出,事件不等同于异步,回调也不等同于异步。将会讨论安全的类型检测惰性载入函数冻结对象定时器等话题。 Vue.js 前后端同构方案之准备篇——代码优化 目前 Vue.js 的火爆不亚于当初的 React,本人对写代码有洁癖,代码也是艺术。此篇是准备篇,工欲善其事,必先利其器。我们先在代...

    Karrdy 评论0 收藏0
  • 彻底搞懂 JS 中 this 机制

    摘要:的四种绑定规则的种绑定规则分别是默认绑定隐式绑定显示绑定绑定。绑定中的操作符,和其他语言中如的机制是不一样的。规则例外在显示绑定中,对于和的绑定将不会生效。它也是作为机制的一种替换,解决之前绑定过程各种规则带来的复杂性。 彻底搞懂 JS 中 this 机制 摘要:本文属于原创,欢迎转载,转载请保留出处:https://github.com/jasonGeng88/blog 目录 t...

    李世赞 评论0 收藏0
  • 一篇文章彻底搞懂JS深浅拷贝和const

    摘要:图数据类型图引用类型深浅拷贝问题不知道什么是深拷贝和浅拷贝的请先去并在调试台自己操作一下,这篇文章只会说明为何中会有这种问题。所以有的时候我们为了避免浅拷贝,会用一些方式实现深拷贝。 首先要了解的js基础 基本数据类型:Object、undefined、null、Boolean、Number、String、Symbol (ES6新加) Object包括: Array 、Date 、R...

    MyFaith 评论0 收藏0

发表评论

0条评论

ygyooo

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<