你真的懂 export default 吗?
export default A和export { A as default }乍一看是一样的,但是里面有一些细微的区别比较容易留坑。本文介绍两种写法的不同之处。
import 语句导入的是引用,不是值
有导出就必然有导入,我们先明确下 import 语句的工作原理。
import { A } from './module.js';
显而易见,在上面的代码中,A 和 ./module.js 中的 A 是相同的。再看这段代码:
const module = await import('./module.js');
const { A: destructuredA } = await import('./module.js');
在这段代码中,module.A 与 A 是相同的,但是因为 destructuredA 是结构赋值,因此就有一些不同了。
我们来看下 ./module.js:
// module.js
export let A = 'initial';
setTimeout(() => {
A = 'changed';
}, 500);
导入 ./module.js 的代码为 ./main.js:
// main.js
import { A as importedA } from './module.js';
const module = await import('./module.js');
let { A } = await import('./module.js');
setTimeout(() => {
console.log(importedA); // "changed"
console.log(module.A); // "changed"
console.log(A); // "initial"
}, 1000);
import 语句导入的是引用,也就是说,当 ./module.js 中 A 的值发生变化的时候,./main.js 中也会跟着变化。解构赋值获得的 A 不会变化是因为解构过程中是使用的值赋值给了新变量,而不是引用。
值得注意的是,静态语句 import { A } ... 虽然看着像解构赋值,实际上与解构赋值并不相同。
小结一下:
// 以下代码获得是引用
import { A } from './module.js';
import { A as otherName } from './module.js';
import * as module from './module.js';
const module = await import('./module.js');
// 以下代码获得的是值
let { A } = await import('./module.js');
export default
我们修改下 ./module.js:
// module.js
let A = 'initial';
export { A };
export default A;
setTimeout(() => {
A = 'changed';
}, 500);
同时也修改 ./main.js:
// main.js
import { A, default as defaultA } from './module.js';
import anotherDefaultA from './module.js';
setTimeout(() => {
console.log(A); // "changed"
console.log(defaultA); // "initial"
console.log(anotherDefaultA); // "initial"
}, 1000);
输出结果是 "initial",为什么呢?
我们知道,我们可以直接 export default 'hello'; 但是却不能 export { 'hello' as A }。规范在这两种语法上有一点不同。export default 后面的将会被作为表达式对待。因此我们可以 export default 'hello';, 甚至可以 export default 1 + 2;。因此,在 export default A 中,A 是作为表达式语句使用的,因此使用的是 A 的值。因此,当 A 的值在 setTimeout 中被改变的时候,export default 出去的值并没有变化。
小结一下:
// 引用
import { A } from './module.js';
import { A as otherName } from './module.js';
import * as module from './module.js';
const module = await import('./module.js');
// 值
let { A } = await import('./module.js');
// 导出引用
export { A };
export { A as otherName };
// 导出值
export default A;
export default 'hello!';
export { A as default }
export {} 导出的始终是一个引用,因此:
// module.js
let A = 'initial';
export { A, A as default };
setTimeout(() => {
A = 'changed';
}, 500);
同样,在先前的 ./main.js 中:
// main.js
import { A, default as defaultA } from './module.js';
import anotherDefaultA from './module.js';
setTimeout(() => {
console.log(A); // "changed"
console.log(defaultA); // "changed"
console.log(anotherDefaultA); // "changed"
}, 1000);
小结下:
// 导入引用
import { A } from './module.js';
import { A as otherName } from './module.js';
import * as module from './module.js';
const module = await import('./module.js');
// 导入值
let { A } = await import('./module.js');
// 导出引用
export { A };
export { A as otherName };
export { A as default };
// 导出值
export default A;
export default 'hello!';
export default function
虽然,前面说过 export default 后面的会被作为表达式使用。但是也有一些例外:
// module.js
export default function A() {}
setTimeout(() => {
A = 'changed';
}, 500);
// main.js
import A from './module.js';
setTimeout(() => {
console.log(A); // "changed"
}, 1000);
输出 "changed",因为 export default function 有其特殊的语法,在这个语法中,函数是作为引用传递的。
我们稍微做一下修改:
// module.js
function A() {}
export default A;
setTimeout(() => {
A = 'changed';
}, 500);
此时控制台输出 ƒ A() {},export 语句不再符合 export default function 语法形式,A 便使用了值传递。
不仅仅 export default function,export default class 也是同样的表现。
为什么呢?
原因与这些语句当被用作表达式时的表现有关。
function someFunction() {}
class SomeClass {}
console.log(typeof someFunction); // "function"
console.log(typeof SomeClass); // "function"
如果我们将他们变成表达式:
(function someFunction() {});
(class SomeClass {});
console.log(typeof someFunction); // "undefined"
console.log(typeof SomeClass); // "undefined"
function 和 class 语句会在作用域/块中创建标识符,而 function 和 class 语句却不会,尽管他们的函数名和类名可以被使用。
因此,下面代码中:
export default function someFunction() {}
console.log(typeof someFunction); // "function"
如果不进行特殊处理的话,输出的将会是 "undefined"。
小结如下:
// 导入引用
import { A } from './module.js';
import { A as otherName } from './module.js';
import * as module from './module.js';
const module = await import('./module.js');
// 导入值
let { A } = await import('./module.js');
// 导出引用
export { A };
export { A as otherName };
export { A as default };
export default function A() {}
// 导出值
export default A;
export default 'hello!';
在早些时候,模块中的默认导出是这样的
export default = A,这样看来,A被当做表达式会更明显一些。
你真的懂 export default 吗?的更多相关文章
- module.exports,exports,export和export default,import与require区别与联系
还在为module.exports.exports.export和export default,import和require区别与联系发愁吗,这一篇基本就够了! 一.首先搞清楚一个基本问题: modu ...
- [C#] C# 知识回顾 - 你真的懂异常(Exception)吗?
你真的懂异常(Exception)吗? 目录 异常介绍 异常的特点 怎样使用异常 处理异常的 try-catch-finally 捕获异常的 Catch 块 释放资源的 Finally 块 一.异常介 ...
- babel6 的 export default bug
把export default 变成 module.exports 就行了
- 【转】was mutated while being enumerated 你是不是以为你真的懂For...in... ??
原文网址:http://www.jianshu.com/p/ad80d9443a92 支持原创,如需转载, 请注明出处你是不是以为你真的懂For...in... ??哈哈哈哈, 我也碰到了这个报错 . ...
- javascript的语法作用域你真的懂了吗
原文:javascript的语法作用域你真的懂了吗 有段时间没有更新了,思绪一下子有点转不过来.正应了一句古话“一天不读书,无人看得出:一周不读书,开始会爆粗:一月不读书,智商输给猪.”.再加上周五晚 ...
- 你真的懂ajax吗?
前言 总括: 本文讲解了ajax的历史,工作原理以及优缺点,对XMLHttpRequest对象进行了详细的讲解,并使用原生js实现了一个ajax对象以方便日常开始使用. damonare的ajax库: ...
- module.exports与exports,export和export default
还在为module.exports.exports.export和export default,import和require区别与联系发愁吗,这一篇基本就够了! 一.首先搞清楚一个基本问题: modu ...
- module.exports,exports,export和export default,import与require区别与联系【原创】
还在为module.exports.exports.export和export default,import和require区别与联系发愁吗,这一篇基本就够了! 一.首先搞清楚一个基本问题: modu ...
- JavaScript ES6中export及export default的区别
相信很多人都使用过export.export default.import,然而它们到底有什么区别呢? 在JavaScript ES6中,export与export default均可用于导出常量.函 ...
随机推荐
- Linux_控制服务与守护进程
一.systemd 1.systemd简介 1️⃣:systemd是用户空间的第一个应用程序,即/sbin/init 2️⃣:init程序的类型: SysV风格:init(centos5),实现系统初 ...
- Kubernetes 部署微服务电商平台(16)
一.概念 微服务就是很小的服务,小到一个服务只对应一个单一的功能,只做一件事.这个服务可以单独部署运行,服务之间可以通过RPC来相互交互,每个微服务都是由独立的小团队开发,测试,部署,上线,负责它的整 ...
- IDEA 全局搜索 Jar 包中源码内容
引言 项目开发过程中,经常遇到需要在依赖的 Jar 包查看源码,查找类方法和属性,介绍两种在 IDEA 中搜索 Jar 包内容的方式 方式一:双击 SHIFT 快捷键 输入需要查询的类名或方法名 方式 ...
- shell基础之变量及表达式
本节内容 1. shell变量简介 2. 定义变量 3. 使用变量 4. 修改变量的值 5. 单引号和双引号的区别 6. 将命令的结果赋值给变量 7. 删除变量 8. 变量类型 9. 特殊变量列表 1 ...
- C语言练习题1(关于快速排序,二分查找与运行时间)
刚刚完成师兄给的一道题目: 随机生成10000位数,进行快速排序后,用二分查找法定位到某个要查询的数(键盘输入某个要查询的数), 结果输出查询的时间,以及是否查到 分享下自己的解题思路: 1,要懂得 ...
- 图解CSS布局(一)- Grid布局
图解CSS布局(一)- Grid布局 先上图 简介 Grid 布局是将容器划分成"行"和"列",产生单元格,然后指定"项目所在"的单元格,可 ...
- .NET平台系列12 .NET未来之开源.NET Core
系列目录 [已更新最新开发文章,点击查看详细] 微软于2014年11月推出了.NET Core 1.0..NET Core的目标是从我们在过去12年中对.NET Framework的构建.交付 ...
- 关于lua闭包导致引用无法释放内存泄露
最近项目存在严重的内存泄漏问题,每次切level 会增加20M无法释放的内存,翻遍了项目用了多个工具,查询资料等 发现项目中两种存在内存泄露的情况 1.lua闭包的不当使用,对比包的引用要及时 释放. ...
- GO学习-(16) Go语言基础之文件操作
Go语言文件操作 本文主要介绍了Go语言中文件读写的相关操作. 文件是什么? 计算机中的文件是存储在外部介质(通常是磁盘)上的数据集合,文件分为文本文件和二进制文件. 打开和关闭文件 os.Open( ...
- springboot打包上线
发布到线上的包结构 runtime是发布到线上的目录结构 1.项目pom.xml添加打包配置 <build> <plugins> <plugin> <grou ...