ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

ArkTS变量与数据类型详解:从零基础到构建实用鸿蒙应用

ArkTS变量与数据类型详解:从零基础到构建实用鸿蒙应用 我们从搭建环境、运行起第一个Hello World之后终于要进入真正决定“能不能写出实用App”的核心环节了。变量和基本数据类型在鸿蒙应用开发里对应着ArkTS语言基于TypeScript的鸿蒙专用开发语言最基础的语法体系。这一节不会讲什么玄乎的理论就是直接用你可以照着敲的例子把这些每天都会用的东西彻底捋明白。学完之后你的“Hello World”就不再是死板的固定文字而是能随用户操作、随程序状态变化的活代码了。我见过很多零基础的朋友前期最容易被“变量”这个概念卡住。原因很简单它看不见摸不着不像UI界面能直接看到效果。我会尽量用生活化的场景把这块补课补到位。而且鸿蒙开发环境的编译器会给很多提示但如果你不理解背后的机制看到红色波浪线照样一头雾水。这篇内容就是来帮你把这些“为什么”补上的。不管你是看了前两节的系列老读者还是半路跳进来的新朋友只要你愿意动手敲代码这一节内容足够你消化好一阵。1. 变量程序里的“收纳盒”1.1 为什么变量是一切代码的起点先抛一个问题如果你要在代码里保存一个用户的姓名、一个购买金额或者一个开关状态你说这些数据该放哪答案是存放在内存里但你不能每次都去记“内存地址0x0012AB”这种鬼东西。于是编程语言给你提供一个包装层这就是变量。变量本质上是“一块有名字的内存空间”你通过名字读写它底层由编译器帮你翻译成真实的内存操作。在鸿蒙应用开发里你写页面、写业务逻辑、存用户输入几乎处处都要用变量。比如用户点了一个按钮你要记录点击次数这个次数就得存在一个变量里登录页面输入的手机号也要先存进变量才能拿去做校验和上传。所以变量不是某个特殊业务才用它是代码的最低层地基。有个特别重要的观念要纠正变量不仅仅是“存东西”。它还有“类型”约束、有“作用域”范围、有“生命周期”。不同类型能做的事不一样名字能生效的范围也不一样这些都直接决定了你的代码会不会报错。所以这一节我们不但要学怎么定义变量更要弄明白它的“脾气”。1.2 零基础最容易理解的变量比喻我给你一个特别直观的比喻变量就是一个贴了标签的收纳盒。标签是变量名盒子本身是内存空间盒子能装什么由数据类型决定。你在厨房里有一个贴着“调味料”的罐子你不会往里面塞电池。同样你把一个字符串存进一个声明为数字的变量编译器会直接提醒你类型不匹配。再细化一下你往盒子里装新东西旧东西就被替换掉了。这对应“重新赋值”。盒子能够被读取对应“取值”。盒子在哪个房间放着对应“作用域”——客厅的盒子厨房里拿不到代码里也一样。这个比喻虽然简单但后面你遇到的所有坑都能用它来解释。 小提示初学者最容易犯的错就是把“赋值”和“相等”混在一起。在代码里一个等号是“把右边装进左边”例如count 1两个等号才是“两边的值是否相等”例如count 1。这个习惯如果不在一开始就纠正后面写判断逻辑时会把自己坑到哭。2. ArkTS 定义变量的三种姿势let、const 与 var2.1 为什么要重点对比这三个关键字很多教程一上来就给你讲怎么声明变量但不会说清楚一件事为什么要分 let、const、var。尤其你之前如果看过一些老的 JavaScript 资料会发现大家都在用 var到了 ArkTS 里官方主推的是 let 和 constvar 反而不建议用。你不理解背后的原因就只能死记硬背。理解了这些东西一辈子忘不掉。var 是 JavaScript 早期就有的设计上比较随意。它有两个著名毛病一是没有严格的块级作用域比如在 if 里面定义了一个变量出了 if 居然还能用这跟人的直觉完全相反容易造成变量被意外修改二是它可以重复声明同样的名字定义了两次编译器不报错后一次覆盖前一次等你想定位问题的时候人都麻了。let 是 ES6 引入的更严谨版本它有严格的块级作用域出了花括号{}范围就访问不到同一个作用域内不允许重复声明。这是最适合写业务代码的声明方式。const 和 let 类似但它的意思是“常量”一旦赋了初值这个盒子的引用就不能再指向新东西了。能优先就优先用 const因为它能让你的代码更稳减少“这个变量怎么被改了”的烦恼。2.2 声明语法与类型标注在 ArkTS 中声明变量你可以显式写上类型也可以让编译器自己猜。标准写法长这样let count: number 0; let userName: string 张三; let isLogin: boolean false; const MAX_NUM: number 100;冒号后面写的number、string、boolean就是类型标注。你可以理解为“我是一个收纳盒我贴着只能装数字的标签”。在不写类型的情况下比如let count 0;编译器会通过你赋的初值推断出它是 number这叫做类型推断。ArkTS 推荐在业务代码里主动加上类型标注为什么因为当你拿到一个别人写的函数返回什么类型一目了然而如果你全程靠推断跨文件协作时很容易产生“这个变量到底是什么”的困惑。var 的写法在 ArkTS 中也能跑但编译器通常会给出提示让你尽量别用。ArkTS 整体比 JavaScript 严格不少它在设计上就希望代码是类型安全的所以 var 这种宽松老物件自然会被边缘化。尽量养成“默认用 const需要重新赋值才用 let永远别用 var”的肌肉记忆。这个习惯在大型项目里的价值比你想的大得多。2.3 命名规范起名字也是一门技术活变量名的约束分两个层次硬性规则和软性规范。硬性规则是编译器不让你过的包括不能用数字开头、不能用保留关键字比如if、for、class、只能使用字母、数字、下划线和美元符号。软性规范则是团队协作时的默契最常见的是小驼峰命名法。所谓小驼峰就是第一个单词全小写后面每个单词首字母大写比如userName、phoneNumber、orderStatus。如果你在一个项目里看到了user_name这种下划线风格也别太惊讶那是 Python 风格类语言的常见做法。但在鸿蒙应用开发里官方示例和主流开源项目基本都是小驼峰保持一致会更好。常量命名则有点不同。如果是一个模块级、全局性的“固定值”通常用全大写加下划线比如MAX_RETRY_COUNT、API_BASE_URL。这种全大写的写法一出现阅读理解成本直接降一个档次——别人一看就知道“哦这是常量不能随便改”。 实操心得命名里最忌讳的就是a、b、tmp、data这种“谁都看不明白”的名字。当年我自己写工具脚本时也爱这么干过了一个月回头一看完全不知道当时想表达什么。现在我的标准是如果一个变量名让人需要额外注释才能看懂那这名字本身就失败了。3. ArkTS 基本数据类型拆解你能存什么、能做什么3.1 字符串 string文本的“万能容器”字符串大概是你打交道最多的类型。用户昵称、商品名称、日志信息、接口返回的提示语全是字符串。定义字符串非常简单可以用单引号、双引号ArkTS 也支持模板字符串——反引号可以把你拼句子这件事简化掉。let name: string 张三; let city: string 北京; // 模板字符串用反引号包裹${变量} 可以直接嵌入 let message: string ${name}来自${city}; console.log(message); // 输出张三来自北京有个容易踩的坑字符串里面如果本身要包含引号你需要用转义字符\或者合理切换外部的引号。模板字符串对多行文本非常友好写一段带换行的长文本时比用\n拼接舒服太多了。字符串不是只能存文本它还有一堆内置方法可以操作文本比如length获取长度、indexOf查找位置、substring截取子串、split按分隔符拆分。这些方法我强烈建议你挨个去 DevEco Studio 里拿补全提示试着敲一遍敲完基本就不会忘。你只靠眼睛看是记不牢的。3.2 数字 number整数小数一把抓很多从 Java 或 C 转过来的朋友到了 ArkTS 会很惊讶怎么没有int、float、double的区分ArkTS 里只有一种数字类型就是number。无论整数还是小数全部用number表示。这简化了选项但也带来了精度问题这一点要小心。let integer: number 42; let decimal: number 3.14; let scientific: number 5e3; // 5000 let negative: number -10;number 有常见的运算加减乘除、取余、幂运算等。这些运算符的优先级跟数学里基本一致先乘除后加减但如果表达式复杂得让人怀疑人生最好加括号让阅读代码的人一眼看懂。浮点数精度问题不仅在 ArkTS 存在几乎所有编程语言都有。比如0.1 0.2你可能觉得是0.3实际算出来是0.30000000000000004。这是因为二进制无法精确表示某些十进制小数。当你做金额相关的业务时千万不能直接用浮点数进行加减要么换算成“分”整数要么使用专门的精准计算库否则对账对不上时你会心态爆炸。3.3 布尔值 boolean条件判断的基石布尔值只有两个true和false。它虽然简单却是所有逻辑判断的底层基础。用户的开关状态、是否登录、是否加载完成这些全都可以用布尔值表达。几乎所有的比较运算结果也是布尔值比如age 18返回一个布尔值。let isVip: boolean true; let canAccess: boolean false; if (isVip canAccess) { // 两个条件都为真才执行 }布尔值最常用的操作是逻辑组合与、或||、非!。这里提醒一句别把逻辑运算符和位运算符混在一起。是逻辑与是位与。平时业务代码里你用逻辑与就够了位运算用得少遇到的时候再单独查不迟。有一个新手必坑的地方if (isVip true)这种写法你在条件里用了一个等号结果是“赋值然后转成布尔”死都不会报错但语义完全错了。如果你曾经排查半天都找不到 bug八成就是这种不知不觉写出来的赋值。3.4 数组 array一串数据排排坐当你要存多个数据比如一个学生列表、一组饼图数据、多个商品 ID你就不能一个萝卜一个坑地定义十个变量了。这时候用数组。let scores: number[] [95, 87, 76, 99]; let names: Arraystring [张三, 李四, 王五];ArkTS 里数组有两种等价声明方式number[]和Arraynumber。因为语言脱胎于 TypeScript所以这两种写法都能用我个人的习惯是用T[]读起来更直观。访问数组里的元素用下标从 0 开始。也就是说第一个元素是scores[0]第二个是scores[1]。数组还提供了push追加元素、pop删除末尾、length获取长度、forEach遍历等常用方法这些都是开发里高频操作。后面我们写列表渲染其实就是把数组一个个展开画到界面上。刚学数组最容易碰到的问题是越界访问scores[5]但数组只有 4 个元素得到一个undefined。在 ArkTS 的严格模式下这类隐患不会消失只是编译器提醒得更早。所以拿索引取元素之前先确认长度。3.5 枚举 enum让一堆魔法数字“开口说话”枚举用于定义一组有名字的常量集合最常见的场景是状态值。比如订单状态你可以定义0是待支付、1是已支付、2是已取消但第二天你再写出一个if (status 1)大概率不知道 1 是什么含义。enum OrderStatus { PENDING 0, PAID 1, CANCELLED 2 }定义了枚举之后代码就变成了if (status OrderStatus.PAID)。含义一目了然还不容易写错后续加新状态也很方便。枚举在 ArkTS 中完全支持尤其适合用在状态管理、错误码、类型分类这些场景里。枚举本质上是一个编译期的映射默认情况下数字枚举反向映射也能拿到名字。不过业务上我们真的不用太关心它实现细节把它当成一道“名字保鲜层”就行。以后代码里如果出现一串意义不明的数字第一反应就应该是用枚举或常量给它个名字。3.6 联合类型与 null/undefined处理“不确定”的状态ArkTS 里还有个很重要的类型设计联合类型。简单来说一个变量可以是几种类型中的一种用|连接。比如接口返回的数据可能是字符串也可能是空let data: string | null null;这比任何时候都只能写一个类型更灵活。但灵活性也带来了约束你使用数据前得先判断它是哪种情况。比如上面的data如果不是null才能放心调字符串方法。ArkTS 的类型系统会在静态分析时盯着你这部分是真严格。null和undefined是两个容易混的概念。null是开发者主动表示“这里没有值”undefined更像是“这个变量根本还没被赋值”。平时写代码如果你要表达“暂无数据”推荐用空数组或者明确的枚举值而不是让变量裸奔为undefined。很多运行时 bug 都是默认值没有处理好导致的。4. 类型推断与类型转换编译器帮你省心也要留神4.1 什么是类型推断当变量声明时没有写类型标注ArkTS 编译器会根据初始值自动推导类型。比如let count 42; // 自动推断为 number let title 你好; // 自动推断为 string let isOpen true; // 自动推断为 boolean这种写法代码更简洁日常完全可以用。但在业务复杂度上去之后显式标注能减少阅读成本。尤其当一个变量是函数参数或返回值时没有类型标注对方就不知道该怎么传参。我的个人建议很简单局部变量可以用推断公共接口函数、类成员必须显式标注。这个习惯兼顾了代码简洁和可维护性。4.2 类型转换的正确姿势有时候你确实需要把一个类型转成另一个类型。最常见的有两种场景用户从输入框拿到的是字符串你要参与数学运算需要转成数字后端返回的状态码是数字你需要转成字符串展示在 UI 上。字符串转数字ArkTS 里有Number()函数或者parseInt、parseFloatlet str: string 123; let num: number Number(str); // 123 let intNum: number parseInt(str); // 123 let floatNum: number parseFloat(3.14); // 3.14数字转字符串方法是String()或者.toString()let num: number 256; let str: string String(num); // 256 let str2: string num.toString(); // 256真正的坑出现在把字符串“硬生生”转数字的场景。你想把12px转成数字直接Number(12px)会得到NaNNot a Number但parseInt(12px)能得到 12因为它能解析到第一个非数字字符为止。这两个函数的行为不一样你得按场景选。另一个典型的坑是数字和字符串相遇时会触发隐式转换。10 5的结果是字符串105不是 15。因为 JavaScript/TypeScript 里遇字符串会优先拼接。理解这一点你就知道为什么有时候明明算了半天结果却是“一行字符串”了。 实操心得所有类型的强制转换建议先在官方文档确认行为尤其注意边界输入。DevEco Studio 的智能提示可以查到方法说明多看一眼能省下调试时间。5. 变量作用域与生命周期这段代码能不能访问到它5.1 作用域是怎么划分的作用域决定了变量“出生之后在哪些范围内可以被看见”。ArkTS 沿用了 TypeScript 的规则花括号{}内的块级作用域。最常见的是函数体、if 分支、for 循环这些都是独立作用域。function test() { let a 1; if (true) { let b 2; console.log(a); // 可以外层的里面能访问 } console.log(b); // 报错b 只活在 if 花括号内 }简单记外层不能访问内层内层可以访问外层。这像阳光照进房间房间里的东西你在屋外看不见但屋里的人能看到屋外的太阳。作用域带来的实际意义是你可以放心地给多个函数各自定义同名的局部变量它们互不影响。比如两个函数里都有let temp 0;它们在各自函数范围内是独立的两份不存在冲突。5.2 let 与 const 的块级作用域 vs var前面提到var缺少块级作用域这里展开说。如果你用var定义了变量即使写在 if 块里面它也会“泄漏”到整个函数范围甚至出现重复声明。这在代码行数不多时看不出问题但页面一复杂变量名冲突和意外覆盖会频繁发生。我用if举例function demo(flag: boolean) { if (flag) { var leaked 我能出去; } console.log(leaked); // var 时不会报错但这是隐患 }如果是let上面这句console.log(leaked)编译直接不通过报‘Cannot find name’错误。你可能会说“报错多麻烦”但正是这种报错保护了你让你不在 10 个文件里满大街找变量被谁改了。这里需要额外说一句ArkTS 对var的容忍度比网上的传统 TypeScript 低很多静态检查器会把var标成警告。不要和编译器对着干老老实实用 let / const你的代码质量会高出一截。5.3 生命周期与全局变量变量的生命周期就是它从内存中被创造到被回收的区间。局部变量在函数执行完就没了模块顶层声明的变量生命周期基本伴随整个程序的运行。在鸿蒙应用里谁活得久就代表谁能被谁记住。比如你横竖屏切换页面会重新创建局部变量全复位而应用级别的全局状态需要通过状态管理框架来共享不能用普通变量硬撑。我不太建议在鸿蒙项目的顶层随意放一堆全局变量。因为 ArkTS 默认的模块作用域机制里跨文件共享变量需要显式导出引入逻辑才清晰。你要是放了一堆裸奔的全局变量会陷入“这个值到底从哪来、被谁改了”的脑壳疼。想共享状态应该配合 State、Prop 等装饰器来做这也是后续系列里一定会展开的内容。6. 常见报错与排查技巧实录6.1 DevEco Studio 高频报错速查我在教零基础学员的过程里收集了一堆高频报错。整理成一张表可以帮你快速定位。报错信息常见原因解决方案Cannot find name xxx变量未定义或超出作用域检查变量是否在访问范围内检查拼写Type string is not assignable to type number类型不匹配检查赋值变量类型需要时先做类型转换Block-scoped variable xxx used before its declaration在声明前使用了变量调整变量定义位置Argument of type string is not assignable to parameter of type number函数传参类型错误检查函数签名确认类型要求Property xxx does not exist on type number对错误类型调用了方法确认变量的真实类型可能是联合类型Unexpected token语法错误检查括号是否配对、引号是否缺失看到“Cannot find name”时新手第一反应是我代码怎么写的其实这些问题 90% 是拼写或者作用域位置导致的。我的排查步骤很固定先看变量有没有在当前文件上方定义再看是不是被花括号圈住了最后看是不是名字大小写差一位。6.2 三个实战排查案例先说一个最经典的Cannot find name userName。学员明明在上面定义了let userName 张三但代码就是报错。我一看他定义的位置在文件末尾使用的地方在文件开头。虽然同一个文件也不是哪里都能提前访问的变量必须先声明后使用。调整位置后马上就好了。第二个案例把string类型当成number用。学员从输入框拿到的是一个字符串类型的值直接跟数字相加结果得到拼接串。排查思路是先打印这个变量的类型和值用console.log(typeof value, value)。发现是字符串后用Number()转一次就正常了。这类问题在开发里太常见了也是为什么要讲类型转换那一节的现实原因。第三个案例修改了数组里的对象属性界面没反应。这不一定是变量的问题而可能是数据状态管理的问题——你需要用 State 装饰器把数据变成响应式的普通变量改了 UI 不感知。以后讲状态管理会细说。但你现在要知道变量和数据绑定、UI 刷新是完全不同的层面普通变量没法做到“改值就刷新页面”。6.3 系统性排查思路当你遇到任何和变量相关的编译错误或运行错误我建议按这个顺序查先看编译器红波浪线给的具体行号。报错位置不一定很精确但可以帮助缩小范围。顺着这个变量往回找它的首次声明。确认声明方式、位置、类型。console.log大法把变量值打出来看。这一招在运行期问题里非常管用。查变量是否被其他代码意外修改。尤其是你用 var 或者全局变量的时候。不要凭感觉改代码先复述一遍这个变量的生命周期再动手。这五步下来超过九成的变量问题都能解决。剩下那一成多半是和系统框架状态管理机制有关那就需要跳到状态管理章节去补课了。写在最后回想我自己折腾 ArkTS 的头几周印象最深的不是某个 API 怎么用而是无数次因为“变量的类型没弄对”而产生的挫败感。这种挫败感是完全正常的。你现在花的时间都是在为后续每一行业务代码铺路。等你真正写出一个带交互界面的小应用回头再来看变量和类型大概率会觉得这节内容怎么这么啰嗦——但那一刻你也明白了地基这东西从来不白打。最后再分享一个我教课时的习惯每学一个新的数据类型或者语法点不要只在页面里声明一下一定要让它在界面上留下痕迹。比如定义完字符串就把Text组件的文本设为这个变量定义完数组就试着用循环把它渲染成一个列表。这样你才能真切感到“这个类型跟我做的事有关系”。变量和类型不是考点是你以后所有页面逻辑都能跑起来的基本动力。把你手头已有的 Hello World 工程打开试着把刚才那些语法挨个打一遍编译没过就按 6.1 的表去查查完继续改后面系列见。
返回列表