散修带你入门鸿蒙应用开发基础第四节:核心内置对象与集合

ArkTS基础第四节:核心内置对象与集合

炼气四重天

【学习目标】

  1. 理解数组、集合(Map/Set)、元组、Record的核心概念,明确其适用场景
  2. 掌握 NumberStringArray 的常用属性与方法,规避数组越界等常见错误
  3. 理解“原始类型”与“对象类型”的关系(如 numberNumber
  4. 掌握元组(Tuple)的定义与使用,明确其与普通数组的差异
  5. 掌握 MapSet 集合及 Record 类型工具的使用场景与方法,能安全处理Map key不存在、Set无索引等场景

【学习重点】

  • 核心概念:数组(动态有序列表)、元组(固定结构数组)、Map(键值对集合)、Set(唯一元素集合)、Record(强类型静态结构)
  • 核心内置对象:String 字符串处理、Array 数组操作(含越界规避)
  • 特殊数组:元组(Tuple)的固定长度/类型约束与使用场景
  • 数值工具:Number 类型转换与格式化
  • 集合类型:Map(键值对集合,含key不存在处理)、Set(唯一元素集合)的使用
  • 类型工具:Record(固定结构对象类型约束)的使用
  • 实战技巧:结合内置对象简化重复逻辑(数组去重、数据筛选等)、各类容器的避坑方法

【代码存放路径】

本节项目工程名称:BuiltInObjectsDemo,所有示例代码均存放于项目 ets/utils/BuiltInObjectsTest.ets 文件中,可直接复制到鸿蒙项目中运行调试。此前课程已详细讲解过鸿蒙工程创建、工具类文件新建的完整流程,且大家已掌握相关操作,因此本节课不再重复提示工程与文件的创建步骤。

数据容器类型介绍

数组/集合/Record是日常开发中最常用的“数据容器”,核心差异在于“数据组织方式”和“访问规则”:数组按索引有序存储,Map按键值映射存储,Set按唯一元素存储,元组是数组的“强约束版本”,Record是强类型静态结构,掌握这些差异能大幅减少代码BUG。

一、原始类型与内置对象的关系

原始类型numberstringboolean 等)是数据的基本形态,而 NumberStringArray 等是对应的内置对象,提供丰富工具方法操作原始类型数据。

ArkTS 会自动完成“原始类型→内置对象”的隐式转换,因此原始类型可直接调用对应内置对象的方法,执行后自动还原为原始类型。

// 原始类型(存储具体值)
let num: number = 123;
let str: string = "hello";

// 直接调用内置对象的方法(ArkTS自动转换)
console.log(`num保留2位小数:${num.toFixed(2)}`); // 输出:num保留2位小数:123.00
console.log(`str长度:${str.length}`); // 输出:str长度:5

二、常用内置对象及方法

2.1 Number:数值处理工具

Number 对象用于处理数值类型数据,核心用法如下:
类型转换: 将字符串、布尔值等类型转换为数值,无效转换返回 NaN(非数字)。

// 字符串转数值(有效)
console.log(`字符串转数值:${Number("123")}`); // 输出:123
// 字符串转数值(无效)
console.log(`无效转换:${Number("123abc")}`); // 输出:NaN
// 布尔值转数值
console.log(`布尔值转数值:${Number(true)}`); // 输出:1
console.log(`布尔值转数值:${Number(false)}`); // 输出:0

保留小数: toFixed(n)保留指定位数的小数,返回字符串类型,注意浮点数精度偏差问题。

const pi = 3.1415926;
// 保留2位小数
console.log(`保留2位小数:${pi.toFixed(2)}`); // 输出:3.14
// 保留0位小数(取整)
console.log(`保留0位小数:${pi.toFixed(0)}`); // 输出:3

// 注意:浮点数精度偏差
const num1 = 0.15;
console.log(`0.15保留1位小数:${num1.toFixed(1)}`); // 输出:0.1(非预期的0.2)

判断整数: Number.isInteger(value)静态方法,仅判断原始数值是否为整数,非数值类型直接返回 false

