ArkTS 高性能编程实践 -- 速查笔记
场景:性能敏感代码 (比如频繁调用、深层循环、动画/渲染逻辑等)
目标:在写业务代码的同时,顺手带上高性能写法习惯。
一、声明与表达式
1.1 使用 const 声明不变的变量
- 原则 :只读、不再修改的变量用
const,方便引擎做优化。
ts
// ✅ 推荐
const index = 10000;
// ❌ 不推荐:逻辑上不会变却用 let
let index = 10000;
1.2 number 避免整型 / 浮点型混用
ArkTS 运行时会区分 "整型 number" 和 "浮点 number",混着用会让优化失效。
// ❌ 不推荐:int ➜ double
let intNum = 1;
intNum = 1.1; // 声明时是整数,后面变成小数
// ❌ 不推荐:double ➜ int
let doubleNum = 1.1;
doubleNum = 1; // 声明时是小数,后面赋值为整数
// ✅ 推荐:类型风格保持一致
let scoreInt: number = 0; // 一直用整数
let scoreDouble: number = 0.0; // 一直用小数
1.3 数值计算避免溢出
原因:溢出后会走"慢路径",性能变差。
- 对
+ - * **运算:尽量保证在INT32_MIN ~ INT32_MAX范围内INT32_MAX = 2147483647INT32_MIN = -2147483648
- 对
&、>>>等位运算:同样避免超过INT32_MAX
简单理解:
- 做位运算 / 累加计数时不要让值无限增长,及时裁剪(比如
& 0xffff或重置)。
1.4 循环中提取常量,减少属性访问
问题代码:每次循环都访问 class 静态属性 + 计算
class Time {
static start: number = 0;
static info: number[] = [1,2,3,4,5,6,7,8,9,10,11,12];
}
function getNum(num: number): number {
let total: number = 348;
for (let index: number = 0x8000; index > 0x8; index >>= 1) {
total += ((Time.info[num - Time.start] & index) !== 0) ? 1 : 0;
}
return total;
}
优化:把循环不变的值缓存到局部常量
function getNum(num: number): number {
let total: number = 348;
const info = Time.info[num - Time.start]; // ✅ 循环外提取
for (let index: number = 0x8000; index > 0x8; index >>= 1) {
if ((info & index) != 0) {
total++;
}
}
return total;
}
"循环里用到但不会改变的东西 → 提前拿出来当局部 const"。
二、函数
2.1 尽量用参数传递,而不是闭包捕获外部变量
问题:闭包 = 引擎额外维护环境,性能有成本。
// ❌ 闭包捕获外部变量
let arr = [0, 1, 2];
function foo(): number {
return arr[0] + arr[1];
}
foo();
优化:显式通过参数传入
// ✅ 推荐
let arr = [0, 1, 2];
function foo(array: number[]): number {
return array[0] + array[1];
}
foo(arr);
心法:性能敏感代码里,"从外面拿变量" 尽量走参数,而不是让函数偷偷闭包引用。
2.2 避免可选参数,使用"必选 + 默认值"
问题:可选参数意味着可能是 undefined,每次使用都要做判断。
// ❌ 有可选参数,内部还要 if 判空
function add(left?: number, right?: number): number | undefined {
if (left != undefined && right != undefined) {
return left + right;
}
return undefined;
}
优化:改成必选参数 + 默认值
// ✅ 推荐
function add(left: number = 0, right: number = 0): number {
return left + right;
}
场景:
- 一般工具函数 尽量设计成参数必传,需要"不传"时用默认值表达。
三、数组
3.1 纯数值运算 ➜ 用 TypedArray
普通数组:
// ❌ 普通 number 数组
const arr1 = new Array<number>(1, 2, 3);
const arr2 = new Array<number>(4, 5, 6);
let res = new Array<number>(3);
for (let i = 0; i < 3; i++) {
res[i] = arr1[i] + arr2[i];
}
TypedArray:
// ✅ 推荐:TypedArray
const typedArray1 = Int8Array.from([1, 2, 3]);
const typedArray2 = Int8Array.from([4, 5, 6]);
let res = new Int8Array(3);
for (let i = 0; i < 3; i++) {
res[i] = typedArray1[i] + typedArray2[i];
}
适用场景:矩阵运算、音频数据、图像 buffer、频繁数值计算等。
3.2 避免"稀疏数组"(sparse array)
稀疏数组 & 大数组(>1024)可能会被运行时转成 hash 表存储,访问会更慢。
// ❌ 大小 100000 的数组:可能走 hash 存储
let count = 100000;
let result: number[] = new Array(count);
// ❌ 稀疏数组:直接给很远的索引赋值
let result2: number[] = new Array();
result2[9999] = 0;
建议:
-
尽量使用
push逐步填充 -
或者用
new Array(count).fill(0)初始化成"密集数组"// ✅ 密集数组
let result: number[] = new Array(count).fill(0);
3.3 避免联合类型数组 & 数值类型混用
// ❌ 数值数组里混合整型 + 浮点型
let arrNum: number[] = [1, 1.1, 2];
// ❌ 元素是 (number | string) 联合类型
let arrUnion: (number | string)[] = [1, 'hello'];
推荐: 不同类型的值分开存:
// ✅ 分类型存储
let arrInt: number[] = [1, 2, 3];
let arrDouble: number[] = [0.1, 0.2, 0.3];
let arrString: string[] = ['hello', 'world'];
心法:
- 数组 = 一种类型,而不是"什么都往里面丢一个"。
四、异常(Error)使用
4.1 性能敏感路径中避免频繁抛异常
抛异常时需要构造栈信息,是非常重 的操作。
特别是在
for循环、热点函数中,不要用异常做控制流。
问题代码:
function div(a: number, b: number): number {
if (a <= 0 || b <= 0) {
throw new Error('Invalid numbers.');
}
return a / b;
}
function sum(num: number): number {
let sum = 0;
try {
for (let t = 1; t < 100; t++) {
sum += div(t, num);
}
} catch (e) {
console.info(e.message);
}
return sum;
}
优化思路:
-
用 返回特殊值 (NaN / -1 / null) 表示异常,而不是直接 throw
-
循环内部做快速判断,避免频繁进入 catch
function div(a: number, b: number): number {
if (a <= 0 || b <= 0) {
return NaN; // ✅ 用返回值表达错误
}
return a / b;
}function sum(num: number): number {
let sum = 0;
for (let t = 1; t < 100; t++) {
if (num <= 0) { // ✅ 提前拦截非法情况
console.info('Invalid numbers.');
break; // 可视情况 break / continue
}
sum += div(t, num);
}
return sum;
}
经验法则:
- 异常只用在"真正异常的情况",而不是普通业务条件判断。
- 热路径里宁可多几次
if,也少一次throw。
五、实战时可以直接套用的"小 Checklist"
写 ArkTS 性能敏感代码时,可以过一遍这几个问题:
- 常量是不是都用
const?- 会不会修改?不会就用
const。
- 会不会修改?不会就用
- number 有没有从 int 变 double 或反过来?
- 是否统一风格?
- 是否可能越界 int32?
- 循环里有没有可以提到外面的不变量?
- 静态属性、数组索引结果、复杂表达式等。
- 函数是不是在闭包里偷偷引用外部变量?
- 能否通过参数传进去?
- 函数参数用的是可选参数吗?
- 能否改成"必选+默认值"?
- 数组是不是稀疏的 / 超大 / 元素类型杂?
- 能否用 TypedArray 或分类型数组?
- for/热点代码里有频繁
throw/try...catch吗?- 能否用返回码 /
NaN/null代表错误?
- 能否用返回码 /