资讯专栏INFORMATION COLUMN

ES6之路之模块详解

huashiou / 3119人阅读

摘要:例如我们导入模块,可以这么导入桃翁欢迎关注公众号前端桃园报错不能定义相同名字变量报错,不能重新赋值小猪可以看到导入绑定这里不理解绑定,文章后面会解释时,形式类似于对象解构,但实际上并无关联。

欢迎访问个人站点
简介 何为模块

一个模块只不过是一个写在文件中的 JavaScript 代码块。

模块中的函数或变量不可用,除非模块文件导出它们。

简单地说,这些模块可以帮助你在你的模块中编写代码,并且只公开应该被你的代码的其他部分访问的代码部分。

为什么要使用模块

增加可维护性:由于每个模块都是独立的,每个人写的代码是不会相互影响的,在维护代码的时候很好排查是哪个模块出错。

可复用性:在日常的开发中,特别是大点的项目,代码的可复用性就更重要了,也许你会用复制粘贴的形式,但是直接一个 import 命令就可以搞定,岂不快哉。

避免命名污染:在 javascript 脚本中,所有的 js 文件的顶级作用域创建的变量,会被添加到共享的全局作用域,这就会导致不同的人开发的代码可能会有相同的变量名,导致变量名污染。

如何使用 导出模块

导出模块所用的命令是 export。

前面也提到一个模块就是一个 javascript 文件,在这个模块中定义的变量,外部是无法获取到的,只有通过 export 导出的变量其他模块才可以用

最简单的导出方式就是在声明的变量、函数、类前面加一个 export

// export1.js 

// 导出变量
export let name = "桃翁";

// 导出函数
export function print() {
    console.log("欢迎关注公众号:前端桃园");
}

// 导出类
export class Person {
    constructor(name) {
        this.name = name;
    }
}
// 私有函数
function privateFunction () {
    console.log("我是私有函数,外部访问不了我");
}
注意
1. 被导出的函数或者类,都必须要有名称,意思就是说不能用这种方式导出匿名函数或者匿名类。
2. privateFunction 函数,没有加 export 命令,被当做这个模块的私有变量,其他模块是访问不到的。

除了上面那种导出方式,还有另外一种

// export2.js

// 导出变量
let name = "桃翁";

// 导出函数
function print() {
    return "欢迎关注公众号:前端桃园";
}

// 导出类
class Person {
    constructor(name) {
        this.name = name;
    }
}

// 私有函数
function privateFunction () {
    return "我是私有函数,外部访问不了我";
}

export { name, print, Person }

上面这种写法导入一组变量,与 export1.js 是等价的。

导入模块

导入的模块可以理解为是生产者(或者服务的提供者),而使用导入的模块的模块就是消费者。

导入模块的命令是 import, import 的基本形式如下:

import { var1, var2 } from "./example.js"

import 语句包含两部分:一是导入需要的标识符,二是模块的来源。

注意:浏览器中模块来源要以「/」或者 「./」 或者 「../」开头 或者 url 形式,不然会报错。

例如我们导入 export1.js 模块,可以这么导入

// import1.js
import { name, print, Person } from "./export1.js";

console.log(name); // 桃翁

console.log(print()); // 欢迎关注公众号:前端桃园

// 报错, 不能定义相同名字变量
let name = 2333; 

// 报错,不能重新赋值
name = "小猪";

可以看到导入绑定(这里不理解绑定,文章后面会解释)时,形式类似于对象解构,但实际上并无关联。

当导入绑定的时候,绑定类似于使用了 const 定义,意味着不能定义相同的变量名,但是没有暂时性死区特性(但是在 深入理解ES6 这本书里面说是有暂时性死区限制,我在 chrome 上测试了的,读者希望也去试下,到底受不受限制)。

let name = 2333;

上面这行代码会报错。

命名空间导入

这种导入方式是把整个生产者模块当做单一对象导入,所有的导出被当做对象的属性。

// import2.js
import * as namespace from "./export1.js"

console.log(namespace.name); // 桃翁