// 整数判断
console.log(`5是整数:${Number.isInteger(5)}`); // 输出:true
console.log(`5.5是整数:${Number.isInteger(5.5)}`); // 输出:false
// 非数值类型判断
console.log(`字符串'5'是整数:${Number.isInteger("5")}`); // 输出:false

判断NaN: Number.isNaN(value)
静态方法,仅对原始类型 numberNaN 生效,比全局 isNaN 更精准。

console.log(`NaN判断:${Number.isNaN(NaN)}`); // 输出:true
console.log(`字符串'NaN'判断:${Number.isNaN("NaN")}`); // 输出:false
console.log(`123是否为NaN:${Number.isNaN(123)}`); // 输出:false

2.2 String:字符串处理工具

String 对象是处理字符串的核心工具,核心用法如下:

获取长度: length获取字符串的字符个数,包含空格、符号等所有字符。

const str1 = "hello";
console.log(`str1长度:${str1.length}`); // 输出:5

// 包含空格的字符串
const strWithSpace = "  hi!  ";
console.log(`带空格字符串长度:${strWithSpace.length}`); // 输出:6

查找子串: indexOf(str) 查找子串首次出现的索引(从0开始),找不到返回 -1,区分大小写。

const str2 = "Hello ArkTS";
// 区分大小写查找
console.log(`'Ark'索引:${str2.indexOf("Ark")}`); // 输出:6
console.log(`'ark'索引:${str2.indexOf("ark")}`); // 输出:-1

// 不区分大小写查找技巧
const targetSubStr = "ark";
const ignoreCaseIndex = str2.toLowerCase().indexOf(targetSubStr.toLowerCase());
console.log(`不区分大小写查找'ark':${ignoreCaseIndex}`); // 输出:6

截取子串: slice(start, end)左闭右开截取子串(包含start,不包含end),支持负数索引(从末尾倒数)。

const str3 = "hello";
// 截取索引1到3的字符(不包含3)
console.log(`截取1-3:${str3.slice(1, 3)}`); // 输出:el
// 负数索引:倒数3位
console.log(`倒数3位:${str3.slice(-3)}`); // 输出:llo
// 从索引2开始截取到末尾
console.log(`从索引2开始:${str3.slice(2)}`); // 输出:llo

拆分数组: split(sep)按指定分隔符将字符串拆分为数组,支持限制拆分次数。

const str4 = "a,b,c,d";
// 全部分拆
console.log(`全部分拆:${str4.split(",")}`); // 输出:a,b,c,d
// 限制拆分次数(只拆前2个)
console.log(`拆分2次:${str4.split(",", 2)}`); // 输出:a,b
// 按空字符拆分(单个字符)
console.log(`单个字符拆分:${"abc".split("")}`); // 输出:a,b,c

去除空格: trim()去除字符串首尾的空格,不影响中间的空格。

const str5 = "  hello world  ";
console.log(`去除空格后:${str5.trim()}`); // 输出:hello world

大小写转换: toUpperCase/toLowerCase

const str6 = "Hello";
// 转大写
console.log(`转大写:${str6.toUpperCase()}`); // 输出:HELLO
// 转小写
console.log(`转小写:${str6.toLowerCase()}`); // 输出:hello

判断包含: includes(substr)判断字符串是否包含指定子串,返回布尔值,区分大小写。

const str7 = "HarmonyOS 开发";
console.log(`是否包含"开发":${str7.includes("开发")}`); // 输出:true
console.log(`是否包含"鸿蒙":${str7.includes("鸿蒙")}`); // 输出:false

替换子串: replace(oldStr, newStr)替换指定子串,支持正则表达式实现全局替换。

const str8 = "ArkTS 很强大,ArkTS 很易用";
// 替换首次出现的子串
console.log(`替换首次:${str8.replace("ArkTS", "ArkTS 4.0")}`); // 输出:ArkTS 4.0 很强大,ArkTS 很易用
// 全局替换(正则/g)
console.log(`全局替换:${str8.replace(/ArkTS/g, "ArkTS 4.0")}`); // 输出:ArkTS 4.0 很强大,ArkTS 4.0 很易用

2.3 Array:数组操作工具

核心用法

获取长度: length获取数组元素个数,推荐使用 类型[] 写法声明数组。

