笔者注:typescript在轻量级游戏开发中相当重要,cocos creator,laya,egret的主要开发语言都是typescript。unity 也可用typescript开发(用工具转lua),所以写这个专题复盘和游戏开发相关的ts基础知识,更多的是给自己记录便于查找。
TypeScript 的作用域
在 TypeScript 里,作用域对变量、函数、类等标识符的可见性与生命周期起着关键的决定作用。
全局作用域
全局作用域是最外围的作用域,其中定义的标识符在整个应用程序里都能被访问到。像在文件顶层声明的变量、函数或者类,都会处于全局作用域。
// 全局变量
const globalVar = 'global';
function globalFunction() {
console.log('This is a global function');
}
class GlobalClass {}
函数作用域
在函数内部声明的变量具有函数作用域,这意味着这些变量只能在该函数内部被访问,函数外部无法访问。TypeScript 中的var
关键字声明的变量就属于函数作用域。
function example() {
var functionScoped = 'I am function scoped';
console.log(functionScoped); // 正常输出
}
example();
console.log(functionScoped); // 错误:找不到变量functionScoped
块级作用域
块级作用域由花括号{}
界定,像if
语句、for
循环、while
循环等内部的代码块都属于块级作用域。使用let
和const
关键字声明的变量就具有块级作用域。
if (true) {
let blockScopedLet = 'I am block scoped with let';
const blockScopedConst = 'I am block scoped with const';
console.log(blockScopedLet); // 正常输出
}
console.log(blockScopedLet); // 错误:找不到变量blockScopedLet
console.log(blockScopedConst); // 错误:找不到变量blockScopedConst
类作用域
类作用域涵盖了类的属性和方法,这些属性和方法可以通过类的实例或者类本身(静态成员)来访问。
class MyClass {
public publicProp = 'public'; // 公共属性,任何地方都能访问
private privateProp = 'private'; // 私有属性,只能在类内部访问
protected protectedProp = 'protected'; // 受保护属性,能在类内部和子类中访问
public publicMethod() {
console.log(this.privateProp); // 正常访问
}
}
const instance = new MyClass();
console.log(instance.publicProp); // 正常访问
console.log(instance.privateProp); // 错误:私有属性只能在类内部访问
console.log(instance.protectedProp); // 错误:受保护属性只能在类内部或子类中访问
模块作用域
在 TypeScript 中,每个文件都可以看作一个独立的模块。模块内部定义的标识符默认是私有的,若想在模块外部访问,需要使用export
关键字进行导出。
// module.ts
const privateToModule = 'private to module';
export const publicToModule = 'public to module';
// main.ts
import { publicToModule } from './module';
console.log(publicToModule); // 正常访问
console.log(privateToModule); // 错误:找不到变量privateToModule
闭包作用域
闭包是指有权访问另一个函数作用域中变量的函数。即便该函数已经执行完毕,其作用域内的变量也会被闭包所保留。
function outer() {
const outerVar = 'I am from outer function';
return function inner() {
console.log(outerVar); // 闭包访问外部函数的变量
};
}
const closure = outer();
closure(); // 输出:I am from outer function
总结
TypeScript 中的作用域规则和 JavaScript 基本一致,不过 TypeScript 增加了一些特性,像类型检查、访问修饰符(private
、protected
)以及模块系统等,让作用域的管理变得更加严谨和安全。
Typescript的编译原理
TypeScript 是 JavaScript 的超集,它为 JavaScript 添加了静态类型系统,最终需要编译成纯 JavaScript 代码才能在浏览器或 Node.js 等环境中运行。
1. 编译流程概览
TypeScript 的编译过程主要分为三个核心阶段:
- 解析(Parsing):将源代码转换为抽象语法树(AST)。
- 类型检查(Type Checking):基于 AST 进行静态类型分析。
- 代码生成(Code Generation):将 AST 转换为 JavaScript 代码。
TypeScript 源代码 → 解析器 → AST → 类型检查器 → 转换 → JavaScript 代码
2. 解析(Parsing)
解析阶段将 TypeScript 源代码转换为抽象语法树(AST),主要分为两个步骤:
词法分析(Lexical Analysis)
- 将源代码字符流转换为有意义的词法单元(Tokens)。
- 例如,
const x: number = 10;
会被分解为const
、x
、:
、number
、=
、10
、;
等 Tokens。
语法分析(Syntactic Analysis)
- 根据 Tokens 构建 AST,每个节点代表代码中的一个语法结构。
- 例如,变量声明、函数调用、类定义等都会被转换为对应的 AST 节点。
示例 AST 结构:
VariableDeclaration
├─ kind: const
├─ declarations
│ └─ VariableDeclarator
│ ├─ name: Identifier(x)
│ ├─ type: Identifier(number)
│ └─ initializer: NumericLiteral(10)
└─ flags: None
3. 类型检查(Type Checking)
TypeScript 的核心特性是静态类型系统,类型检查器通过以下方式工作:
类型推断(Type Inference)
- 自动推导变量类型,无需显式声明。
const x = 10; // 推断为 number 类型
类型注解(Type Annotation)
- 显式指定变量类型。
const y: string = "hello";
类型检查规则
- 验证类型一致性,例如:
const z: number = "world"; // 类型错误:string 不能赋值给 number
类型系统特性
- 接口(Interfaces):定义对象结构。
- 泛型(Generics):创建可重用的类型。
- 联合类型(Union Types):允许变量为多种类型之一。
- 交叉类型(Intersection Types):组合多个类型为一个。
4. 代码生成(Code Generation)
类型检查完成后,编译器将 AST 转换为 JavaScript 代码,主要工作包括:
移除类型信息
- 编译后的 JavaScript 不包含类型注解,仅保留运行时代码。
// TypeScript const add = (a: number, b: number): number => a + b; // JavaScript const add = (a, b) => a + b;
语法降级(Downleveling)
- 将高级 JavaScript 语法转换为目标环境支持的版本(如 ES5)。
// TypeScript (ES6) class Person { constructor(public name: string) {} } // JavaScript (ES5) var Person = (function () { function Person(name) { this.name = name; } return Person; })();
特性转换
- 转换 TypeScript 特有语法(如枚举、装饰器)为 JavaScript。
// TypeScript 枚举 enum Color { Red, Green, Blue } // JavaScript 等价实现 var Color; (function (Color) { Color[Color["Red"] = 0] = "Red"; Color[Color["Green"] = 1] = "Green"; Color[Color["Blue"] = 2] = "Blue"; })(Color || (Color = {}));
5. 编译选项与配置
tsconfig.json
是 TypeScript 的核心配置文件,控制编译行为:
{
"compilerOptions": {
"target": "ES6", // 目标 JavaScript 版本
"module": "commonjs", // 模块系统
"strict": true, // 启用所有严格类型检查
"esModuleInterop": true, // 支持 ES6 模块与 CommonJS 互操作
"skipLibCheck": true, // 跳过类型声明文件检查
"forceConsistentCasingInFileNames": true // 强制文件名大小写一致
},
"include": ["src/**/*.ts"],
"exclude": ["node_modules"]
}
6. 高级特性
增量编译(Incremental Compilation)
- 使用
tsconfig.json
中的incremental: true
缓存编译结果,加快后续编译速度。
声明文件(.d.ts)
- 提供类型信息但不生成 JavaScript 代码,用于描述现有 JavaScript 库的类型。
// lodash.d.ts declare function debounce(func: Function, wait: number): Function;
装饰器(Decorators)
- 通过编译转换为元数据和函数调用。
// 装饰器 @log class MyClass {} // 编译后 var MyClass = (function () { function MyClass() {} MyClass = __decorate([log], MyClass); return MyClass; })();
7. 编译工具链
- tsc:TypeScript 官方编译器。
- Babel:通过
@babel/preset-typescript
编译 TypeScript(仅移除类型信息,不进行类型检查)。 - webpack:结合
ts-loader
或awesome-typescript-loader
集成到构建流程中。
总结
TypeScript 编译的核心目标是在保留 JavaScript 动态特性的同时,通过静态类型检查提供早期错误捕获。整个过程通过解析、类型检查和代码生成三个阶段,将带有类型信息的 TypeScript 代码转换为纯 JavaScript 代码,同时确保类型安全。理解编译原理有助于更好地利用 TypeScript 的特性,并在遇到编译错误时快速定位问题。