返回「计算机、信息技术与工程」

JavaScript & TypeScript

JavaScript & TypeScript ECMAScript(简称 ES)是一部由 Ecma International 制定的脚本语言规范,文件编号 ECMA 262 。它定义了语言的语法、类型系统、内建对象、错误模型、并发抽象(Promise Jobs)等核心语义,用来保证不同实现之间可以运行同一段脚本代码。

更多
Markdown 结构化数据
本文目录 29 个章节
本文介绍了JaavScript与TypeScript的基础概念以及互相关系。介绍了两者的核心语法以及差异,并说明 TS 编译时删除类型、按环境保留或降级语法。

JavaScript & TypeScript

ECMAScript(简称 ES)是一部由 Ecma International 制定的脚本语言规范,文件编号 ECMA-262。它定义了语言的语法、类型系统、内建对象、错误模型、并发抽象(Promise Jobs)等核心语义,用来保证不同实现之间可以运行同一段脚本代码。

  • 1995 年 Brendan Eich 在 Netscape 浏览器中实现了早期的 LiveScript/JavaScript
  • 为了避免与 Sun 的“Java”商标纠纷,1996 年 Netscape 把这门语言送交标准机构 ECMA,双方同意用 “ECMAScript” 作为标准名称,浏览器实现继续对外叫 “JavaScript”。

ECMAScript 是 JavaScript 的规范。

维度 JavaScript TypeScript
核心定位 ECMAScript 标准的脚本语言,动态类型、解释执行 JavaScript 的静态类型超集。在 JS 语法之上加入类型系统、装饰器、枚举、命名空间等,并在编译期做类型检查
设计目标 浏览器脚本、通用运行时语言 JS 的_静态类型超集_,为中大型项目提供类型安全
运行方式 解释 / JIT 执行 必须先编译为 JS (tsc, esbuild, vite, webpack…)
更新节奏 ECMAScript 年度规范 (ES2024…) TS 每月/季度发版(当前 5.8, 2025-03)
生态依赖 浏览器、Node.js、Bun… 与 JS 共用生态,编译后 100 % 兼容

代码结构与模块系统

主题 JavaScript TypeScript
文件扩展名 .js, .mjs, .cjs .ts, .tsx (React JSX)
顶级模块化 ESM import/exportCommonJS require/module.exports 完全同 JS;编译器可输出多种模块格式
tsconfig.json 描述编译目标 (target)、模块格式 (module)、路径别名等

语法与差异

1. 代码结构与模块系统

1.1 文件后缀

  • JS:*.js / *.mjs / *.cjs
  • TS:*.ts / *.tsx(含 React JSX)

1.2 模块化语法

  • ES Modulesimport/export
  • CommonJSrequire/module.exports
  • TS 与 JS 写法一致,但可通过 tsconfig.json 选择输出格式

1.3 tsconfig.json 要点

{
  "target": "es2022",        // 语法下限
  "module": "esnext",        // 模块格式
  "strict": true,            // 全面类型严格模式
  "paths": { "@/*": ["src/*"] }
}

2. 基础语法

2.1 变量与作用域

// JavaScript
var oldVar = 1;          // 函数级作用域
let count = 42;          // 块级作用域
const PI = 3.14;         // 只读

// TypeScript(加类型)
let count: number = 42;

2.2 原始类型

  • JS 运行时:number, string, boolean, undefined, null, symbol, bigint
  • TS 静态补充:any, unknown, never, void

2.3 运算符 / 流程控制

  • 与 C 家族基本一致
  • TS 仅做 “类型是否合理” 检查,不改变写法

3. 函数与高阶能力

// JS:无类型限制
function add(a, b) {
  return a + b;
}

// TS:显式标注
function add(a: number, b: number): number {
  return a + b;
}

// 默认 / 剩余参数
function greet(name = 'world', ...extras) {}

// 箭头函数固定 this
const inc = (x: number) => x + 1;

// 泛型(TS 专属)
function identity<T>(value: T): T {
  return value;
}

4. 面向对象(类机制)

4.1 类与实例字段