// 推荐写法:number类型数组
let arr1: number[] = [1, 2, 3];
// let arr2:Array<number> = new Array(1,2,3) // 等价
console.log(`arr1长度:${arr1.length}`); // 输出:3

末尾添加 push(item)
向数组末尾添加元素,直接修改原数组,返回新数组长度。

const arr2 = [1];
// 添加元素2、3
const newLen = arr2.push(2, 3);
console.log(`添加后数组:${arr2}`); // 输出:1,2,3
console.log(`新数组长度:${newLen}`); // 输出:3

末尾删除 pop()
删除数组最后一个元素,直接修改原数组,返回被删除的元素。

const arr3 = [1, 2, 3];
// 删除最后一个元素
const deletedItem = arr3.pop();
console.log(`删除后数组:${arr3}`); // 输出:1,2
console.log(`被删除元素:${deletedItem}`); // 输出:3

截取子数组 slice(start, end)
左闭右开截取子数组,不修改原数组,返回新数组。

const arr4 = [1, 2, 3, 4];
// 截取索引0到2的元素(不包含2)
const sliceArr = arr4.slice(0, 2);
console.log(`截取的子数组:${sliceArr}`); // 输出:1,2
console.log(`原数组(无变化):${arr4}`); // 输出:1,2,3,4

增删元素 splice(start, len)
删除/添加数组元素,直接修改原数组,返回被删除的元素数组。

const arr5 = [1, 2, 3, 4];
// 从索引1开始,删除1个元素
const deletedArr = arr5.splice(1, 1);
console.log(`被删除元素:${deletedArr}`); // 输出:2
console.log(`修改后数组:${arr5}`); // 输出:1,3,4

slice vs splice 核心区别

const arrCompare = [1,2,3,4];
// slice:不修改原数组
const sliceArr2 = arrCompare.slice(0,2);
console.log(`slice截取结果:${sliceArr2}`); // 输出:1,2
console.log(`slice后原数组:${arrCompare}`); // 输出:1,2,3,4(无变化)

// splice:修改原数组
const spliceArr = arrCompare.splice(0,2);
console.log(`splice删除结果:${spliceArr}`); // 输出:1,2
console.log(`splice后原数组:${arrCompare}`); // 输出:3,4(已修改)

拼接字符串 join(sep)
用指定分隔符将数组元素拼接为字符串。

const arr6 = [1, 2, 3];
// 用连字符分隔
console.log(`连字符分隔:${arr6.join("-")}`); // 输出:1-2-3
// 无分隔符拼接
console.log(`无分隔符:${arr6.join("")}`); // 输出:123
// 用中文逗号分隔
console.log(`中文逗号分隔:${arr6.join("、")}`); // 输出:1、2、3

判断包含 includes(item)
判断数组是否包含指定元素,返回布尔值。

const arr8 = [1,2,3,4];
console.log(`是否包含3:${arr8.includes(3)}`); // 输出:true
console.log(`是否包含5:${arr8.includes(5)}`); // 输出:false

拼接数组 concat(arr1, arr2...)
拼接多个数组,不修改原数组,返回新数组。

const arr9 = [1,2];
const arr10 = [3,4];
// 拼接arr9、arr10和新数组[5,6]
const mergedArr = arr9.concat(arr10, [5,6]);
console.log(`拼接后数组:${mergedArr}`); // 输出:1,2,3,4,5,6
console.log(`原数组(无变化):${arr9}`); // 输出:1,2

循环遍历(for循环)
用普通for循环遍历数组所有元素。

const numbers: number[] = [1, 2, 3, 4, 5];
console.log("数组遍历结果:");
for (let i = 0; i < numbers.length; i++) {
  console.log(`第${i+1}个元素:${numbers[i]}`); 
  // 依次输出:1、2、3、4、5
}

实战场景:处理用户列表数据

// 声明用户类型(简化版,仅基础属性)
interface User = {
  name: string;
  age: number;
};

// 用户列表数据
const users: User[] = [
  { name: "张三", age: 18 },
  { name: "李四", age: 22 },
  { name: "王五", age: 17 }
];