console.log(namespace.print()); // 欢迎关注公众号:前端桃园
重命名导入导出

有时候你并不想导出变量的原名称,需要重新命名,这个时候只需要使用 as 关键字来制定新的名字即可。

重命名导出
// export3.js

function print() {
    return "欢迎关注公众号:前端桃园";
}

export { print as advertising }
导重命名入

拿上面导出的举例子

// import3.js
import { advertising as print } from "./export3.js"

console.log(typeof advertising); // "undefined"

console.log(print()); // 欢迎关注公众号:前端桃园 

此代码导入 advertising 函数并重命名为了 print ,这意味着此模块中 advertising 标识符不存在了。

default 关键字

default 关键字是用来做默认导入导出的。

默认导出
// defaultExport.js

// 第一种默认导出方式
export default function print() {
    return "欢迎关注公众号:前端桃园";
}

// 第二种默认导出方式
function print() {
    return "欢迎关注公众号:前端桃园";
}

export default print;

// 第三种默认导出方式
function print() {
    return "欢迎关注公众号:前端桃园";
}

export { print as default }

default 这个关键字在 JS 中具有特殊含义,既可以作为同命名导出,又标明了模块需要使用默认值。

注意: 一个模块中只能有一个默认导出。
默认导入

默认导入和一般的导入不同之处就是不需要写大括号了,看起来更简洁。

把上面 defaultExport.js 模块导出的作为例子

import print from "./defaultExport.js"

console.log(print()); // 欢迎关注公众号:前端桃园 

那如果既有默认的又有非默认的怎么导入呢?看例子就明白了

// defaultImport1.js

let name = "桃翁";

function print() {
    return "欢迎关注公众号:前端桃园";
}

export { name, print as default }
// defaultImport2.js

import print, { name } from "./defaultImport1.js"

console.log(print()); // 欢迎关注公众号:前端桃园

console.log(name); // 桃翁

混合导入需要把默认导入的名称放在最前面,然后用逗号和后面非默认导出的分割开。

思考了很久是否应该加上进阶内容,本来是想写入门级系列的,但是想了想,还是都写进来吧,入门的看入门前面基础,深入理解的看进阶。
进阶

进阶部分主要介绍 模块的几个特性

静态执行

动态关联

模块不会重复执行

静态执行

所谓静态执行其实就是在编译阶段就需要确定模块的依赖关系,那么就会出现 import 命令会优先于模块其他内容的执行,会提前到编译阶段执行。

// static1.js
console.log("佩奇");

import { nouse } from "./static2.js"

// static2.js
export function nouse() {
    return "我是不需要的";
}

console.log("小猪");

可以看到最后输出的应该是「小猪」先输出,而「佩奇」后输出,可以得出虽然 static2.js 在后面引入,但是会被提升到模块的最前面先执行。

这也是我前面所说的不受暂时性死区原因之一,在这里可以写一个例子试试:

// static3.js
console.log(nouse());

import { nouse } from "./static2.js"

// 结果:
// 小猪
// 我是不需要的

经检验确实是可以在 import 之前使用导入的绑定。

静态执行还会导致一个问题,那就是不能动态导入模块。

// 报错
if (flag) {
    import { nouse } from "./static3.js"
}

// 报错
import { "no" + "use" } from "./static3.js"

因为 import 是静态执行的,所以在静态(词法)分析阶段,是没法得到表达式或者变量的值的。

但是为了解决这个问题,因为了 import() 这个函数,这个算扩展内容吧,写太多了我怕没人看完了,后面会有扩展阅读链接。

动态关联

所谓的动态关联,其实就是一种绑定关系, 这是 ES6 非常重要的特性,一定仔细阅读。

在 ES6 的模块中,输出的不是对象的拷贝,不管是引用类型还是基本类型, 都是动态关联模块中的值,。

// dynamic1.js
export let name = "桃翁";

export function setName(name) {
    name = name;
}

// dynamic2.js
import { name, setName } from "./dynamic1.js"

console.log(name); // 桃翁