class Animal {
  // ES2022 公有字段写法
  speed = 0;

  // ① 运行期真正私有
  #id: number;

  // ② 仅编译期私有(旧环境友好)
  private name: string;

  constructor(name: string, id: number) {
    this.name = name;
    this.#id = id;
  }

  move(distance: number = 0) {
    this.speed = distance;
    console.log(`${this.name} moved ${distance}m`);
  }
}

4.2 高级特性(TS 增强)

  • 访问修饰符:public (默认) / private / protected / readonly
  • 抽象类 & 抽象方法:abstract class Base { abstract foo(): void }
  • 接口契约:interface Shape { area(): number }
  • 参数属性速写:constructor(private id: number) {}
  • 泛型类:class Box<T> { value: T }

5. 类型系统(TS 专属精华)

// 联合 & 交叉
type ID = number | string;
type Position = { x: number } & { y: number };

// 条件类型
type IsString<T> = T extends string ? 'yes' : 'no';

// 映射类型
type Readonly<T> = { readonly [K in keyof T]: T[K] };

// 模板字面量类型
type Hex = `#${string}`;

6. 异步编程

// Promise
function fetchData(): Promise<User[]> { ... }

// async / await
async function main() {
  const list = await fetchData();
}

// Axios 响应体泛型
axios.get<User[]>('/api/user');

10. 常用参考

其他特性

对象字面量

用一对花括号 { … } 当场写出 键 → 值 的映射,马上得到一个对象实例,而 需要先声明类或构造函数。

const user = {
  id: 42,                       // 数值字段
  name: "Perrin",               // 字符串字段
  isAdmin: true,                // 布尔字段
  sayHi() {                     // 函数字面量(方法)
    console.log(`Hi, I’m ${this.name}`);
  }
};
  • 运行期可增删属性
const p = { x:1, y:2 };
p.z = 3; delete p.x;
  • 字段值可为函数
do: () => {}
写法 例子 说明
基本键值对 { a: 1, b: 2 } 冒号左边是键、右边是任何表达式
简写属性 { a, b } 当变量名与键名相同时写一次即可
方法定义简写 { calc(x) { return x*2 } } 等价于 calc: function(x){…}
计算得出的键名 { [prefix + id]: val } 方括号里写表达式,求值后当键
解构合并 { ...base, extra: 1 } 扩展运算符 ... 复制字段

常见枚举方式

for (const k in obj) { ... }          // 可遍历原型链 & 可枚举属性
Object.keys(obj)                      // 自有可枚举键数组
Object.getOwnPropertyNames(obj)       // 包含不可枚举

与 类 (class) 的关系

  • class 语法本质是语法糖:编译后仍然生成 函数 (constructor)原型对象
  • 对象字面量适合“一次性数据包”或“配置表”;若需要多个实例共享方法、属性访问控制,则用 class

闭包

  1. 定义 闭包是指函数与其创建时的词法环境(Lexical Environment)共同组成的实体。该环境记录了函数在定义位置能够访问的所有变量(包括参数、本地变量及外层作用域变量)。

  2. 词法环境与环境记录

    • 每次执行上下文(函数调用、全局、Eval)会创建一个环境记录,存放该上下文的所有标识符绑定。
    • 闭包中的函数对象会持有对其所属环境记录的引用,即使外层函数已返回,环境记录也不会被 GC 回收。
  3. 多闭包共享变量 同一词法作用域内定义的多个闭包,都会引用同一个环境记录,从而可读写同一份变量。修改该变量会影响所有引用它的闭包。

  4. 与 C 语言对比

    • C 语言无内建闭包;要跨函数共享状态,需使用全局/静态变量显式传递指针/结构体
    • JavaScript 通过闭包实现私有化可多函数共享的状态管理,无需暴露全局。

示例

// 外层函数,返回两个闭包
function createPair() {
  let shared = 0;  // 环境记录中的共享变量

  function inc() {
    shared += 1;
    console.log('inc ->', shared);
  }

  function dec() {
    shared -= 1;
    console.log('dec ->', shared);
  }

  return { inc, dec };
}