// 筛选成年用户(年龄≥18)
const adults: User[] = [];
for (let i = 0; i < users.length; i++) {
  if (users[i].age >= 18) {
    adults.push(users[i]);
  }
}
console.log(`成年用户:${JSON.stringify(adults)}`); 
// 输出:[{"name":"张三","age":18},{"name":"李四","age":22}]

// 提取成年用户姓名并拼接
const adultNames: string[] = [];
for (let i = 0; i < adults.length; i++) {
  adultNames.push(adults[i].name);
}
console.log(`成年用户姓名:${adultNames.join("、")}`); 
// 输出:张三、李四

关键避坑:数组越界访问

什么是数组越界?
访问数组不存在的索引(索引≥数组长度 或 负数索引未处理),称为“越界访问”。

  • 普通数组:越界访问编译期不报错,运行时返回 undefined,易导致后续逻辑异常;
  • 元组:越界访问编译期直接报错,从根源规避风险(核心优势)。

越界场景与风险

const arr: number[] = [1, 2, 3]; // 长度3,有效索引:0、1、2

// 场景1:索引≥长度(越界)
console.log(arr[3]); // 输出:undefined(编译不报错,运行时异常)

// 场景2:负数索引未处理(越界)
console.log(arr[-1]); // 输出:undefined(编译不报错)

// 风险:越界返回undefined,参与运算会导致NaN
const sum = arr[0] + arr[3]; // 1 + undefined = NaN
console.log(sum); // 输出:NaN

规避方法

  1. 访问前校验索引合法性;
  2. 遍历数组优先用 for 循环(自动限制索引范围);
  3. 结构化固定数据优先用元组(编译期防越界)。
const arr: number[] = [1, 2, 3];
const targetIndex = 3;

// 方法1:索引合法性校验
if (targetIndex >= 0 && targetIndex < arr.length) {
  console.log(`合法访问:${arr[targetIndex]}`);
} else {
  console.log(`索引${targetIndex}越界,数组长度:${arr.length}`); // 输出:索引3越界,数组长度:3
}

// 方法2:优先用for循环遍历(无越界风险)
for (let i = 0; i < arr.length; i++) {
  console.log(`索引${i}:${arr[i]}`); // 仅访问0/1/2,无越界
}

// 方法3:结构化数据用元组(编译期防越界)
const tuple: [number, number, number] = [1, 2, 3];
// console.log(tuple[3]); // 编译直接报错:索引3超出元组长度3

2.4 元组(Tuple):固定长度/类型的数组

核心语法

// 定义:[类型1, 类型2, ...](指定每个位置的类型)
let 元组名: [类型1, 类型2, 类型3] = [值1, 值2, 值3];

基础示例

// 定义元组:第1位string(姓名)、第2位number(年龄)、第3位boolean(是否成年)
let userInfo: [string, number, boolean] = ["张三", 18, true];

// 访问元组元素(通过索引,与数组一致)
console.log(`姓名:${userInfo[0]}`); // 输出:张三
console.log(`年龄:${userInfo[1]}`); // 输出:18
console.log(`是否成年:${userInfo[2]}`); // 输出:true

类型/长度强约束

元组的核心特性是“编译期校验”,长度/类型不匹配直接报错:

let point: [number, number] = [100, 200]; // 正确:2个数字(坐标x,y)

// 错误1:长度不匹配(元组要求2个元素,传入3个)
// point = [100, 200, 300]; 

// 错误2:类型不匹配(第2位要求number,传入string)
// point = [100, "200"]; 

// 错误3:越界访问(元组仅2个元素,访问索引2)
// console.log(point[2]);

常用场景:存储结构化基础数据(坐标、日期等)

// 场景1:坐标数据(x: number, y: number)
let position: [number, number] = [50, 80];
console.log(`坐标:x=${position[0]}, y=${position[1]}`); // 输出:x=50, y=80

// 场景2:日期数据(年: number, 月: number, 日: number)
let date: [number, number, number] = [2025, 12, 4];
console.log(`日期:${date[0]}年${date[1]}月${date[2]}日`); // 输出:2025年12月4日

