HarmonyOS ArkTS 常用工具函数实现与应用详解

引言

在HarmonyOS应用开发中,工具函数是提升开发效率、保证代码质量的重要组成部分。无论是数据校验、性能优化还是数据处理,优秀的工具函数都能极大简化开发流程。本文将深入探讨ArkTS中最常用的工具函数,包括isEmptydebouncethrottledeepClone等,从原理、实现到应用场景进行全方位解析。


一、工具函数的设计原则

在开始具体实现之前,我们需要明确工具函数的设计原则:

1.1 单一职责原则

每个工具函数应该只负责一个功能,避免"瑞士军刀"式的万能函数。

1.2 类型安全

ArkTS作为静态类型语言,工具函数必须保证严格的类型检查,避免使用anyunknown类型。

1.3 无副作用

纯函数是工具函数的最佳实践,避免修改传入参数或产生全局状态变化。

1.4 防御性编程

处理边界情况,对异常输入进行合理处理,提供清晰的错误信息。


二、isEmpty - 空值判断函数

2.1 函数概述

isEmpty函数用于判断一个值是否为空,支持多种数据类型的判断。

2.2 实现原理

空值判断需要考虑多种情况:

  • nullundefined
  • 空字符串(包括仅包含空格的字符串)
  • NaN(非数字)
  • 空数组
  • 空对象

2.3 ArkTS实现

typescript 复制代码
export function isEmpty(value: string | number | boolean | object | undefined | null): boolean {
  // 处理 null 和 undefined
  if (value === null || value === undefined) {
    return true;
  }
  
  // 处理字符串类型
  if (typeof value === 'string') {
    return value.trim().length === 0;
  }
  
  // 处理数字类型(NaN判断)
  if (typeof value === 'number') {
    return isNaN(value);
  }
  
  // 处理数组类型
  if (Array.isArray(value)) {
    return value.length === 0;
  }
  
  // 处理对象类型
  if (typeof value === 'object') {
    return Object.keys(value).length === 0;
  }
  
  // 其他类型默认为非空
  return false;
}

export function isNotEmpty(value: string | number | boolean | object | undefined | null): boolean {
  return !isEmpty(value);
}

2.4 关键技术点解析

2.4.1 类型联合声明
typescript 复制代码
value: string | number | boolean | object | undefined | null

ArkTS支持联合类型,通过|连接多种类型,使函数能够接受不同类型的参数。

2.4.2 typeof 类型守卫
typescript 复制代码
if (typeof value === 'string') {
  return value.trim().length === 0;
}

typeof操作符在ArkTS中是类型守卫,可以在条件分支中缩小类型范围。

2.4.3 Array.isArray 检测
typescript 复制代码
if (Array.isArray(value)) {
  return value.length === 0;
}

由于typeof []返回'object',需要使用Array.isArray专门检测数组类型。

2.4.4 对象空值检测
typescript 复制代码
if (typeof value === 'object') {
  return Object.keys(value).length === 0;
}

使用Object.keys()获取对象的所有键名,通过判断键名数量来确定对象是否为空。

2.5 使用场景

场景1:表单验证
typescript 复制代码
interface FormData {
  username: string;
  password: string;
  email: string;
}

function validateForm(form: FormData): boolean {
  if (isEmpty(form.username)) {
    console.error('用户名不能为空');
    return false;
  }
  if (isEmpty(form.password)) {
    console.error('密码不能为空');
    return false;
  }
  if (isEmpty(form.email)) {
    console.error('邮箱不能为空');
    return false;
  }
  return true;
}
场景2:API请求参数校验
typescript 复制代码
interface ApiParams {
  page?: number;
  size?: number;
  keyword?: string;
}

function buildRequest(params: ApiParams): Record<string, unknown> {
  const query: Record<string, unknown> = {};
  
  if (isNotEmpty(params.page)) {
    query['page'] = params.page;
  }
  if (isNotEmpty(params.size)) {
    query['size'] = params.size;
  }
  if (isNotEmpty(params.keyword)) {
    query['keyword'] = params.keyword;
  }
  
  return query;
}
场景3:状态管理中的空值判断
typescript 复制代码
@Local userInfo: User | null = null;

