游戏开发的TypeScript(1)TypeScript 的作用域|TypeScript编译原理

笔者注: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循环等内部的代码块都属于块级作用域。使用letconst关键字声明的变量就具有块级作用域。

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 增加了一些特性,像类型检查、访问修饰符(privateprotected)以及模块系统等,让作用域的管理变得更加严谨和安全。

Typescript的编译原理

TypeScript 是 JavaScript 的超集,它为 JavaScript 添加了静态类型系统,最终需要编译成纯 JavaScript 代码才能在浏览器或 Node.js 等环境中运行。

1. 编译流程概览

TypeScript 的编译过程主要分为三个核心阶段:

  1. 解析(Parsing):将源代码转换为抽象语法树(AST)。
  2. 类型检查(Type Checking):基于 AST 进行静态类型分析。
  3. 代码生成(Code Generation):将 AST 转换为 JavaScript 代码。
TypeScript 源代码 → 解析器 → AST → 类型检查器 → 转换 → JavaScript 代码

2. 解析(Parsing)

解析阶段将 TypeScript 源代码转换为抽象语法树(AST),主要分为两个步骤:

词法分析(Lexical Analysis)
  • 将源代码字符流转换为有意义的词法单元(Tokens)。
  • 例如,const x: number = 10; 会被分解为 constx: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 的特性,并在遇到编译错误时快速定位问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值