1. 常用全局变量
- __filename
- __dirname
- setTimeout
- clearTimeout
- setInterval
- console
- process
- 等等..
2. 模块化开发
2.1 JavaScript 模块化规范
CommonJS 规范
AMD 规范
CMD 规范
CommonJS 使用
module
为了实现模块的导出,Node 中使用的是 Module 类,每一个模块都是 Module 的一个实例,也就是 module
故在 Node 中真正用于导出的其实根本不是 exports,而是 module.exports
exports
浅层拷贝 = 引用赋值 = 引用的赋值 = 指向同一个对象
module 和 exports 的关系
module 对象的 exports 属性是 exports 对象的一个引用;即 module.exports = exports
require
global
ES6 规范
认识 ES Module
- 采用
export和import关键字来实现模块化export负责将模块内的内容导出import负责从其他模块导入内容
- 采用编译期的静态分析,并且加入了动态引用的方式
- 自动采用严格模式:
use strict
案例代码结构
<script src="./modules/foo.js" type="module"></script>
注意:在浏览器中打开时,可能会报错,参考这里,解决办法:在 VSCode 安装插件:Live Server
export 关键字
export关键字将一个模块中的变量、函数、类等导出。有三种方式
// foo.js
// 方式一:在语句声明的前面直接加上 export 关键字
export const name = "why";
export const sayHello = function (name) {
console.log("你好" + name);
};
// 方式二:将所有需要导出的标识符,放到 export 后面的 {} 中
const name = "why";
const sayHello = function (name) {
console.log("你好" + name);
};
export { name, sayHello };
// 注意:这里的 {} 里面不是ES6的对象字面量的增强写法,{} 也不是表示一个对象的;
// 所以: export {name: name},是错误的写法;
// 方式三:导出时给标识符起一个别名(很少用)
const name = "why";
const sayHello = function (name) {
console.log("你好" + name);
};
export {
name as fName,
sayHello as fSayHello
};
import 关键字
import关键字负责从另外一个模块中导入内容。
// index.js
// 方式一:import {标识符列表} from '模块'
import { name, sayHello } from "./modules/foo.js";
console.log(name);
sayHello("jack");
// 方式二:导入时给标识符起别名
import { name as fName, sayHello as fsayHello } from "./modules/foo.js";
console.log(fName);
// 方式三:通过 * 将模块功能放到一个模块功能对象(a module object)上
import * as foo from "./modules/foo.js";
console.log(foo.name);
foo.sayHello("Summer");
export 和 import 结合使用
在开发和封装一个功能库时,通常我们希望将暴露的所有接口放到一个文件中; 这样方便指定统一的接口规范,也方便阅读;这个时候,我们就可以使用
export和import结合使用。
export { sum as barSum } from './bar.js'
default 用法
默认导出
export时可以不需要指定名字;在导入时不需要使用{},并且可以自己来指定名字;它也方便我们和现有的CommonJS等规范相互操作。
// foo.js
// 默认导出方式: format 可省略
export default function format() {
console.log("格式化函数");
}
// index.js
// 默认导入方式:
import fmt from "./modules/foo.js";
fmt();
// 注意:在一个模块中,只能有一个默认导出(default export)
import 函数
如果想要动态的加载某一个模块,可以这样操作吗?
if (true) {
import sub from "./modules/foo.js";
}
显然是不可以的。这是因为 ES Module 在被 JS 引擎解析时,就必须知道它的依赖关系。正确的做法如下:使用 import 函数。
let flag = true;
if (flag) {
import("./modules/foo.js")
.then((res) => {
console.log("在then中的打印");
console.log(res.name);
console.log(res.age);
})
.catch((err) => {
console.log(err);
});
}
CommonJS 的加载过程
CommonJS模块加载js文件的过程是运行时加载的,并且是同步的。
运行时加载意味着:js 引擎在执行 js 代码的过程中加载模块。
同步意味着:一个文件没有加载结束之前,后面的代码都不会执行。
CommonJS通过module.exports导出的是一个对象
导出的是一个对象意味着可以将这个对象的引用在其他模块中赋值给其他变量。但是最终他们都指向的是同一个对象,那么一个变量修改了对象的属性,所有的地方都会被修改。
2.2 Node 模块化实现
CommonJS 和 ES Module 交互
结论一:通常情况下,CommonJS 不能加载 ES Module
因为
CommonJS是同步加载的,但是ES Module必须经过静态分析等,无法在这个时候执行JavaScript代码; 但是这个并非绝对的,某些平台在实现的时候可以对代码进行针对性的解析,也可能会支持; 当然Node当中是不支持的;
结论二:多数情况下,ES Module可以加载CommonJS
ES Module在加载CommonJS时,会将其module.exports导出的内容作为default导出方式来使用; 这个依然需要看具体的实现,比如webpack中是支持的、Node最新的Current版本也是支持的。
// foo.js
const name = "coderwhy";
// CJS 导出
module.exports = {
name,
};
// index.mjs
// ES Module 导入
import foo from "./modules/foo.js";
console.log(foo.name);
与 ES6 对比区别
值的拷贝和引用对比
运行时加载和编译时加载
6.Node 原理解析
6.1 进程和线程
操作系统 - 工厂,进程 - 车间,线程 - 工人
一个工厂(操作系统)可以有多个车间(进程),一个车间可以有多个工人(线程),即多进程和多线程
CPU - 为每一个车间开灯,这样工人可以正常工作。但开灯时间是固定的,即过一段时间,灯会自动熄灭。
6.2 阻塞和非阻塞
6.3 事件循环机制
事件循环是
JavaScript应用程序和浏览器 或Node沟通的桥梁。 而回调函数充当车辆的角色,来回搬运数据。当然咯,这个桥梁上,还有收费站,检查站等设施。