build() {
  Column() {
    if (isEmpty(this.userInfo)) {
      Text('请先登录')
        .fontSize(16)
        .fontColor('#999999');
    } else {
      Text(`欢迎, ${this.userInfo.name}`)
        .fontSize(16)
        .fontColor('#333333');
    }
  }
}

2.6 边界情况处理

测试用例 预期结果 说明
isEmpty(null) true null值
isEmpty(undefined) true undefined值
isEmpty('') true 空字符串
isEmpty(' ') true 空白字符串
isEmpty(0) false 数字0
isEmpty(NaN) true 非数字
isEmpty([]) true 空数组
isEmpty({}) true 空对象
isEmpty(false) false 布尔值false
isEmpty(true) false 布尔值true

三、debounce - 防抖函数

3.1 函数概述

防抖函数用于限制函数的执行频率,确保在指定时间内只有最后一次调用生效。

3.2 应用场景

防抖常用于以下场景:

  • 搜索框输入联想
  • 窗口resize事件
  • 按钮重复点击防止重复提交
  • 滚动事件处理

3.3 实现原理

防抖的核心思想是:

  1. 当事件触发时,设置一个定时器
  2. 如果在定时器触发前再次触发事件,则清除定时器并重新设置
  3. 只有在指定时间内没有再次触发,才执行目标函数

3.4 ArkTS实现

typescript 复制代码
type SimpleFunction = () => void;

export function debounce(fn: SimpleFunction, delay: number): SimpleFunction {
  let timer: number | null = null;
  
  return (): void => {
    // 如果存在定时器,清除它
    if (timer !== null) {
      clearTimeout(timer);
    }
    
    // 设置新的定时器
    const timerValue = setTimeout(() => {
      fn();
    }, delay);
    
    timer = timerValue as number;
  };
}

3.5 关键技术点解析

3.5.1 闭包的使用
typescript 复制代码
return (): void => {
  if (timer !== null) {
    clearTimeout(timer);
  }
  // ...
};

防抖函数利用闭包特性,使内部函数能够访问外部函数的timer变量,从而实现定时器的持久化和清除。

3.5.2 定时器管理
typescript 复制代码
let timer: number | null = null;
// ...
const timerValue = setTimeout(() => {
  fn();
}, delay);
timer = timerValue as number;

使用setTimeout创建定时器,并将返回值保存,以便后续清除。

3.5.3 类型安全设计
typescript 复制代码
type SimpleFunction = () => void;

定义SimpleFunction类型别名,确保传入的函数符合预期的签名。

3.6 使用示例

示例1:搜索框防抖
typescript 复制代码
@Local searchKeyword: string = '';
@Local searchResults: string[] = [];

private debouncedSearch: () => void = debounce(() => {
  this.performSearch();
}, 300);

performSearch(): void {
  if (isEmpty(this.searchKeyword)) {
    this.searchResults = [];
    return;
  }
  
  // 模拟API请求
  console.log(`搜索: ${this.searchKeyword}`);
  // this.searchResults = await api.search(this.searchKeyword);
}

build() {
  Column() {
    TextInput({ placeholder: '请输入搜索关键词' })
      .onChange((value: string) => {
        this.searchKeyword = value;
        this.debouncedSearch();
      })
    
    List() {
      ForEach(this.searchResults, (item: string) => {
        ListItem() {
          Text(item)
            .fontSize(14)
        }
      })
    }
  }
}
示例2:窗口resize处理
typescript 复制代码
@Local windowWidth: number = 0;
@Local windowHeight: number = 0;

private debouncedResize: () => void = debounce(() => {
  this.updateWindowSize();
}, 200);

updateWindowSize(): void {
  // 获取窗口尺寸
  // this.windowWidth = getWindowWidth();
  // this.windowHeight = getWindowHeight();
  console.log(`窗口尺寸: ${this.windowWidth} x ${this.windowHeight}`);
}

