概念及定位
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/export 、CommonJS require/module.exports | 完全同 JS;编译器可输出多种模块格式 |
tsconfig.json | — | 描述编译目标 (target )、模块格式 (module )、路径别名等 |
语法与差异
1. 代码结构与模块系统
1.1 文件后缀
- JS:
*.js
/*.mjs
/*.cjs
- TS:
*.ts
/*.tsx
(含 React JSX)
1.2 模块化语法
- ES Modules →
import/export
- CommonJS →
require/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. 常用参考
- TypeScript Handbook www.typescriptlang.org/docs/handbo…
- ECMAScript 提案追踪 tc39.es
- TS Playground 在线实验 www.typescriptlang.org/play
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 name
→name
:修饰符只存在于 .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) { ... } | 普通匿名函数,实现真正的业务逻辑。 |