写前端代码时,我们经常遇到两种用法:
<scriptsrc="./xxdemo.js"></script>import{hello}from'./xxdemo.js'同样是 JS 文件,为什么加载方式不同?为什么有些库会写window.xxx,有些库却使用export?IIFE 又是什么,为什么要让函数定义后立即执行?
1. JS、TS、ESM、npm,分别在说什么?
先分清它们描述的层面:
| 名称 | 描述的事情 | 简单理解 |
|---|---|---|
| JavaScript,简称 JS | 编程语言 | 编写浏览器执行的逻辑 |
| TypeScript,简称 TS | 带类型系统的语言 | 开发时增加类型检查,通常转换为 JS 后交付 |
| ESM | JavaScript 的标准模块机制 | 用import和export共享功能 |
| IIFE | 一种函数表达式写法 | 函数定义后立即调用,常用于隔离内部变量 |
| npm | 软件包管理和分发 | 安装依赖、管理版本、发布代码包 |
| Vite、esbuild 等 | 开发和构建工具 | 转换源码、处理依赖、生成发布文件 |
因此,“使用 JS 还是 ESM”并不是两个并列选项。采用 ESM 时,写的仍然可以是 JavaScript。
TypeScript 的类型主要在开发阶段帮助检查代码,不会作为类型检查逻辑自动带到浏览器中。参考:TypeScript 官方入门
2. 普通 script 是怎么使用 JS 的?
本文把没有type="module"的 JavaScript 脚本称为“普通脚本”,也叫经典脚本。
例如,一个完整的 HTML 页面可以这样写:
<!DOCTYPEhtml><htmllang="zh-CN"><head><metacharset="UTF-8"><title>xxdemo 普通脚本示例</title></head><body><pid="output"></p><script>functionhello(name){return'你好,'+name}document.getElementById('output').textContent=hello('xxdemo')</script></body></html>这里的代码由浏览器直接执行。也可以把脚本移到外部文件,再通过src加载。
文件以.js结尾,并不能说明它是普通脚本还是 ESM。还要看文件内容,以及它被怎样加载。参考:MDN script 元素
3. window.xxx 是什么?
在浏览器页面里,window是全局对象。下面的代码给它增加了一个属性:
window.XXDemo={hello(name){return'你好,'+name}}其他脚本就可以使用:
window.XXDemo.hello('xxdemo')在没有同名局部变量遮蔽等情况下,也可以写:
XXDemo.hello('xxdemo')把功能集中在XXDemo下面,可以避免将许多名字分别放到全局,例如window.hello、window.reset、window.start。
但window.xxx本身不是模块规范。普通脚本可以给它赋值,ESM 也可以给它赋值。
4. IIFE 为什么要立即执行?
IIFE 的全称是 Immediately Invoked Function Expression,中文叫“立即执行函数表达式”。
普通函数往往先定义,再调用:
functionstart(){console.