js 在 import 之前,为什么需要先显式声明 export 呢?

2022-08-04 11:09:43 +08:00
 ignor

这边有个写给自己用的前端项目,js 都是用原始的 script 标签逐个引入的,里面的各种函数和变量都是全局的,现在渐渐感到各种依赖关系有些麻烦,需要模块化了,所以初步学习了一下。

看了一下 ES6 模块语法,似乎之前定义的那些全局变量都需要一个个添加 export……之前写 Java, python 都没有这样的困扰,是什么原因造成了这样的设计呢?或者是我打开方式不对?正确的模块化改造步骤是怎样的?

3256 次点击
所在节点    JavaScript
20 条回复
wayh
2022-08-04 11:15:35 +08:00
因为模块有自己的作用域,默认模块内部的变量函数都是不导出的,如果需要外界访问到就需要 export 导出,或者声明变量的时候,直接 window.xxx 这种 就不用就 export 了,但这种就是全局变量了,和你之前的 script 方式没有本质区别。
westoy
2022-08-04 11:17:45 +08:00
java 又不会产生一个文件导出几个 public 类这种问题

python 有__all__限制默认导出变量, 只是一开始没考虑到, 印象里是 2.1 才后加进去的
retrocode
2022-08-04 11:18:25 +08:00
如果你开心的话也可以写个闭包, 然后闭包全局工具类
wangtian2020
2022-08-04 11:21:06 +08:00
```
let mInit = () => {
globalThis.fk ='114514'
}

export { mInit }
```
lisongeee
2022-08-04 11:29:39 +08:00
js 模块化之后就可以《模块热替换》,这是前端构建工具最重要的特性之一,有了它就能极大地提高开发速度

你改动单个文件,构建工具根据这个文件找到依赖边界,浏览器界面就能进行局部刷新,而不是重新刷新这个标签页

比如你改动 vue 文件的 template ,你的界面上这个组件的区域就会刷新,但是你的状态还在,不会刷新整个页面

![hmr]( https://github.com/lisonge/src/raw/main/img/2022-07-18_18-00-12.gif)
iidear2015
2022-08-04 11:37:13 +08:00
script 标签直接引入的 js 文件里,var 声明的变量是挂在全局作用域下的,var xx = 1 和 window.xx = 1 一样。
模块化之后的 js 文件里,var 声明的变量是挂在模块作用域下的,等于是模块的私有变量。只有 export 出去的变量才能被其他模块访问访问。编译器是通过闭包实现的


模块化改造就是要消灭全局变量,如果还是要使用全局变量,使用 window.xx
DOLLOR
2022-08-04 11:38:09 +08:00
上面都是答非所问的。
楼主问的是别的语言(像 python )不需要写 export 关键字,就能在别的模块引入该模块的变量。
而 JS 需要显式声明 export 变量,才能在别的模块里 import 进来。

我也好奇这种不同的设计有什么优点缺点,出于什么考虑。
aaronlam
2022-08-04 11:46:43 +08:00
其实比较值得说的是,为什么 `import` 的方式不改成类似这样的 `from './demo.js' import { Demo }` 书写顺序,更加的符合直觉。
dcsuibian
2022-08-04 11:55:58 +08:00
js 模块的话不 export 就是私有的,这就是封装嘛,不让你关注细节

Java 有访问控制符啊,public 、protected 之类的。

Python 倒是留给导入者决定的。
但我感觉 JS 的 export 明显更好啊,看 export 部分就知道导出了啥,有哪些可用的。
dcsuibian
2022-08-04 11:59:27 +08:00
@aaronlam
我也感觉这么更好,如果这么写的话,那我写完 `from './demo.js' ` IDE 就可以推导了
现在把 import 的放前面就很麻烦
crysislinux
2022-08-04 12:17:58 +08:00
@dcsuibian 这个问题很多相关讨论了,import 在后面一方面是跟 import 'xxx'这种引入整个包形式上更一致,二是往往我们更关心导入了什么,设想一下如果 from 后面的路径比较长会是什么样子。
ailer
2022-08-04 12:21:55 +08:00
@aaronlam from 的地址可能会很长,但意义不大,放后面可以突出导出的内容
aaronlam
2022-08-04 13:04:33 +08:00
@crysislinux
@ailer

是的,其实指出这种方式,是希望在导入时能利用上 IDE 的推导便于导入所需要的内容,不过如果路径很长的确是一种反作用。
dcsuibian
2022-08-04 13:55:16 +08:00
@crysislinux 但实际上不是很长。
ES 在给出模块化定义的时候,没给出推荐的形式(至少我是从没听人提起过),比如 Java 的域名反写,那确实到会是很长。
SingeeKing
2022-08-04 14:01:35 +08:00
你可以换一种理解方式,把 JS 中的 export 当成 Java 中的 public 理解

export let x = 123

export function xxx() {}
dcsuibian
2022-08-04 14:02:31 +08:00
@crysislinux 以前我在初学 npm 和 python 的 pip 的时候,就有过疑问:他们要怎么解决命名冲突?为啥不参考 maven 的 pom 坐标的方式?
当时我在网上搜索的时候,得到的答案是:js 开发者倾向于简单的做法。(不记得在哪儿看到了)
我接受了这个理由,所以才会觉得 ES 官方的导入语法不太合适。

我个人感觉 JS 和 Python 是非常相近的,都是脚本语言,追求简单。npm install xxx 和 pip install xxx ,但 Python 就提供了 from xxx import xxx 的语法。
lsdxl
2022-08-04 14:21:59 +08:00
兄弟姐妹们,我有个和这不相干的问题咨询下:
就是在 npm 安装了 vue3 最新版本,typescript 使用引入 vue 文件,我没有去手动增加 vue module 的声明,但是 vue 模块被识别了(调用 tsc 类型也可以通过),这难道源码某个声明文件有实现吗 我没找到 有了解的吗
chnwillliu
2022-08-18 20:27:48 +08:00
那你先列举一下默认导出顶层作用域中的变量比默认不导出有什么优势。顶层变量不想导出还得专门引入一种语法来声明不是?像 python 一样用下划线么?还是用其他关键字?结果和默不导出不就是一回事吗?

let a=1; //不导出
export let b = 2; //加关键字导出,和 java 的 public 岂不是一样?
export {a}; // export 还可以导出已有内部变量
chnwillliu
2022-08-18 20:37:12 +08:00
下划线开头的变量名在 ES6 之前的版本中并没有实际意义,强行引入额外的语义只会挖坑。并且当时已经流行的社区 js 模块化方案 commonjs requireJS 都是显式声明导出的变量。显式声明导出也符合从无模块化到有模块化迁移的过程。那时候用闭包模拟模块肯定没法做到默认导出模块顶层作用域。
ignor
2022-08-18 20:58:21 +08:00
@chnwillliu 最开始项目小,单文件的时候定义的全局变量,就是该项目最公开的部分,那么拆成模块后,就理应是该模块最公开的部分,个人觉得这是很直观的。
所以说到底还是历史原因吧,就是大家已经习惯了“闭包没办法导出顶层作用域”

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/870590

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX