零基础鸿蒙应用开发第十五节:变量的作用域与生命周期 【学习目标】理解全局、局部函数、块级三类作用域的核心边界规则能精准判断变量的可访问范围掌握变量的完整生命周期创建→存活→销毁结合函数执行流程理解变量的“存活时长”揭秘作用域链的底层查找逻辑规避“变量找不到、重复定义、变量泄露”等新手高频坑掌握ArkTS对作用域/生命周期的核心规范写出规范且安全的代码能结合函数场景分析变量的作用域与生命周期从源头避免运行时错误【学习重点】作用域规则全局→局部→块级的访问边界及优先级规则同名变量时内层优先生命周期变量“创建初始化→存活可访问→销毁释放内存”的完整流程作用域链变量查找的“从内到外”规则及变量遮蔽不同作用域同名变量覆盖问题实战避坑循环变量泄露、变量遮蔽、全局变量泛滥等常见问题一、工程结构本节我们将创建名为ScopeLifecycleDemo的工程基于**鸿蒙5.0API12开发使用DevEco Studio 6.0**工具项目结构目录如下ScopeLifecycleDemo/ ├── entry/ │ ├── src/ │ │ ├── main/ │ │ │ ├── ets/ │ │ │ │ ├── entryability/ # 应用入口默认生成无需修改 │ │ │ │ │ └── EntryAbility.ets │ │ │ │ ├── pages/ # 页面代码含UI逻辑调用 │ │ │ │ │ └── Index.ets # 核心页面作用域测试 │ │ │ │ └── utils/ # 工具函数目录 │ │ │ │ └── ScopeTest.ets # 作用域/生命周期核心代码 │ │ │ ├── resources/ # 资源文件默认生成无需修改 │ │ │ └── module.json5 # 模块配置默认生成无需修改二、作用域变量的“访问边界”作用域Scope定义了变量的可访问范围简单说就是“变量能在哪些代码区域被读取/修改”。ArkTS中核心分为三类作用域优先级同名变量时内层优先块级作用域 局部函数作用域 全局作用域。2.1 全局作用域整个应用“可见”定义在所有函数、代码块外部声明的变量属于全局作用域生命周期应用启动时创建应用退出时销毁存活时间最长核心规范尽量少用全局变量易导致命名冲突、内存泄露必须使用时加统一前缀如APP_全局作用域示例代码// utils/ScopeTest.ets// 全局常量不可修改全应用可见constAPP_VERSION:string1.0.0;// 全局变量可修改全应用可见尽量少用letAPP_USER_COUNT:number0;// 测试函数1访问并修改全局变量functiontestGlobalVariable():void{console.log([全局作用域] 应用版本${APP_VERSION});// 输出[全局作用域] 应用版本1.0.0APP_USER_COUNT1;console.log([全局作用域] 当前用户数${APP_USER_COUNT});// 输出[全局作用域] 当前用户数1}// 测试函数2验证全局变量的跨函数访问functiontestGlobalCrossFunction():void{console.log([全局作用域] 跨函数访问用户数${APP_USER_COUNT});// 输出[全局作用域] 跨函数访问用户数1}// 错误示例注释掉避免编译报错 // 错误同一全局作用域重复声明同名变量编译报错标识符已声明// const APP_VERSION: string 2.0.0;// 不推荐全局变量命名无前缀易冲突不报错但规范不建议// let userCount: number 0; // 建议改为 APP_USER_COUNT2.2 局部函数作用域仅函数内部“可见”定义在函数/箭头函数内部声明的变量属于局部作用域生命周期函数开始执行时创建函数执行结束后销毁核心规则函数外部无法访问函数内部的局部变量不同作用域的同名变量互不影响内层遮蔽外层同一函数作用域内禁止重复声明同名变量局部作用域示例代码// utils/ScopeTest.ets// 全局变量用于对比letglobalNum:number100;// 局部作用域核心逻辑functiontestLocalScope():void{// 局部变量仅当前函数内可见letlocalUserName:string张三;constlocalUserAge:number25;console.log([局部作用域] 用户名${localUserName}年龄${localUserAge});// 输出[局部作用域] 用户名张三年龄25localUserName李四;console.log([局部作用域] 修改后用户名${localUserName});// 输出[局部作用域] 修改后用户名李四// 局部变量遮蔽全局变量letglobalNum:number200;// 局部变量和全局变量同名console.log([局部作用域] 局部变量globalNum${globalNum});// 输出[局部作用域] 局部变量globalNum200优先访问局部变量}// 验证局部变量外部不可访问functiontestLocalAccessOutside():void{// 错误函数外部访问其他函数的局部变量编译报错找不到名称// console.log(localUserName);// 读取全局变量未被局部变量影响console.log([局部作用域] 全局变量globalNum${globalNum});// 输出[局部作用域] 全局变量globalNum100}// 局部作用域错误示例functiontestLocalError():void{// 错误同一函数作用域内重复声明同名变量ArkTS编译报错标识符已声明// let localNum: number 10;// let localNum: number 20;// 错误尝试访问箭头函数内部的局部变量编译报错找不到名称arrowVarconstarrowFunc(){letarrowVar:string箭头函数变量;};// console.log(arrowVar);}2.3 块级作用域仅代码块内部“可见”定义在{}包裹的代码块内声明的变量if/for/while/switch等属于块级作用域生命周期代码块开始执行时创建代码块执行结束后销毁块级作用域示例代码iffor综合// utils/ScopeTest.etsfunctiontestBlockScope():void{letouterVar:string外部变量;// 1. if块级作用域if(true){letinnerVar:stringif内部变量;console.log([块级作用域] if内部${outerVar}${innerVar});// 输出[块级作用域] if内部外部变量 if内部变量outerVar外部变量已修改;}console.log([块级作用域] if外部${outerVar});// 输出[块级作用域] if外部外部变量已修改// 错误访问if块内的变量编译报错找不到名称// console.log(innerVar);// 2. for循环块级作用域for(leti:number0;i3;i){// 延迟执行验证每次循环的i是独立的setTimeout((){console.log([块级作用域] 循环i${i});// 输出0 → 1 → 2let声明的块级变量特性},100);}// 错误访问循环块内的变量编译报错找不到名称// console.log(i);// var导致变量泄露的原因示例TypeScript场景// 说明ArkTS中直接禁用var编译报错此处仅演示var的问题// for (var j: number 0; j 3; j) {}// console.log(j); // var无块级作用域变量泄露到外部输出3易引发逻辑错误}2.4 作用域核心规则总结作用域类型声明位置可访问范围生命周期核心规范全局作用域所有函数/块外部整个应用应用启动→退出1. 少用加统一前缀2. 禁止重复声明局部作用域函数/箭头函数内部仅函数内部函数执行→结束1. 禁止重复声明2. 推荐显式声明类型块级作用域{}代码块内部仅代码块内部代码块执行→结束1. 禁止重复声明2. var无块级作用域易泄露ArkTS直接禁用三、生命周期变量的“存活时长”变量的生命周期Lifecycle是指变量从“创建分配内存→存活可访问→销毁释放内存”的完整过程其时长完全由作用域决定。3.1 生命周期三阶段创建阶段变量声明并初始化分配内存存活阶段变量可被读取/修改处于作用域内销毁阶段变量超出作用域内存被释放3.2 生命周期综合示例局部块级// utils/ScopeTest.ets// 全局变量生命周期 应用生命周期constGLOBAL_LIFETIME:string全局变量-永久存活;functiontestLifecycleComprehensive():void{console.log(\n[生命周期] 函数执行开始 → 局部变量创建);// 1. 局部变量生命周期函数级letlocalVar:string函数内局部变量;console.log([生命周期] 局部变量存活${localVar});localVar局部变量-已修改;console.log([生命周期] 局部变量存活修改后${localVar});// 2. 块级变量生命周期循环块for(letk:number0;k2;k){console.log(\n[生命周期] 循环${k}开始 → 块级变量创建);letblockVar:string循环块变量-${k};console.log([生命周期] 块级变量存活${blockVar});console.log([生命周期] 循环${k}结束 → 块级变量销毁);}console.log(\n[生命周期] 函数执行结束 → 局部变量销毁);}// 验证全局变量生命周期functiontestGlobalLifecycle():void{console.log(\n[生命周期] 全局变量仍存活${GLOBAL_LIFETIME});// 输出[生命周期] 全局变量仍存活全局变量-永久存活}// 生命周期错误示例functiontestLifecycleErrors():void{// 错误1访问已销毁的局部变量函数执行完毕后内部变量被销毁编译报错consttempFunc(){lettempVar:number10;};tempFunc();// 执行后tempVar已销毁// console.log(tempVar);// 错误2块级作用域变量泄露访问编译报错找不到名称blockVarif(true){letblockVar:string块级变量;}// console.log(blockVar);}四、作用域链变量的“查找规则”当代码访问一个变量时ArkTS会按照“从内到外”的顺序查找变量这个查找链条就是作用域链。4.1 作用域链核心规则优先查找当前作用域的变量找到则使用未找到则向上一级作用域查找直到全局作用域若仍未找到则编译报错不同作用域的同名变量会“遮蔽”上一级作用域的变量优先访问内层变量同一作用域内重复声明同名变量会直接编译报错不存在遮蔽问题。4.2 作用域链示例// utils/ScopeTest.ets// 全局作用域变量letchainVar:string全局层变量;// 外层函数第一层作用域functionouterFunc():void{// 外层函数变量遮蔽全局变量letchainVar:string外层函数变量;// 内层箭头函数第二层作用域constinnerFunc():void{// 内层函数变量遮蔽外层函数变量letchainVar:string内层函数变量;console.log([作用域链] 内层访问${chainVar});// 输出[作用域链] 内层访问内层函数变量};innerFunc();console.log([作用域链] 外层访问${chainVar});// 输出[作用域链] 外层访问外层函数变量}// 验证全局变量未被遮蔽影响functiontestGlobalChain():void{console.log([作用域链] 全局访问${chainVar});// 输出[作用域链] 全局访问全局层变量}// 变量遮蔽避坑示例functiontestShadowingBestPractice():void{// 推荐不同作用域变量命名表意化避免遮蔽letglobalUserCount:number100;constfuncUserCount():void{letfuncUserCount:number200;// 命名区分全局变量if(true){letblockUserCount:number300;// 命名区分函数变量console.log([避坑] 块级变量${blockUserCount});// 输出[避坑] 块级变量300}console.log([避坑] 函数变量${funcUserCount});// 输出[避坑] 函数变量200};funcUserCount();console.log([避坑] 全局变量${globalUserCount});// 输出[避坑] 全局变量100}五、统一测试调用代码5.1 测试入口函数// utils/ScopeTest.ets// 统一测试入口仅调用不整合函数逻辑 exportfunctionallScopeTests():void{console.log( 全局作用域测试 );testGlobalVariable();testGlobalCrossFunction();console.log(\n 局部作用域测试 );testLocalScope();testLocalAccessOutside();testLocalError();console.log(\n 块级作用域测试 );testBlockScope();console.log(\n 生命周期测试 );testLifecycleComprehensive();testGlobalLifecycle();testLifecycleErrors();console.log(\n 作用域链测试 );outerFunc();testGlobalChain();testShadowingBestPractice();console.log(\n✅ 所有测试执行完成);}5.2 页面调用代码Index.ets// pages/Index.etsimport{allScopeTests}from../utils/ScopeTest;EntryComponentstruct Index{aboutToAppear(){allScopeTests();// 调用统一测试函数}build(){Column({space:20}){// 标题Text(变量作用域与生命周期测试).fontSize(32).fontWeight(FontWeight.Bold).textAlign(TextAlign.Center).width(100%);}.width(100%).height(100%).justifyContent(FlexAlign.Center);}}六、核心总结作用域核心全局/局部/块级作用域边界清晰同名变量遵循“内层遮蔽外层”规则同一作用域内禁止重复声明变量生命周期核心变量存活时长由作用域决定全局变量随应用存活局部/块级变量随函数/代码块执行结束销毁作用域链核心变量查找“从内到外”未找到则编译报错命名表意化可避免变量遮蔽问题避坑核心少用全局变量加统一前缀、循环变量用let声明、不同作用域变量命名差异化从源头规避泄露和遮蔽问题。七、代码仓库工程名称ScopeLifecycleDemo仓库地址https://gitee.com/juhetianxia321/harmony-os-code-base.git八、下节预告下一节将进入闭包的核心原理与基础应用这是作用域/生命周期的进阶应用——我们将揭秘闭包“让局部变量突破函数生命周期”的底层逻辑掌握闭包在状态管理、防抖节流等场景的核心应用同时理解对闭包的内存管理规范让你既能用好闭包又能规避闭包导致的内存泄露问题零基础鸿蒙应用开发第十五节变量的作用域与生命周期