// 场景3:商品基础信息(名称: string, 价格: number, 库存: number)
let product: [string, number, number] = ["鸿蒙耳机", 199, 50];
console.log(`商品名称:${product[0]},价格:${product[1]}元,库存:${product[2]}件`);
// 输出:商品名称:鸿蒙耳机,价格:199元,库存:50件

元组 vs 普通数组

对比维度 元组(Tuple) 普通数组(Array)
长度约束 固定长度(编译期校验) 动态长度(可随意增删)
类型约束 每个位置类型固定(如[string, number]) 所有元素类型统一(如number[])
越界访问 编译期报错 运行时返回undefined(无编译报错)
适用场景 结构化固定数据(坐标、日期等) 动态列表数据(用户列表、成绩列表等)
灵活性 低(强约束) 高(弱约束)

三、高级集合类型与类型工具

3.1 Map:键值对映射集合

创建与存储

// 创建Map(指定键为string类型,值为number类型)
const userMap: Map<string, number> = new Map<string, number>();

// 存储键值对(支持链式调用)
userMap.set("age", 18);
userMap.set("score", 95);
userMap.set("grade", 3);

取值与判断

// 获取指定键的值
const scoreValue = userMap.get("score");
console.log(`score的值:${scoreValue}`); // 输出:95

// 判断是否包含指定键
const hasNameKey = userMap.has("name");
console.log(`是否包含name键:${hasNameKey}`); // 输出:false

// 获取Map大小(元素个数)
console.log(`Map大小:${userMap.size}`); // 输出:3

删除与清空

// 删除指定键
const deleteResult = userMap.delete("age");
console.log(`删除age是否成功:${deleteResult}`); // 输出:true
console.log(`删除后大小:${userMap.size}`); // 输出:2

// 清空所有键值对
userMap.clear();
console.log(`清空后大小:${userMap.size}`); // 输出:0

遍历Map

// 创建带初始值的Map
const studentMap: Map<string, number> = new Map([
  ["张三", 85],
  ["李四", 92],
  ["王五", 78]
]);

// 遍历Map
console.log("学生成绩:");
for (const entry of studentMap) {
  // entry[0] = 键,entry[1] = 值
  console.log(`${entry[0]}:${entry[1]}分`);
}

关键避坑:Map key不存在的处理

key不存在的场景与返回值
调用 map.get(key) 时,若key不存在,返回 undefined,直接使用会导致逻辑异常:

const userMap: Map<string, number> = new Map();
userMap.set("张三", 85);

// 场景1:访问不存在的key
const score = userMap.get("李四");
console.log(score); // 输出:undefined

// 风险:undefined参与运算,导致NaN
const total = score + 10; // undefined + 10 = NaN
console.log(total); // 输出:NaN

安全取值方法

  1. 取值前用 map.has(key) 校验;
  2. 取值后设置默认值(避免undefined)。
const userMap: Map<string, number> = new Map([["张三", 85]]);
const targetUser = "李四";

// 方法1:先校验后取值
if (userMap.has(targetUser)) {
  console.log(`${targetUser}的成绩:${userMap.get(targetUser)}`);
} else {
  console.log(`${targetUser}不存在,默认成绩:0`); // 输出:李四不存在,默认成绩:0
}

// 方法2:取值时设置默认值(推荐)
const score = userMap.get(targetUser) ?? 0; // 不存在则取0
console.log(`${targetUser}的成绩:${score}`); // 输出:李四的成绩:0

Map 核心特性总结

  • 键支持任意类型(数字、字符串、Symbol等),且键唯一;
  • 遍历顺序与插入顺序严格一致;
  • 可通过 size 属性直接获取元素个数;
  • 频繁增删键值对时性能优于其他静态结构;
  • 泛型约束键值类型,编译期可校验类型合法性。

3.2 Set:唯一元素集合

创建与添加

// 创建Set(指定存储string类型)
const tagSet: Set<string> = new Set<string>();

// 添加元素(重复元素自动忽略)
tagSet.add("前端开发");
tagSet.add("ArkTS");
tagSet.add("鸿蒙");
tagSet.add("ArkTS"); // 重复,不会存储

console.log(`Set大小:${tagSet.size}`); // 输出:3

判断与删除

