跳至内容
张叶安的小站
用户工具
登录
站点工具
搜索
工具
显示页面
过去修订
反向链接
最近更改
媒体管理器
网站地图
登录
>
最近更改
媒体管理器
网站地图
您的足迹:
typescript:第二章变量声明
本页面只读。您可以查看源文件,但不能更改它。如果您觉得这是系统错误,请联系管理员。
====== 第二章:变量声明 ====== ===== 本章概述 ===== 变量声明是编程的基础,TypeScript 继承了 JavaScript 的所有声明方式,并添加了类型注解。本章将深入讲解 let、const、var 的区别,解构赋值,展开运算符等重要概念。 ===== 2.1 var 声明(传统方式) ===== 在 ES6 之前,JavaScript 只有 var 这一种声明变量的方式。了解 var 的特性有助于理解为什么需要 let 和 const。 ==== 2.1.1 函数作用域 ==== ```typescript function example() { var x = 10; if (true) { var x = 20; // 同一个变量! console.log(x); // 20 } console.log(x); // 20 } ``` var 声明的变量具有函数作用域,而不是块级作用域。 ==== 2.1.2 变量提升 ==== ```typescript console.log(hoisted); // undefined(不会报错) var hoisted = "I am hoisted"; // 实际上等价于: var hoisted2; console.log(hoisted2); hoisted2 = "I am hoisted"; ``` ==== 2.1.3 重复声明 ==== ```typescript var x = 1; var x = 2; // 不会报错,覆盖了之前的值 console.log(x); // 2 ``` ==== 2.1.4 为什么避免使用 var ==== ```typescript // 问题1:循环中的闭包问题 for (var i = 0; i < 3; i++) { setTimeout(() => console.log(i), 100); } // 输出:3, 3, 3(不是 0, 1, 2) // 问题2:意外的变量覆盖 var message = "Hello"; if (true) { var message = "World"; // 意外覆盖了外部变量 } console.log(message); // "World" ``` ===== 2.2 let 声明(块级作用域) ===== let 是 ES6 引入的新的变量声明方式,提供了块级作用域。 ==== 2.2.1 块级作用域 ==== ```typescript function letExample() { let x = 10; if (true) { let x = 20; // 不同的变量! console.log(x); // 20 } console.log(x); // 10 } ``` 块级作用域包括: - if/for/while 语句块 - try/catch/finally 块 - 单独的 {} 块 ==== 2.2.2 暂时性死区(Temporal Dead Zone) ==== ```typescript console.log(letVar); // Error: Cannot access 'letVar' before initialization let letVar = "hello"; // 在声明之前访问 let 变量会导致错误 function deadZone() { // 暂时性死区开始 console.log(value); // Error! // 暂时性死区结束 let value = 42; } ``` ==== 2.2.3 不允许重复声明 ==== ```typescript let x = 1; // let x = 2; // Error: Cannot redeclare block-scoped variable 'x' // 但在不同作用域可以 let y = 1; if (true) { let y = 2; // OK console.log(y); // 2 } console.log(y); // 1 ``` ==== 2.2.4 解决循环闭包问题 ==== ```typescript // let 正确工作的循环 for (let i = 0; i < 3; i++) { setTimeout(() => console.log(i), 100); } // 输出:0, 1, 2 // 每次迭代创建新的绑定 for (let i = 0; i < 3; i++) { let i = "inner"; // 可以重新声明,因为是新的作用域 console.log(i); } // 输出:inner, inner, inner ``` ===== 2.3 const 声明(常量) ===== const 声明的是常量,一旦被赋值就不能再改变。 ==== 2.3.1 基本用法 ==== ```typescript const PI = 3.14159; // PI = 3.14; // Error: Cannot assign to 'PI' because it is a constant const greeting = "Hello"; // greeting = "Hi"; // Error ``` ==== 2.3.2 必须初始化 ==== ```typescript // const uninitialized; // Error: 'const' declarations must be initialized const initialized = "I have a value"; // OK ``` ==== 2.3.3 常量引用 vs 值 ==== ```typescript // 基本类型 - 值不能改变 const count = 5; // count = 10; // Error // 对象 - 引用不能改变,但属性可以修改 const person = { name: "Alice", age: 25 }; person.age = 26; // OK person.name = "Bob"; // OK // person = {}; // Error: Cannot assign to 'person' // 数组同理 const numbers = [1, 2, 3]; numbers.push(4); // OK numbers[0] = 10; // OK // numbers = []; // Error ``` ==== 2.3.4 使用 Object.freeze 深度冻结 ==== ```typescript const frozenPerson = Object.freeze({ name: "Alice", age: 25, address: { city: "Beijing" } }); // frozenPerson.age = 26; // Error in strict mode // 注意:Object.freeze 是浅冻结 frozenPerson.address.city = "Shanghai"; // 仍然可以修改! // 深度冻结函数 function deepFreeze<T>(obj: T): T { const propNames = Object.getOwnPropertyNames(obj); for (const name of propNames) { const value = (obj as any)[name]; if (value && typeof value === "object") { deepFreeze(value); } } return Object.freeze(obj); } ``` ===== 2.4 let vs const 选择指南 ===== ==== 2.4.1 默认使用 const ==== ```typescript // 好的实践:默认使用 const const apiUrl = "https://api.example.com"; const maxRetries = 3; const config = { timeout: 5000, retries: 3 }; // 只有在需要重新赋值时才使用 let let currentUser = null; let retryCount = 0; let isLoading = false; ``` ==== 2.4.2 何时使用 let ==== ```typescript // 1. 循环计数器 for (let i = 0; i < 10; i++) { console.log(i); } // 2. 需要重新赋值的变量 let score = 0; score += 10; score *= 2; // 3. 条件赋值 let result: string; if (condition) { result = "A"; } else { result = "B"; } // 4. try-catch 中的变量 let data: User; try { data = JSON.parse(jsonString); } catch (e) { data = defaultUser; } ``` ===== 2.5 解构赋值(Destructuring) ===== 解构赋值允许从数组或对象中提取值并赋值给变量。 ==== 2.5.1 数组解构 ==== ```typescript // 基本解构 const colors = ["red", "green", "blue"]; const [first, second, third] = colors; console.log(first); // "red" console.log(second); // "green" // 跳过元素 const [primary, , tertiary] = colors; console.log(tertiary); // "blue" // 剩余元素 const [head, ...tail] = [1, 2, 3, 4, 5]; console.log(head); // 1 console.log(tail); // [2, 3, 4, 5] // 默认值 const [a = 10, b = 20] = [1]; console.log(a); // 1 console.log(b); // 20 // 交换变量 let x = 1, y = 2; [x, y] = [y, x]; console.log(x, y); // 2, 1 ``` ==== 2.5.2 对象解构 ==== ```typescript // 基本解构 const user = { name: "Alice", age: 25, email: "alice@example.com" }; const { name, age, email } = user; console.log(name); // "Alice" // 重命名 const { name: userName, age: userAge } = user; console.log(userName); // "Alice" // 默认值 const { name: n, country = "Unknown" } = user; console.log(country); // "Unknown" // 嵌套解构 const nested = { user: { profile: { firstName: "Alice", lastName: "Smith" } } }; const { user: { profile: { firstName, lastName } } } = nested; console.log(firstName); // "Alice" // 剩余属性 const { name: n2, ...rest } = user; console.log(rest); // { age: 25, email: "alice@example.com" } ``` ==== 2.5.3 函数参数解构 ==== ```typescript // 对象参数解构 function greetUser({ name, age }: { name: string; age: number }): string { return `Hello ${name}, you are ${age} years old`; } // 带默认值 function createUser({ name, age = 18, role = "user" }: { name: string; age?: number; role?: string; }) { return { name, age, role }; } // 数组参数解构 function processCoordinates([x, y]: [number, number]): string { return `X: ${x}, Y: ${y}`; } // 嵌套解构 function processUser({ name, address: { city, country } }: { name: string; address: { city: string; country: string }; }) { return `${name} lives in ${city}, ${country}`; } ``` ==== 2.5.4 解构与类型注解 ==== ```typescript // 数组解构带类型 const [firstNum, secondNum]: [number, number] = [1, 2]; // 对象解构带类型 const { id, title }: { id: number; title: string } = { id: 1, title: "Hello" }; // 接口配合解构 interface Point { x: number; y: number; } function movePoint({ x, y }: Point, dx: number, dy: number): Point { return { x: x + dx, y: y + dy }; } ``` ===== 2.6 展开运算符(Spread Operator) ===== 展开运算符(...)可以将可迭代对象展开为多个元素。 ==== 2.6.1 数组展开 ==== ```typescript // 复制数组 const original = [1, 2, 3]; const copy = [...original]; // 连接数组 const arr1 = [1, 2]; const arr2 = [3, 4]; const combined = [...arr1, ...arr2]; // [1, 2, 3, 4] // 在数组中添加元素 const withPrefix = [0, ...original]; // [0, 1, 2, 3] const withSuffix = [...original, 4]; // [1, 2, 3, 4] const inMiddle = [0, ...original, 4]; // [0, 1, 2, 3, 4] // 数组转参数 const numbers = [1, 2, 3, 4, 5]; const max = Math.max(...numbers); // 5 // 字符串转数组 const chars = [..."hello"]; // ['h', 'e', 'l', 'l', 'o'] ``` ==== 2.6.2 对象展开 ==== ```typescript // 复制对象(浅拷贝) const original = { a: 1, b: 2 }; const copy = { ...original }; // 合并对象 const obj1 = { a: 1, b: 2 }; const obj2 = { c: 3, d: 4 }; const merged = { ...obj1, ...obj2 }; // { a: 1, b: 2, c: 3, d: 4 } // 覆盖属性 const defaults = { host: "localhost", port: 3000 }; const config = { ...defaults, port: 8080 }; // { host: "localhost", port: 8080 } // 条件展开 const condition = true; const dynamic = { a: 1, ...(condition && { b: 2 }), // 条件为真时才包含 b }; // 添加新属性 const user = { name: "Alice" }; const userWithAge = { ...user, age: 25 }; ``` ==== 2.6.3 函数参数(Rest Parameters) ==== ```typescript // 收集剩余参数 function sum(...numbers: number[]): number { return numbers.reduce((total, n) => total + n, 0); } sum(1, 2, 3, 4, 5); // 15 // 与普通参数结合 function greet(greeting: string, ...names: string[]): string { return `${greeting}, ${names.join(" and ")}!`; } greet("Hello", "Alice", "Bob", "Charlie"); // "Hello, Alice and Bob and Charlie!" ``` ===== 2.7 类型声明的最佳实践 ===== ==== 2.7.1 优先使用 const ==== ```typescript // ✓ 好的实践 const API_URL = "https://api.example.com"; const MAX_ITEMS = 100; const user = { name: "Alice", age: 25 }; // ✗ 避免 let API_URL = "https://api.example.com"; // 不会改变的值不需要 let ``` ==== 2.7.2 解构时添加类型 ==== ```typescript // ✓ 明确的类型 interface User { name: string; age: number; } function processUser(user: User): void { const { name, age }: { name: string; age: number } = user; // 或使用接口 const { name: n, age: a }: User = user; } // ✓ 更简洁的方式 function processUser2({ name, age }: User): void { console.log(name, age); } ``` ==== 2.7.3 展开运算符的注意事项 ==== ```typescript // ✓ 浅拷贝问题 const user = { name: "Alice", address: { city: "Beijing" } }; const userCopy = { ...user }; userCopy.address.city = "Shanghai"; console.log(user.address.city); // "Shanghai" - 原始对象也被修改了! // ✓ 深拷贝方案 const deepCopy = JSON.parse(JSON.stringify(user)); // 或使用库如 lodash.cloneDeep ``` ===== 2.8 作用域与闭包 ===== ==== 2.8.1 块级作用域详解 ==== ```typescript // 块级作用域示例 { let blockScoped = "I am in block"; const alsoBlockScoped = "Me too"; } // console.log(blockScoped); // Error: not defined // if 块级作用域 if (true) { let ifScoped = "if block"; } // for 块级作用域 for (let i = 0; i < 1; i++) { let forScoped = "for block"; } ``` ==== 2.8.2 闭包与变量捕获 ==== ```typescript // 使用 let 的正确闭包 function createCounters() { const counters = []; for (let i = 0; i < 3; i++) { counters.push(() => i); } return counters; } const counters = createCounters(); console.log(counters[0]()); // 0 console.log(counters[1]()); // 1 console.log(counters[2]()); // 2 // 对比:使用 var 的问题 function createCountersVar() { const counters = []; for (var i = 0; i < 3; i++) { counters.push(() => i); } return counters; } const countersVar = createCountersVar(); console.log(countersVar[0]()); // 3 console.log(countersVar[1]()); // 3 console.log(countersVar[2]()); // 3 ``` ===== 2.9 高级解构技巧 ===== ==== 2.9.1 解构结合类型别名 ==== ```typescript type Coordinates = [number, number, number?]; const parseLocation = (loc: string): Coordinates => { const [x, y, z] = loc.split(",").map(Number); return z !== undefined ? [x, y, z] : [x, y]; }; const [lat, lng, alt = 0] = parseLocation("39.9,116.3,100"); ``` ==== 2.9.2 解构与重命名 ==== ```typescript interface APIResponse { data: { user_name: string; // 蛇形命名 user_age: number; }; } const response: APIResponse = { data: { user_name: "Alice", user_age: 25 } }; // 解构时转换为驼峰命名 const { data: { user_name: userName, user_age: userAge } } = response; console.log(userName, userAge); // "Alice" 25 ``` ==== 2.9.3 解构默认值 ==== ```typescript interface Config { host?: string; port?: number; ssl?: boolean; } const defaultConfig: Required<Config> = { host: "localhost", port: 3000, ssl: false }; function createServer(userConfig: Config = {}) { const { host = defaultConfig.host, port = defaultConfig.port, ssl = defaultConfig.ssl } = userConfig; return { host, port, ssl }; } ``` ===== 2.10 本章小结 ===== 本章我们学习了: 1. **var、let、const 的区别** - 理解作用域、变量提升、重复声明的差异 2. **let 和 const 的最佳实践** - 默认使用 const,必要时使用 let 3. **解构赋值** - 从数组和对象中提取值 4. **展开运算符** - 复制、合并、扩展数组和对象 5. **类型注解结合** - 在解构和展开时保持类型安全 ===== 2.11 练习题 ===== ==== 练习 1:变量声明转换 ==== 将以下使用 var 的代码改写为使用 let 和 const,并解释为什么: ```typescript var name = "Alice"; var age = 25; for (var i = 0; i < 5; i++) { setTimeout(function() { console.log(i); }, 100); } var config = { apiUrl: "https://api.example.com", timeout: 5000 }; ``` <details> <summary>参考答案</summary> ```typescript const name = "Alice"; // 不会重新赋值 let age = 25; // 可能会改变 for (let i = 0; i < 5; i++) { // let 创建块级作用域 setTimeout(function() { console.log(i); // 正确输出 0, 1, 2, 3, 4 }, 100); } const config = { // 对象引用不变 apiUrl: "https://api.example.com", timeout: 5000 }; ``` **解释**: - name 和 config 不会改变引用,使用 const - age 可能会改变,使用 let - for 循环使用 let 确保每次迭代有独立的 i 绑定 </details> ==== 练习 2:解构赋值练习 ==== 使用解构赋值重写以下代码: ```typescript const user = { id: 1, profile: { firstName: "Alice", lastName: "Smith", address: { city: "Beijing", country: "China" } }, hobbies: ["reading", "coding", "gaming"] }; const firstName = user.profile.firstName; const city = user.profile.address.city; const firstHobby = user.hobbies[0]; ``` <details> <summary>参考答案</summary> ```typescript const { profile: { firstName, address: { city } }, hobbies: [firstHobby] } = user; // 或者分步解构 const { profile } = user; const { firstName, address: { city } } = profile; const [firstHobby] = user.hobbies; ``` </details> ==== 练习 3:展开运算符应用 ==== 完成以下函数,使用展开运算符实现功能: ```typescript // 1. 合并配置,用户配置优先级更高 function mergeConfig(defaultConfig: object, userConfig: object): object { // 实现 } // 2. 将数组元素插入到另一个数组的指定位置 function insertAt<T>(arr: T[], index: number, ...elements: T[]): T[] { // 实现 } // 3. 移除对象中的指定属性 function omit<T extends object, K extends keyof T>(obj: T, keys: K[]): Omit<T, K> { // 实现 } ``` <details> <summary>参考答案</summary> ```typescript // 1. 合并配置 function mergeConfig(defaultConfig: object, userConfig: object): object { return { ...defaultConfig, ...userConfig }; } // 2. 插入元素 function insertAt<T>(arr: T[], index: number, ...elements: T[]): T[] { return [...arr.slice(0, index), ...elements, ...arr.slice(index)]; } // 3. 移除属性 function omit<T extends object, K extends keyof T>(obj: T, keys: K[]): Omit<T, K> { const result = { ...obj }; for (const key of keys) { delete result[key]; } return result; } // 或使用更函数式的写法 function omit2<T extends object, K extends keyof T>(obj: T, keys: K[]): Omit<T, K> { const keySet = new Set(keys); return Object.fromEntries( Object.entries(obj).filter(([key]) => !keySet.has(key as K)) ) as Omit<T, K>; } ``` </details> ==== 练习 4:类型安全的配置解析 ==== 实现一个类型安全的配置解析器: ```typescript interface DatabaseConfig { host: string; port: number; username: string; password: string; database: string; ssl?: boolean; } // 实现 parseConfig 函数,从环境变量解析配置 // 使用解构和默认值 function parseConfig(env: NodeJS.ProcessEnv): DatabaseConfig { // 你的实现 } ``` <details> <summary>参考答案</summary> ```typescript function parseConfig(env: NodeJS.ProcessEnv): DatabaseConfig { const { DB_HOST = "localhost", DB_PORT = "5432", DB_USERNAME = "postgres", DB_PASSWORD = "", DB_DATABASE = "myapp", DB_SSL = "false" } = env; return { host: DB_HOST, port: parseInt(DB_PORT, 10), username: DB_USERNAME, password: DB_PASSWORD, database: DB_DATABASE, ssl: DB_SSL === "true" }; } // 使用示例 const config = parseConfig(process.env); ``` </details> ===== 扩展阅读 ===== - [[TypeScript:第三章_基本类型|下一章:基本类型]] - [[https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/let|MDN - let]] - [[https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/const|MDN - const]] - [[https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment|MDN - 解构赋值]]
typescript/第二章变量声明.txt
· 最后更改:
2026/02/03 19:45
由
127.0.0.1
页面工具
显示页面
过去修订
反向链接
回到顶部