aboutToAppear() {
  // 监听窗口resize事件
  // window.addEventListener('resize', this.debouncedResize);
}

aboutToDisappear() {
  // 移除事件监听
  // window.removeEventListener('resize', this.debouncedResize);
}

3.7 防抖与节流的区别

特性 防抖(debounce) 节流(throttle)
执行时机 最后一次触发后延迟执行 固定时间间隔执行
触发频率高时 只执行最后一次 按固定频率执行
适用场景 搜索输入、窗口resize 滚动事件、按钮点击
实现复杂度 较简单 较复杂

四、throttle - 节流函数

4.1 函数概述

节流函数用于限制函数的执行频率,确保在指定时间间隔内只执行一次。

4.2 应用场景

节流常用于以下场景:

  • 滚动事件处理
  • 按钮点击防重复
  • 鼠标移动事件
  • 游戏中的帧率控制

4.3 实现原理

节流的核心思想是:

  1. 记录上次执行的时间戳
  2. 当事件触发时,检查当前时间与上次执行时间的差值
  3. 如果差值大于等于指定间隔,则执行函数并更新时间戳
  4. 否则忽略本次触发

4.4 ArkTS实现

typescript 复制代码
type SimpleFunction = () => void;

export function throttle(fn: SimpleFunction, delay: number): SimpleFunction {
  let lastTime: number = 0;
  
  return (): void => {
    const now: number = Date.now();
    
    // 如果当前时间与上次执行时间的差值大于等于delay,则执行
    if (now - lastTime >= delay) {
      lastTime = now;
      fn();
    }
  };
}

4.5 关键技术点解析

4.5.1 时间戳记录
typescript 复制代码
let lastTime: number = 0;
// ...
const now: number = Date.now();
if (now - lastTime >= delay) {
  lastTime = now;
  fn();
}

使用Date.now()获取当前时间戳,通过时间差判断是否执行函数。

4.5.2 首次执行策略

初始lastTime设为0,确保第一次触发时必定执行(因为Date.now() - 0 >= delay在delay不为0时成立)。

4.6 使用示例

示例1:滚动加载更多
typescript 复制代码
@Local pageIndex: number = 1;
@Local loading: boolean = false;

private throttledLoadMore: () => void = throttle(() => {
  this.loadMoreData();
}, 1000);

loadMoreData(): void {
  if (this.loading) return;
  
  this.loading = true;
  // 模拟加载数据
  console.log(`加载第 ${this.pageIndex} 页`);
  this.pageIndex++;
  
  setTimeout(() => {
    this.loading = false;
  }, 1000);
}

build() {
  Scroll() {
    Column() {
      // 列表内容
    }
    .onScroll((event: ScrollEvent) => {
      // 判断是否滚动到底部
      if (event.offsetDistance >= event.scrollableDistance - 100) {
        this.throttledLoadMore();
      }
    })
  }
}
示例2:按钮点击节流
typescript 复制代码
@Local clickCount: number = 0;

private throttledClick: () => void = throttle(() => {
  this.handleClick();
}, 500);

handleClick(): void {
  this.clickCount++;
  console.log(`点击次数: ${this.clickCount}`);
}

build() {
  Button('点击测试')
    .onClick(() => {
      this.throttledClick();
    })
  
  Text(`点击次数: ${this.clickCount}`)
    .fontSize(14)
}

五、deepClone - 深拷贝函数

5.1 函数概述

深拷贝函数用于创建一个对象的完全独立副本,确保修改副本不会影响原对象。

5.2 浅拷贝与深拷贝的区别

特性 浅拷贝 深拷贝
基本类型 值复制 值复制
对象类型 引用复制 值复制
嵌套对象 共享引用 递归复制
修改副本 影响原对象 不影响原对象

5.3 实现原理

深拷贝需要处理多种情况:

  1. 基本类型(直接返回)
  2. null值(直接返回)
  3. 数组类型(递归复制每个元素)
  4. 对象类型(递归复制每个属性)

5.4 ArkTS实现