// 判断是否包含指定元素
const hasHarmony = tagSet.has("鸿蒙");
console.log(`是否包含鸿蒙:${hasHarmony}`); // 输出:true

// 删除指定元素
const deleteResultSet = tagSet.delete("ArkTS");
console.log(`删除ArkTS是否成功:${deleteResultSet}`); // 输出:true

// 清空Set
tagSet.clear();
console.log(`清空后大小:${tagSet.size}`); // 输出:0

遍历Set

// 创建带初始值的Set(自动去重)
const fruitSet: Set<string> = new Set(["苹果", "香蕉", "橙子", "苹果"]);

console.log("水果列表:");
for (const fruit of fruitSet) {
  console.log(fruit); // 输出:苹果、香蕉、橙子(无重复)
}

关键特性:无索引,不可通过下标访问

Set 没有索引,不能像数组一样用 set[0] 访问元素,这是新手最易踩的坑:

const fruitSet: Set<string> = new Set(["苹果", "香蕉"]);

// 错误:Set无索引,访问返回undefined
console.log(fruitSet[0]); // 输出:undefined

// 正确:遍历访问 或 转换为数组后访问
// 方式1:遍历
for (const fruit of fruitSet) {
  console.log(fruit); // 输出:苹果、香蕉
}

// 方式2:转数组后索引访问
const fruitArr = Array.from(fruitSet);
console.log(fruitArr[0]); // 输出:苹果

Set与数组转换

推荐用 Array.from() 实现Set和数组的互转(ArkTS 不支持扩展运算符解构)。

// Set转数组
const fruitArr = Array.from(fruitSet);
console.log(`Set转数组:${fruitArr}`); // 输出:苹果,香蕉,橙子

// 数组去重(核心用法)
const duplicateArr = [1, 2, 2, 3, 3, 3];
const uniqueArr = Array.from(new Set(duplicateArr));
console.log(`原数组:${duplicateArr}`); // 输出:1,2,2,3,3,3
console.log(`去重后:${uniqueArr}`); // 输出:1,2,3

Set与数组的区别

对比维度 Set(唯一元素集合) Array(有序数组)
元素重复性 不允许重复(自动去重) 允许重复元素
元素查找效率 高(哈希表实现,O(1)) 低(遍历查找,O(n))
索引访问 无索引,不能通过下标访问(如set[0]) 支持索引访问(如arr[0])
核心用途 存储唯一元素、快速去重、判断存在性 存储有序数据、需要索引访问、允许重复

3.3 Record:强类型固定结构对象

基础语法

// 语法:Record<键类型联合, 值类型>
type 类型名 = Record<键1 | 键2 | 键3, 值类型>;
  • 键类型联合:指定对象允许的所有键(仅支持 string/number/symbol);
  • 值类型:所有键对应值的统一类型(或联合类型)。

基础用法(用户信息约束)

// 约束对象只能有name和age两个键,值为string或number
type UserInfo = Record<'name' | 'age', string | number>;

// 符合约束的对象
const userParam: UserInfo = {
  "name": "张三",
  "age": 18
};
console.log(`用户信息:${JSON.stringify(userParam)}`); // 输出:{"name":"张三","age":18}

场景示例(商品配置约束)

// 约束商品参数结构
type GoodsConfig = Record<'id' | 'name' | 'price', number | string>;

// 符合约束的商品参数
const goods: GoodsConfig = {
  "id": 1001,
  "name": "鸿蒙手机壳",
  "price": 29.9
};
console.log(`商品参数:${JSON.stringify(goods)}`);

错误示例(编译期报错)

// 错误1:包含约束外的键(gender)
// const badUser: UserInfo = {
//   'name': "李四",
//   'age': 20,
//   'gender': "男" // 编译报错:对象文字可以只指定已知属性
// };

// 错误2:值类型不匹配
// const badGoods: GoodsConfig = {
//   'id': 1001,
//   'name': "鸿蒙手机壳",
//   'price': true // 编译报错:类型boolean不能赋值给类型string | number
// };

Record 核心特性总结

  • 仅作用于编译期,无运行时实例和额外开销;
  • 严格约束对象的键范围和值类型,规避非法键/值;
  • 适用于配置项、接口参数等固定结构的静态数据;
  • 泛型加持,类型校验能力强,可大幅减少运行时错误。

