你真的懂 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中级之keepalived概念
一.HA集群中的相关术语 1.节点(node) 运行HA进程的一个独立主机,称为节点,节点是HA的核心组成部分,每个节点上运行着操作系统和高可用软件服务,在高可用集群中,节点有主次之分,分别称之为主节 ...
- strcpy和memcpy的区别-(转自stone Jin)
strcpy和memcpy都是标准C库函数,它们有下面的特点.strcpy提供了字符串的复制.即strcpy只用于字符串复制,并且它不仅复制字符串内容之外,还会复制字符串的结束符. 已知strcpy函 ...
- -bash: $'\201ccd': δ 的错误是linux编码问题(Centos7)
如果目录是中文目录,你的编码为: [root@dbbd-api01 ~]# cat /etc/locale.conf LANG=zh_CN.GB18030 [root@dbbd-api01 ~]# 那 ...
- C语言编程 菜鸟练习100题(01-10)
[练习1]输出 "Hello, World!" 0. 题目: 输出 "Hello, World!" 1. 分析: 使用 printf() 输出 "He ...
- 19c PDB数据泵迁入
1.问题描述 用数据泵进行pdb的迁入迁出,模拟测试将其他库的数据导入到19cpdb中 2.环境介绍 source:12.2.0.1.0 target:19.0.0.0.0 3.源端制造数据 创建表空 ...
- 【SpringBoot基础系列】手把手实现国际化支持实例开发
[SpringBoot基础系列]手把手实现国际化支持实例开发 国际化的支持,对于app开发的小伙伴来说应该比价常见了:作为java后端的小伙伴,一般来讲接触国际化的机会不太多,毕竟业务开展到海外的企业 ...
- ELK技术栈之-Logstash详解
ELK技术栈之-Logstash详解 前言 在第九章节中,我们已经安装好Logstash组件了,并且启动实例测试它的数据输入和输出,但是用的是最简单的控制台标准输入和标准输出,那这节我们就来深入的 ...
- TensorFlow实现多层感知机函数逼近
TensorFlow实现多层感知机函数逼近 准备工作 对于函数逼近,这里的损失函数是 MSE.输入应该归一化,隐藏层是 ReLU,输出层最好是 Sigmoid. 下面是如何使用 MLP 进行函数逼近的 ...
- CUDA 7 流并发性优化
异构计算是指高效地使用系统中的所有处理器,包括 CPU 和 GPU .为此,应用程序必须在多个处理器上并发执行函数. CUDA 应用程序通过在 streams 中执行异步命令来管理并发性,这些命令是按 ...
- TVM源码框架安装方法
TVM源码框架安装方法 本文提供如何在各种系统上从零构建和安装TVM包的说明.它包括两个步骤: 首先从C++代码中构建共享库(linux的libtvm.so,macOS的libtvm.dylib和wi ...