typescript 复制代码
export function deepClone(obj: object | null): object | null {
  // 处理null和非对象类型
  if (obj === null || typeof obj !== 'object') {
    return obj;
  }
  
  // 处理数组类型
  if (Array.isArray(obj)) {
    const arrLen = (obj as []).length;
    const result: object[] = [];
    
    for (let i = 0; i < arrLen; i++) {
      const item = (obj as []).slice(i, i + 1)[0];
      
      if (item === null || typeof item !== 'object') {
        result.push(item as object);
      } else {
        const cloned = deepClone(item as object);
        if (cloned !== null) {
          result.push(cloned);
        } else {
          result.push(item as object);
        }
      }
    }
    
    return result;
  }
  
  // 处理对象类型
  const keys = Object.keys(obj);
  const keyLen = keys.length;
  const clone: Record<string, object | null> = {};
  
  for (let i = 0; i < keyLen; i++) {
    const key = keys[i];
    const value = (obj as Record<string, object | null>)[key];
    
    if (value === null || typeof value !== 'object') {
      clone[key] = value;
    } else {
      clone[key] = deepClone(value);
    }
  }
  
  return clone;
}

5.5 关键技术点解析

5.5.1 递归实现
typescript 复制代码
if (item === null || typeof item !== 'object') {
  result.push(item as object);
} else {
  const cloned = deepClone(item as object);
  // ...
}

通过递归调用处理嵌套对象,确保所有层级的对象都被复制。

5.5.2 数组处理
typescript 复制代码
if (Array.isArray(obj)) {
  const arrLen = (obj as []).length;
  const result: object[] = [];
  
  for (let i = 0; i < arrLen; i++) {
    const item = (obj as []).slice(i, i + 1)[0];
    // ...
  }
  
  return result;
}

使用Array.isArray检测数组类型,然后逐个处理数组元素。

5.5.3 对象处理
typescript 复制代码
const keys = Object.keys(obj);
const keyLen = keys.length;
const clone: Record<string, object | null> = {};

for (let i = 0; i < keyLen; i++) {
  const key = keys[i];
  const value = (obj as Record<string, object | null>)[key];
  // ...
}

使用Object.keys()获取对象的所有键,然后逐个处理每个属性。

5.6 使用示例

示例1:状态管理中的深拷贝
typescript 复制代码
interface User {
  name: string;
  age: number;
  address: {
    city: string;
    street: string;
  };
}

@Local originalUser: User = {
  name: '张三',
  age: 25,
  address: {
    city: '北京',
    street: '朝阳区'
  }
};

@Local clonedUser: User | null = null;

cloneUser(): void {
  this.clonedUser = deepClone(this.originalUser) as User;
}

modifyClonedUser(): void {
  if (this.clonedUser) {
    this.clonedUser.name = '李四';
    this.clonedUser.address.city = '上海';
  }
}

build() {
  Column() {
    Button('克隆用户')
      .onClick(() => {
        this.cloneUser();
      })
    
    Button('修改克隆用户')
      .onClick(() => {
        this.modifyClonedUser();
      })
    
    Text(`原始用户: ${JSON.stringify(this.originalUser)}`)
      .fontSize(12)
    
    Text(`克隆用户: ${JSON.stringify(this.clonedUser)}`)
      .fontSize(12)
  }
}
示例2:表单数据备份
typescript 复制代码
interface FormData {
  username: string;
  password: string;
  preferences: {
    darkMode: boolean;
    notifications: boolean;
  };
}

@Local formData: FormData = {
  username: '',
  password: '',
  preferences: {
    darkMode: false,
    notifications: true
  }
};

@Local savedData: FormData | null = null;

saveForm(): void {
  this.savedData = deepClone(this.formData) as FormData;
  console.log('表单已保存');
}

resetForm(): void {
  if (this.savedData) {
    this.formData = deepClone(this.savedData) as FormData;
    console.log('表单已重置');
  }
}

5.7 性能优化考虑

5.7.1 循环引用检测

当前实现不支持循环引用检测,如果对象中存在循环引用会导致无限递归。可以通过WeakSet来检测:

typescript 复制代码
export function deepCloneWithCycle(obj: object | null, seen: WeakSet<object> = new WeakSet()): object | null {
  if (obj === null || typeof obj !== 'object') {
    return obj;
  }
  
  // 检测循环引用
  if (seen.has(obj)) {
    return obj; // 返回已处理的对象,避免无限递归
  }
  seen.add(obj);
  
  if (Array.isArray(obj)) {
    const arrLen = (obj as []).length;
    const result: object[] = [];
    
    for (let i = 0; i < arrLen; i++) {
      const item = (obj as []).slice(i, i + 1)[0];
      if (item === null || typeof item !== 'object') {
        result.push(item as object);
      } else {
        const cloned = deepCloneWithCycle(item as object, seen);
        if (cloned !== null) {
          result.push(cloned);
        }
      }
    }
    
    return result;
  }
  
  const keys = Object.keys(obj);
  const keyLen = keys.length;
  const clone: Record<string, object | null> = {};
  
  for (let i = 0; i < keyLen; i++) {
    const key = keys[i];
    const value = (obj as Record<string, object | null>)[key];
    if (value === null || typeof value !== 'object') {
      clone[key] = value;
    } else {
      clone[key] = deepCloneWithCycle(value, seen);
    }
  }
  
  return clone;
}

六、generateId - 唯一ID生成函数

6.1 函数概述

generateId函数用于生成唯一标识符,常用于数据库记录、组件key等场景。

6.2 ArkTS实现

typescript 复制代码
export function generateId(): string {
  const timestamp: number = Date.now();
  const random: number = Math.floor(Math.random() * 10000);
  return `${timestamp}${random}`;
}

6.3 实现原理

  • 时间戳:保证ID的唯一性和递增性
  • 随机数:避免同一毫秒内生成重复ID

6.4 使用场景

typescript 复制代码
interface TodoItem {
  id: string;
  title: string;
  completed: boolean;
}

@Local todos: TodoItem[] = [];

addTodo(title: string): void {
  const newTodo: TodoItem = {
    id: generateId(),
    title: title,
    completed: false
  };
  this.todos.push(newTodo);
}

build() {
  List() {
    ForEach(this.todos, (item: TodoItem) => {
      ListItem() {
        Text(item.title)
          .fontSize(16)
      }
      .key(item.id)
    })
  }
}

七、formatFileSize - 文件大小格式化函数

7.1 函数概述

formatFileSize函数用于将字节数转换为可读的文件大小格式。

7.2 ArkTS实现

typescript 复制代码
export function formatFileSize(bytes: number): string {
  if (bytes < 1024) {
    return `${bytes} B`;
  } else if (bytes < 1024 * 1024) {
    return `${(bytes / 1024).toFixed(2)} KB`;
  } else if (bytes < 1024 * 1024 * 1024) {
    return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
  } else {
    return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
  }
}

7.3 使用场景

typescript 复制代码
interface FileInfo {
  name: string;
  size: number;
  type: string;
}

@Local files: FileInfo[] = [
  { name: 'document.pdf', size: 2048000, type: 'pdf' },
  { name: 'image.jpg', size: 153600, type: 'jpg' },
  { name: 'video.mp4', size: 52428800, type: 'mp4' }
];

build() {
  List() {
    ForEach(this.files, (file: FileInfo) => {
      ListItem() {
        Row() {
          Text(file.name)
            .fontSize(14)
          Text(formatFileSize(file.size))
            .fontSize(12)
            .fontColor('#999999')
        }
      }
    })
  }
}

八、formatDuration - 时长格式化函数

8.1 函数概述

formatDuration函数用于将秒数转换为可读的时长格式。

8.2 ArkTS实现