const { inc, dec } = createPair();

inc(); // inc -> 1
inc(); // inc -> 2
dec(); // dec -> 1
dec(); // dec -> 0
  • shared 变量存在于 createPair 的环境记录中。
  • incdec 两个闭包都捕获并共享同一份 shared
  • shared 的增减对另一个闭包可见,实现了多闭包之间的状态同步。

JS与TS面向对象差异与编译方法

class语法差异

1. JavaScript:原型链 + 语法糖

class Animal {
  constructor(name) {
    this.name = name;   // 实例字段
  }
  move(dist = 0) {      // 原型方法(所有实例共享)
    console.log(`${this.name} moved ${dist}m`);
  }
}

class Dog extends Animal {
  bark() {
    console.log('Woof!');
  }
}

幕后class 只是把早年的 “构造函数 + prototype” 写法包装得更像 C#/Java。继承时 Dog.prototype 会把原型链指向 Animal.prototype,调用 super() 本质就是执行父构造函数。


2. TypeScript:在同一套语法上贴“类型安全标签”

abstract class Animal {
  #id: number;                  // 运行期私有
  protected speed = 0;          // 编译期受保护

  constructor(private name: string, id: number) { // “参数属性”自动赋值 name
    this.#id = id;
  }

  abstract sound(): void;       // 抽象方法

  move(distance: number = 0) {
    this.speed = distance;
    console.log(`${this.name} moved ${distance} m`);
  }
}

class Dog extends Animal {
  sound() { console.log('Woof!'); }
}

TS class 编译过程

TypeScript 面向对象代码示例

// file: animal.ts  —— TypeScript 源码
export default class Animal {
  #id: number;          // ECMAScript 私有字段
  private name: string; // TS 私有修饰符(仅编译期检查)
  protected speed = 0;  // 受保护成员

  constructor(id: number, name: string) {
    this.#id = id;
    this.name = name;
  }

  move(distance: number = 0) {
    this.speed = distance;
    console.log(`${this.name} moved ${distance} m`);
  }
}

target: "es2015"现代浏览器/Node 18+

因为运行环境原生支持 class#private 字段,TypeScript 只删掉类型信息,其余几乎原封不动:

// animal.js  —— ES2015 输出
export default class Animal {
    #id;
    name;      // ← 仅保留属性,private 被擦除
    speed = 0;
    constructor(id, name) {
        this.#id = id;
        this.name = name;
    }
    move(distance = 0) {
        this.speed = distance;
        console.log(`${this.name} moved ${distance} m`);
    }
}
  • private namename修饰符只存在于 .ts,运行时代码里消失
  • #id 保留:这是 ES 私有字段的语法,浏览器自己支持。

target: "es5"老浏览器/IE 仍在时的写法

老环境不认识 class,编译器会降级为 立即执行函数 + 原型 模式:

// animal.js  —— ES5 输出
"use strict";
exports.__esModule = true;
var _id = new WeakMap();       // 用 WeakMap 模拟 #id 私有字段
var Animal = /** @class */ (function () {
    function Animal(id, name) {
        _id.set(this, id);     // 维护私有值
        this.name = name;      // private 修饰符依旧被擦除
        this.speed = 0;
    }
    Animal.prototype.move = function (distance) {
        if (distance === void 0) { distance = 0; }
        this.speed = distance;
        console.log(this.name + " moved " + distance + " m");
    };
    return Animal;
}());
exports["default"] = Animal;

IIFE + 原型链:把 class 降级为构造函数

IIFE Immediately-Invoked Function Expression:写完就立刻调用自己,用来制造一个私有作用域,避免里面的变量泄漏到外面。...() 最后那对小括号就是“立刻执行”。

(function () { ... }()): 这是一个 自执行函数 (IIFE),返回值是一个构造函数Animal, 赋给变量 Animal

原型方法 Animal.prototype.move = function (distance) { ... }

元素 说明
Animal.prototype 原型对象:挂在这里的方法会被 所有实例共享,节省内存。
move 方法名。
function (distance) { ... } 普通匿名函数,实现真正的业务逻辑。