setName("不要脸");

console.log(name); // 不要脸

奇迹般的发现在 dynamic2.js 模块中可以修改 dynamic1.js 模块里面的值, 并且反应到 name 绑定上(这个是重点,这个反应到了消费者模块), 所以我们把导入的变量叫做绑定。

在生产者模块导出的变量与消费者模块导入的变量会有一个绑定关系,无论前者或者后者发生改变,都会互相影响。

注意区分在一个文件或模块中基本类型的赋值,两者是互不影响的。
模块不会重复执行

这个特性比较好理解,就是如果从一个生产者模块中分别导入绑定,而不是一次性导入,生产者模块不会执行多次。

// noRepeat1.js
export let name = "桃翁";

export let age = "22";

console.log("我正在执行。。。");

// noRepeat2.js
import { name } from "./noRepeat1.js";
import { age } from "./noRepeat1.js";

console.log(name);
console.log(age);

// 结果
// 我正在执行。。。
// 桃翁
// 22

虽然导入了两次,但是 noRepeat1.js 只有执行一次。若同一个应用(注意是同一个应用不是模块)中导入同一个模块,则那些模块都会使用一个模块实例,意思就是说是一个单例。

后记

码字不易,写技术文章是真的累,作者花的时间至少是读者读的时间的十倍。在此想到阮老师写了那么多文章,不知道是花了多少时间,竟然还有人这么恨他,攻击他的网站。

我在文章中给我公众号打了很多广告,在此抱个歉,刚运营的公众号,需要拉点粉丝,不喜欢的注重内容就好。

拓展

原生ECMAScript模块: 动态 import()

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

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

相关文章

  • ES6模块详解

    摘要:例如我们导入模块,可以这么导入桃翁欢迎关注公众号前端桃园报错不能定义相同名字变量报错,不能重新赋值小猪可以看到导入绑定这里不理解绑定,文章后面会解释时,形式类似于对象解构,但实际上并无关联。 欢迎访问个人站点 简介 何为模块 一个模块只不过是一个写在文件中的 JavaScript 代码块。 模块中的函数或变量不可用,除非模块文件导出它们。 简单地说,这些模块可以帮助你在你的模块中编写...

    newtrek 评论0 收藏0
  • Vue ES6 Jade Scss Webpack Gulp

    摘要:主有前端后端,并加,各一名。本着工欲善其事,必先利其器的理念,一直以来在工作效率这块,略怀执念一个问题不应该被解决两次。下图为开发项目机制所涉及到的插件工欲善其事,必先利其器,语言,框架皆可以归结为器而不当仅局限于开发工具以及机。 原文链接: http://www.jeffjade.com/2016/05/08/106-vue-es6-jade-scss-webpack-gulp/ 一...

    rickchen 评论0 收藏0
  • 2017-08-15 前端日报

    摘要:前端日报精选变量声明与赋值值传递浅拷贝与深拷贝详解浅谈自适应学习比你想象的要简单常见排序算法之实现世界万物诞生记中文深入理解笔记与异步编程译不可变和中的知乎专栏译怎样避免开发时的深坑疯狂的技术宅在翻译网格布局掘金详解改变模糊度亮 2017-08-15 前端日报 精选 ES6 变量声明与赋值:值传递、浅拷贝与深拷贝详解浅谈web自适应学习 React.js 比你想象的要简单常见排序算法之...

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

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

    Karrdy 评论0 收藏0
  • 2017-08-30 前端日报

    摘要:前端日报精选精读个最佳特性翻译轻量级函数式编程第章组合函数之组件类型写的姿势中文周二放送面试题详解知乎专栏译原生值得学习吗答案是肯定的掘金个超赞的视觉效果众成翻译布局时常见总结腾讯前端团队社区归档打地鼠入门学习书籍 2017-08-30 前端日报 精选 精读《Web fonts: when you need them, when you don’t》10个最佳ES6特性翻译 -《Jav...

    weizx 评论0 收藏0

发表评论

0条评论

huashiou

|高级讲师

TA的文章

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