typescript 复制代码
export function formatDuration(seconds: number): string {
  const hours: number = Math.floor(seconds / 3600);
  const minutes: number = Math.floor((seconds % 3600) / 60);
  const secs: number = seconds % 60;
  
  if (hours > 0) {
    return `${hours}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
  }
  return `${minutes}:${secs.toString().padStart(2, '0')}`;
}

8.3 使用场景

typescript 复制代码
@Local videoDuration: number = 3661; // 1小时1分1秒
@Local progress: number = 120; // 2分钟

build() {
  Column() {
    Text(`总时长: ${formatDuration(this.videoDuration)}`)
      .fontSize(14)
    
    Text(`已播放: ${formatDuration(this.progress)}`)
      .fontSize(14)
  }
}

九、工具函数的模块化组织

9.1 目录结构

复制代码
common/
├── utils/
│   ├── common.ets       # 通用工具函数
│   ├── formatter.ets    # 格式化工具函数
│   ├── converter.ets    # 类型转换工具函数
│   ├── validator.ets    # 验证工具函数
│   └── index.ets        # 统一导出入口
├── ui/
│   ├── components.ets   # UI组件
│   ├── layouts.ets      # 布局组件
│   └── styles.ets       # 样式定义
├── network/
│   ├── http.ets         # HTTP请求封装
│   └── aiClient.ets     # AI接口封装
├── data/
│   ├── parser.ets       # 数据解析
│   └── storage.ets      # 数据存储
└── state/
    └── store.ets        # 状态管理

9.2 统一导出入口

typescript 复制代码
// common/utils/index.ets
export * from './common';
export * from './formatter';
export * from './converter';
export * from './validator';

9.3 使用方式

typescript 复制代码
import { 
  isEmpty, 
  debounce, 
  throttle, 
  deepClone, 
  generateId,
  formatFileSize,
  formatDuration 
} from '../common/utils';

十、性能优化与最佳实践

10.1 避免重复创建函数

typescript 复制代码
// 错误:每次渲染都会创建新的防抖函数
@Builder
buildSearchInput() {
  TextInput()
    .onChange(() => {
      debounce(() => this.search(), 300)();
    })
}

// 正确:在组件初始化时创建一次
@Local debouncedSearch: () => void = debounce(() => {
  this.search();
}, 300);

@Builder
buildSearchInput() {
  TextInput()
    .onChange(() => {
      this.debouncedSearch();
    })
}

10.2 及时清理定时器

typescript 复制代码
@Local timer: number | null = null;

someFunction() {
  if (this.timer !== null) {
    clearTimeout(this.timer);
  }
  
  this.timer = setTimeout(() => {
    // 执行操作
  }, 1000);
}

aboutToDisappear() {
  if (this.timer !== null) {
    clearTimeout(this.timer);
  }
}

10.3 深拷贝的性能考虑

对于大型对象,深拷贝可能会影响性能。可以考虑:

  • 使用不可变数据结构
  • 只复制需要修改的部分
  • 使用缓存机制避免重复拷贝

十一、总结

本文详细介绍了HarmonyOS ArkTS中常用工具函数的实现与应用,包括:

  1. isEmpty - 空值判断,支持多种类型
  2. debounce - 防抖函数,限制高频事件的执行
  3. throttle - 节流函数,固定频率执行
  4. deepClone - 深拷贝函数,创建独立副本
  5. generateId - 唯一ID生成
  6. formatFileSize - 文件大小格式化
  7. formatDuration - 时长格式化

这些工具函数是HarmonyOS应用开发中不可或缺的基础组件,掌握它们的原理和使用场景,能够显著提升开发效率和代码质量。

在实际项目中,建议将这些工具函数按照模块化方式组织,便于维护和复用。同时,根据具体业务需求进行适当的扩展和优化,以满足不同场景的需求。


附录:工具函数完整代码

typescript 复制代码
// common/utils/common.ets

export function isEmpty(value: string | number | boolean | object | undefined | null): boolean {
  if (value === null || value === undefined) {
    return true;
  }
  if (typeof value === 'string') {
    return value.trim().length === 0;
  }
  if (typeof value === 'number') {
    return isNaN(value);
  }
  if (Array.isArray(value)) {
    return value.length === 0;
  }
  if (typeof value === 'object') {
    return Object.keys(value).length === 0;
  }
  return false;
}

export function isNotEmpty(value: string | number | boolean | object | undefined | null): boolean {
  return !isEmpty(value);
}

export function generateId(): string {
  const timestamp: number = Date.now();
  const random: number = Math.floor(Math.random() * 10000);
  return `${timestamp}${random}`;
}

type SimpleFunction = () => void;

export function debounce(fn: SimpleFunction, delay: number): SimpleFunction {
  let timer: number | null = null;
  return (): void => {
    if (timer !== null) {
      clearTimeout(timer);
    }
    const timerValue = setTimeout(() => {
      fn();
    }, delay);
    timer = timerValue as number;
  };
}

export function throttle(fn: SimpleFunction, delay: number): SimpleFunction {
  let lastTime: number = 0;
  return (): void => {
    const now: number = Date.now();
    if (now - lastTime >= delay) {
      lastTime = now;
      fn();
    }
  };
}

export function formatFileSize(bytes: number): string {
  if (bytes < 1024) {
    return `${bytes} B`;
  } else if (bytes < 1024 * 1024) {
    return `${(bytes / 1024).toFixed(2)} KB`;
  } else if (bytes < 1024 * 1024 * 1024) {
    return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
  } else {
    return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
  }
}

export function formatDuration(seconds: number): string {
  const hours: number = Math.floor(seconds / 3600);
  const minutes: number = Math.floor((seconds % 3600) / 60);
  const secs: number = seconds % 60;
  
  if (hours > 0) {
    return `${hours}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
  }
  return `${minutes}:${secs.toString().padStart(2, '0')}`;
}