四、核心数据容器总结

4.1 核心容器对比总表

数据容器 核心定位 核心特性 适用场景
普通数组(Array) 有序动态列表 动态长度、索引访问、允许重复、运行时越界 成绩列表、用户列表等动态有序数据
元组(Tuple) 强约束结构化数组 固定长度、类型强约束、编译期防越界 坐标、日期、商品基础信息等固定格式数据
Map 动态键值对集合 任意类型键、键唯一、插入顺序遍历、size属性 缓存、动态配置、数字ID映射等动态键值对
Set 唯一元素集合 元素唯一、无索引、查找效率高、自动去重 标签列表、ID去重、元素存在性判断
Record 强类型静态结构约束 仅编译期校验、无运行时实例、固定键范围 接口参数、固定配置项等需强校验的静态数据

4.2 选型核心原则

  1. 有序可重复列表 → 普通数组(Array);
  2. 固定格式结构化数据 → 元组(Tuple);
  3. 动态键值对(非字符串键/频繁增删) → Map;
  4. 去重/元素存在性判断 → Set;
  5. 固定结构静态数据(需强校验) → Record。

五、原始类型 vs 内置对象类型

类型类别 示例 特点 使用建议
原始类型 number/string 存储具体值,小写开头,轻量高效 日常开发优先使用
内置对象类型 Number/String 工具对象,大写开头,提供丰富方法 仅需调用方法时自动转换使用
特殊数组类型 元组(Tuple) 固定长度/类型,强约束 结构化固定数据场景使用
类型工具 Record 仅编译期约束,无运行时实例 约束静态对象结构时使用
// 原始类型(推荐)
let num: number = 123;
let str: string = "hello";

// 内置对象类型(不推荐手动创建)
// let numObj: Number = new Number(123);
// let strObj: String = new String("hello");

// 元组类型(结构化数据)
let userTuple: [string, number] = ["张三", 18];

// 类型判断
console.log(`num是number类型:${typeof num === 'number'}`); // 输出:true
console.log(`str是string类型:${typeof str === 'string'}`); // 输出:true

// 核心特性:原始类型可直接调用对象方法(ArkTS自动转换)
console.log(`num保留2位小数:${num.toFixed(2)}`); // 无需手动创建Number对象

六、核心避坑总结

6.1 数组避坑

  1. 越界访问:访问前校验索引 0 ≤ index < arr.length,遍历优先用 for 循环;
  2. 类型错误:声明数组时指定明确类型(如 number[]),避免混合类型;
  3. slice vs spliceslice 不修改原数组,splice 直接修改原数组。

6.2 元组避坑

  1. 长度/类型约束:元组长度、类型固定,不可随意增删或修改类型;
  2. 越界防护:元组越界编译期报错,结构化固定数据优先用元组而非数组。

6.3 Map避坑

  1. key不存在:取值前用 has(key) 校验,或用 ?? 设置默认值;
  2. 键类型:Map键可以是任意类型,需注意键的唯一性。

6.4 Set避坑

  1. 无索引:Set不能通过下标访问,需遍历或转数组后访问;
  2. 元素唯一:Set自动去重,添加重复元素无效果(无报错)。

6.5 Record避坑

  1. Record仅编译期约束,无运行时能力,不可动态增删键;
  2. 键范围固定,超出声明范围的键会编译报错,需提前规划键列表。

【课堂小结】

  1. 核心定位:数组是有序索引列表,元组是强约束数组,Map是键值对集合,Set是唯一元素集合,Record是强类型静态结构约束工具;
  2. 原始类型(number/string)可直接调用对应内置对象的方法,ArkTS 会自动完成类型转换;
  3. String 高频方法:length/slice/split/trim/includes/replace
  4. Array 核心区别:slice(不修改原数组)、splice(修改原数组),需规避越界访问风险;
  5. 元组是固定长度/类型的特殊数组,编译期防越界/类型错误,适用于坐标、日期等结构化固定数据;
  6. Set + Array.from() 是数组去重的极简方案,效率远高于手动遍历;
  7. Map 适用于动态键值对(尤其是非字符串键),需处理key不存在的场景;
  8. Record 是编译期类型工具,用于约束静态对象结构,无运行时开销;
  9. 选型原则:根据“有序性、重复性、键类型、是否固定结构”选择对应容器,最大化利用编译期校验减少BUG。

七、【代码仓库】

第四节项目代码(BuiltInObjectsDemo)已同步:https://gitee.com/juhetianxia321/harmony-os-code-base.git

八、【课后练习】

  1. 操作题:处理字符串 const msg: string = " Hello World! ",实现:

    • ① 去除首尾空格;
    • ② 转换为全小写;
    • ③ 截取子串"world"(不区分大小写)。
  2. 操作题:处理数组 const scores: number[] = [85, 92, 78, 90, 88],实现:

    • ① 筛选出90分及以上的成绩;
    • ② 计算所有成绩的平均分;
    • ③ 将成绩转换为等级(90+为A,80-89为B,70-79为C)。
  3. 分析题:以下代码输出什么?为什么?

    const arr: number[] = [1, 2, 3];
    const newArr: number[] = arr.slice(0, 2);
    console.log(`原数组:${arr}`); 
    console.log(`新数组:${newArr}`);
    
  4. 元组应用题:定义一个元组 [string, number, string] 存储“商品名称、价格、分类”,实现:

    • ① 创建元组并赋值(如["鸿蒙耳机", 199, "数码配件"]);
    • ② 遍历元组打印每个元素;
    • ③ 尝试修改元组第2位为字符串(如"199"),观察编译报错。
  5. Map避坑题:用Map存储3个学生姓名+成绩(张三85、李四92、王五78),实现:

    • ① 存储所有学生数据;
    • ② 查询“李四”的成绩;
    • ③ 计算3个学生的平均分;
    • ④ 查询“赵六”的成绩,若不存在则返回默认值0。
  6. Set应用题:用 Set 处理数组 const tags: string[] = ["前端", "ArkTS", "鸿蒙", "前端", "UI", "ArkTS"],实现:

    • ① 对数组去重;
    • ② 统计去重后的元素个数;
    • ③ 判断是否包含“UI”;
    • ④ 将去重后的元素拼接为“前端、ArkTS、鸿蒙、UI”;
  7. Record应用题:用 Record 约束商品参数(id/name/price/category),创建符合约束的对象,并尝试添加非约束键(如"brand"),观察编译报错信息。

  8. 思考题:

    • Arrayslicesplice 有哪些核心区别?分别适用于什么场景?
    • ② 元组和普通数组的核心差异是什么?分别适用于哪些场景?
    • ③ Map和Set的核心差异是什么?如何选择使用?

【下节预告】

下一节将进入函数体系的入门学习,解锁“炼气五重天”——函数的定义与使用(基础篇)。我们会掌握函数的核心封装逻辑,学会传统函数与箭头函数的定义、调用方法,熟练处理必需参数、可选参数、默认参数等不同输入场景,同时理解类型声明对函数安全性的关键作用,为后续复杂逻辑封装和高阶函数学习筑牢基础。

九、鸿蒙开发者学习与认证指引

(一)、官方学习班级报名(免费)

  1. 班级链接HarmonyOS赋能资源丰富度建设(第四期)
  2. 学号填写规则:填写个人手机号码即可完成班级信息登记

(二)、HarmonyOS应用开发者认证考试(免费)

  1. 考试链接HarmonyOS开发者能力认证入口

  2. 认证等级及适配人群

    • 基础认证:适配软件工程师、移动应用开发人员,需掌握HarmonyOS基础概念、DevEco Studio基础使用、ArkTS及ArkUI基础开发等能力;
    • 高级认证:适配项目经理、工程架构师,需掌握系统核心技术理念、应用架构设计、关键技术开发及应用上架运维等能力;
    • 专家认证:适配研发经理、解决方案专家,需掌握分布式技术原理、端云一体化开发、跨端迁移及性能优化等高级能力。
  3. 认证权益:通过认证可获得电子版证书以及其他专属权益。

posted @ 2025-12-09 14:46  鸿蒙-散修  阅读(3)  评论(0)    收藏  举报