【Web客户端生态】JavaScript与TypeScript核心速通

0 阅读4分钟

概念及定位

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

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

ECMAScript 是 JavaScript 的规范。

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

代码结构与模块系统

主题JavaScriptTypeScript
文件扩展名.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. 常用参考

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) { ... }普通匿名函数,实现真正的业务逻辑。