export function deepClone(obj: object | null): object | null {
  if (obj === null || typeof obj !== 'object') {
    return obj;
  }
  
  if (Array.isArray(obj)) {
    const arrLen = (obj as []).length;
    const result: object[] = [];
    for (let i = 0; i < arrLen; i++) {
      const item = (obj as []).slice(i, i + 1)[0];
      if (item === null || typeof item !== 'object') {
        result.push(item as object);
      } else {
        const cloned = deepClone(item as object);
        if (cloned !== null) {
          result.push(cloned);
        } else {
          result.push(item as object);
        }
      }
    }
    return result;
  }
  
  const keys = Object.keys(obj);
  const keyLen = keys.length;
  const clone: Record<string, object | null> = {};
  for (let i = 0; i < keyLen; i++) {
    const key = keys[i];
    const value = (obj as Record<string, object | null>)[key];
    if (value === null || typeof value !== 'object') {
      clone[key] = value;
    } else {
      clone[key] = deepClone(value);
    }
  }
  return clone;
}
相关推荐
文慧的科技江湖3 小时前
开源 | 慧知开源重卡充电桩平台 V2.0.1 —— 三级账户体系、VIN自动识别、自动分账结算、分时电价管控、车队全生命周期管理、多维度运营报表
开源·慧知开源重卡充电桩平台
网安蟹佬霸3 小时前
Google开源DiffusionGemma:26B MoE扩散语言模型,放弃自回归实现4倍推理加速
语言模型·回归·开源
lichenyang4533 小时前
# 打车票根卡片 UI 重构:从 Circle 挖洞到 clipShape PathShape,再到 100% 自适应
ui·华为·重构·harmonyos
LeoZY_3 小时前
CH347应用 USB转JTAG功能之:probe-rs搭配CH347下载MCU命令全指南
单片机·嵌入式硬件·mcu·开源·github
风华圆舞3 小时前
鸿蒙导航意图 的 Flutter 侧封装思路
flutter·华为·harmonyos
我认不到你3 小时前
【开源、教程】RAG全流程实现(java+完整代码):第二弹
java·开发语言·人工智能·深度学习·ai·语言模型·开源
风华圆舞3 小时前
Flutter 调用原生失败时,如何优雅处理 `MissingPluginException`
flutter·华为·harmonyos
金启攻3 小时前
鸿蒙原生应用实战(五):编译构建与性能优化 —— 从开发到上架
华为·harmonyos
前端不太难3 小时前
鸿蒙游戏世界模型:实现原理 + Demo实现
游戏·状态模式·harmonyos