鸿蒙零基础语法入门:开启你的开发之旅

前言

HarmonyOS是新一代的智能终端操作系统,为不同设备的智能化、互联与协同提供了统一的语言,为用户带来简捷,流畅,连续,安全可靠的全场景交互体验。

在鸿蒙操作系统的开发中,掌握基础语法是每位开发者的必修课。本文将为您详细讲解鸿蒙编程的基础语法,帮助初学者轻松入门,为后续开发打下坚实基础。

OK!话不多说,直接开始!

本文参考于 华为课程官方文档

1、环境搭建与ArkTs语言介绍

1.1 环境搭建

下载官方编译器

既然要开发鸿蒙程序,那么就需要对应官方编译器【DevEco】 点我下载 (面向实名认证开发,需要注册登录华为账号)

安装编译器

如图所示:

  • 为了方便后续操作,这里最好是全部勾上,尤其是【添加bin】选项

配置开发SDK

如图所示:

  • Location:配置对应SDK路径
  • API Version xx:对应SDK版本【按需安装即可】

开始第一个项目

如图所示:

  • 刚开始选择Empty Ability,然后点击Next就会进入第二个页面
  • 在第二页面中:
    • 【Bundle name】:表示该鸿蒙软件唯一标识 ,也就是所谓的包名
    • 【Device type】:表示该项目所支持的设备类型,这也是鸿蒙特色 一套代码多端使用
    • 点击Next 就会进入下一个页面

如图所示:

  • 点击左边的【Previewer】 左侧将会出现预览图,当预览出现Hello World时,则说明项目创建成功

  • 回到项目右侧,现在介绍下工程目录结构(Stage模型)

    • AppScope > app.json5 :应用的全局配置信息,详见app.json5配置文件

    • entry:HarmonyOS工程模块,编译构建生成一个HAP包。

      • src > main > ets:用于存放ArkTS源码。
      • src > main > ets > entryability:应用/服务的入口。
      • src > main > ets > entrybackupability:应用提供扩展的备份恢复能力。
      • src > main > ets > pages:应用/服务包含的页面。
      • src > main > resources :用于存放应用/服务所用到的资源文件,如图形、多媒体、字符串、布局文件等。关于资源文件,详见资源分类与访问
      • src > main > module.json5 :模块配置文件。主要包含HAP包的配置信息、应用/服务在具体设备上的配置信息以及应用/服务的全局配置信息。具体的配置文件说明,详见module.json5配置文件
      • build-profile.json5:当前的模块信息 、编译信息配置项,包括buildOption、targets配置等。
      • hvigorfile.ts:模块级编译构建任务脚本。
      • obfuscation-rules.txt :混淆规则文件。混淆开启后,在使用Release模式进行编译时,会对代码进行编译、混淆及压缩处理,保护代码资产。详见开启代码混淆
      • oh-package.json5:用来描述包名、版本、入口文件(类型声明文件)和依赖项等信息。
    • oh_modules:用于存放三方库依赖信息。

    • build-profile.json5:工程级配置信息,包括签名signingConfigs、产品配置products等。其中products中可配置当前运行环境,默认为HarmonyOS。

    • hvigorfile.ts:工程级编译构建任务脚本。

    • oh-package.json5:主要用来描述全局配置,如:依赖覆盖(overrides)、依赖关系重写(overrideDependencyMap)和参数化配置(parameterFile)等

到这!鸿蒙开发环境已经搭建成功。现在开始介绍ArkTS语言

1.2 ArkTS语言介绍

ArkTS是鸿蒙生态的应用开发语言。它在保持TypeScript(简称TS)基本语法风格的基础上,进一步通过规范强化静态检查和分析,使得在程序运行之前的开发期能检测更多错误,提升代码健壮性,并实现更好的运行性能。同时,提供了声明式UI范式、状态管理支持等相应的能力,让开发者可以以更简洁、更自然的方式开发高性能应用

话不多说,直接开始上手吧!

2、ArkTS语法讲解

2.1 ArkTS声明

ArkTS通过声明引入变量、常量、函数和类型

2.1.1 变量声明

以关键字let开头的声明引入变量,该变量在程序执行期间可以具有不同的值。

示例:

ArkTS 复制代码
let hi: string = 'hello';
hi = 'hello, world';

2.1.2 常量声明

以关键字const开头的声明引入只读常量,该常量只能被赋值一次,如果强制重新赋值将会造成编译时错误!

示例:

ArkTS 复制代码
const hello: string = 'hello';

2.1.3 自动类型推断

因为ArkTS是一种静态类型语言,因此所有数据类型必须在编译时确定。

但是,如果一个变量或常量的声明包含了初始值,那么开发者就不需要显式指定其类型。ArkTS规范中列举了所有允许自动推断类型的场景。

以下示例中,两条声明语句都是有效的,两个变量都是string类型:

ArkTS 复制代码
let hi1: string = 'hello';
let hi2 = 'hello, world';

2.2 ArkTS基础数据类型

2.2.1 number类型

ArkTS提供number类型

  • number用于表示整数和浮点数
  • 支持大部分的算术运算
  • 数字的范围和行为与JavaScript类似

示例:

ArkTS 复制代码
let n1 = 3.14;
let n2 = 3.141592;
let n3 = .5;
let n4 = 1e2;

function factorial(n: number): number {
  if (n <= 1) {
    return 1;
  }
  return n * factorial(n - 1);
}

factorial(n1)  //  7.660344000000002 
factorial(n2)  //  7.680640444893748 
factorial(n3)  //  1 
factorial(n4)  //  9.33262154439441e+157 

注意:number类型在表示大整数时会造成精度丢失。在开发时可以按需使用BigInt类型来确保精度

BigInt示例:

ArkTS 复制代码
let bigIntger: BigInt = BigInt('999999999999999999999999999999999999999999999999999999999999');
console.log('bigIntger' + bigIntger.toString());

2.2.2 boolean类型

  • boolean类型由true和false两个逻辑值组成。
  • 通常在条件语句中使用boolean类型的变量:

示例:

ArkTS 复制代码
let isDone: boolean = false;

if (isDone) {
  console.log ('Done!');
}

2.2.3 string类型

  • string代表字符序列;可以使用转义字符来表示字符。

  • 字符串可以由 单引号 ' 或者 双引号 " 相互包裹零个或多个字符组成。

  • 字符串还可以用反向单引号 ` 加上 ${变量} 组合使用

示例:

ArkTS 复制代码
let s1 = 'Hello, world!\n';
let s2 = 'this is a string';
let a = 'Success';
let s3 = `The result is ${a}`;

2.2.4 void类型

  • 通常用于函数的返回类型,表示该函数不返回任何只

示例:

ArkTS 复制代码
class Class<T> {
  //...
}
let instance: Class <void>

2.2.5 null 和 underfined

  • Null表示一个明确的空值
  • Undefined 表示一个变量未被赋值或未初始化时的默认状态

示例:

ArkTS 复制代码
let unknownValue: null = null;
let uninitializedValue: undefined = undefined

2.3 ArkTS复合数据类型

2.3.1 Array(数组)

  • array 用于存储多个值。可以是同一类型或不同类型的值(根据定义的类型)
  • 支持常见的数组操作,如遍历、索引访问、数组方法等。

示例:

ArkTS 复制代码
let numbers:number[]=[1,2,3,4,5];
let names:string[]=["Alice","Bob","Charlie"];

2.3.2 Tuple(元组)

  • Tuple是一种固定大小、已知类型的数组。每个元素可以有不同类型

示例:

ArkTS 复制代码
let person:[string,number]=["Bob",12];
  • 元组允许存储不同类型的数据,例如:[string,number]代表一个包含字符串和数组的元组

2.3.3 Object(对象)

  • Object类型是所有引用类型的基类型。
  • 任何值,包括基本类型的值,都可以直接被赋给Object类型的变量(基本类型值会被自动装箱)
  • Object类型用于表示除基本类型外的类型

示例:

ArkTS 复制代码
let o1: Object = 'Alice';
let o2: Object = ['a','b'];
let o3: Object = 1;

2.3.4 Function(函数类型)

  • Function类型用于表示函数的类型,可以指定函数的参数类型和返回值类型

示例:

ArkTS 复制代码
let add: (x: number, y: number) => number = (x, y) => x + y;
let greet: (name: string) => void = (name) => console.log('hello,'+name);

add(12,23)
greet("Bob")

2.3.5 Literal Types(字面量类型)

  • 字面量类型用于表示某个具体的值,而不是值的类型

示例:

ArkTS 复制代码
let color: "red" | "green" | "blue" = "red"
let status: 1 | 2 | 3 = 2

在这个示例中,color 就只能是 redgreenred这三者中的其中之一,status同理。

2.3.6 Enum(枚举)

  • Enum 是一组有名称的常数值,通常用于表示一组固定的选项或状态

示例:

ArkTS 复制代码
enum Direction {
  up = 1,
  down = 2,
  left = 3,
  right = 4
}


let direction = Direction.up

2.3.7 Union(联合类型)

  • Union类型,即联合类型,是由多个类型组合成的引用类型
  • 联合类型包含了变量可能的所有类型
ArkTS 复制代码
let value:string|number="hello"
value=30
// 可以将类型为联合类型的变量赋值为任何组成类型的有效值

2.3.8 Aliases(匿名类型)

  • Aliases类型为匿名类型(如数组、函数、对象字面量或联合类型)提供名称,或为已定义的类型提供替代名称

示例:

ArkTS 复制代码
type Matrix = number[][];
const gameBoard: Matrix = [
  [1, 0],
  [0, 1]
];

2.4 运算符

2.4.1 算术运算符

运算符 说明
+ 加法
- 减法
* 乘法
/ 除法
% 取余(模)
++ 自增
-- 自减

2.4.2 赋值运算符

赋值运算符用于将右边的值赋给左边的变量

运算符 说明
= 赋值,使用方式如 x=y
2.4.2.1 复合赋值运算符

复合赋值运算符将赋值运算符与运算符组合在一起

运算符 说明
+= 加赋值,使用方式如 x += y,等价于 x = x + y
-= 减赋值,使用方式如 x -= y,等价于 x = x - y
*= 乘赋值,使用方式如 x *= y,等价于 x = x * y
/= 除赋值,使用方式如 x /= y,等价于 x = x / y
%= 取余赋值,使用方式如 x %= y,等价于 x = x % y
<<= 左移赋值,示例效果:3 <<= 2 → 12
>>= 右移赋值,示例效果:12 >>= 2 → 3
>>>= 无符号右移赋值,示例效果:-8 >>>= 1 → 大正数
&= 按位与赋值,两位都为1才为1,示例效果:6 &= 3 → 2
` =`
^= 按位异或赋值,相同为 0,不同为 1,示例效果:6 ^= 3 → 5

2.4.3 位运算符

位运算符用于对整数值进行位级别的操作

运算符 说明
& 按位与(两位都为 1 才为 1)
` `
^ 按位异或(相同为 0,不同为 1)
~ 按位非(按位取反,0 变 1,1 变 0)
<< 左移(左移 n 位,相当于乘以 2n
>> 右移(右移 n 位,保留符号位,相当于除以 2n 并向下取整)
>>> 无符号右移(右移后高位补 0,不保留符号位)

2.4.4 比较运算符

比较运算符用于比较两个值,并返回布尔值(true或false)

运算符 说明
=== 如果两个操作数严格相等(对于不同类型的操作数认为是不相等的),则返回true。
!== 如果两个操作数严格不相等(对于不同类型的操作数认为是不相等的),则返回true。
== 如果两个操作数相等,则返回true。
!= 如果两个操作数不相等,则返回true。
> 如果左操作数大于右操作数,则返回true。
>= 如果左操作数大于或等于右操作数,则返回true。
< 如果左操作数小于右操作数,则返回true。
<= 如果左操作数小于或等于右操作数,则返回true。

2.4.5 逻辑运算符

逻辑运算符用于处理布尔值的运算

运算符 说明
&& 逻辑与(AND)
`
! 逻辑非(NOT)

2.4.6 类型运算符

类型运算符用于进行类型检查和类型转换

运算符 示例 说明
instanceof obj instanceof className 如果obj是className类或其子类的实例,则返回值为true;否则,返回值为false

2.5 表达式

2.5.1 if-else 语句

  • if-else语句根据逻辑条件执行不同的语句场景。当条件为真时,执行为真的语句,否则执行另一组语句

示例:

ArkTS 复制代码
if (condition1) {
  //执行满足 condition1为真(即为true) 的语句
} else {
  //执行满足 condition1为真(即为false) 的语句
}

2.5.2 switch-case语句

  • switch-case语句根据不同的case选项,当满足case选项条件时,则执行对应语句

示例:

ArkTS 复制代码
switch (people) {
  case teacher:
    //如果people 是 teacher,则执行这里面语句
    break
  case student:
    //如果people 是 student,则执行这里面语句
    break
  case worker:
    //如果people 是 worker,则执行这里面语句
    break
  default :
    //如果都不满足以上选项,则执行这里面语句
    break
}

2.5.3 循环结构

在 ArkTS 中,循环结构的语法和 TypeScript/JavaScript 基本一致,支持常 见的循环结构如 for 循环、 while 循环和 do-while 循环。下面是对 ArkTS 中各种循环结构的详细介绍。

2.5.3.1 for循环

for 循环是最常见的循环结构,适用于已知循环次数或需要通过计数器控 制的情况

语法

scss 复制代码
for (initialization; condition; increment) { 

// 循环体 

}

示例

ArkTS 复制代码
// 从 0 到 4 打印数字
for (let i = 0; i < 5; i++) {
  console.log(i.toString());
}
  • initialization,也就是示例中let i = 0:初始化表达式,通常用于定义和初始化计数器
  • condition,也就是示例中 i < 5:循环的条件表达式,只有当条件为 续执行。 true 时,循环才会继续执行
  • increment,也就是示例中i++:每次循环结束时执行的增量表达式,通常用于更新计数器。
2.5.3.2 for...of循环
  • for...of 循环用于遍历可迭代对象(如数组、字符串、Map、Set 等)的 值。它不会返回索引或键,而是直接返回集合中的元素。

示例

ArkTS 复制代码
let numbers=[10,202,30]
for (let index of numbers){
  console.log(index.toString())
}
2.5.3.3 while循环
  • while 循环用于在给定条件为 true的情况下重复执行循环体。条件表达式在每次迭代前都会检查

示例:

ArkTS 复制代码
let count = 0;
while (count < 5) {
  console.log(count.toString());
  count++;  // 更新计数器
}
2.5.3.4 do...while循环
  • do...while循环与while循环类似,不同之处在于 do...while循环会至少执行一次(即使不满足条件),因为条件是循环体执行后再判断的。

示例:

ArkTS 复制代码
let count = 0;
do {
  console.log(count.toString());
  count++;  // 更新计数器
} while (count < 5);

注意:当我们使用循环时,一定要有合适的契机结束循环,否则就会造成死循环,要么循环条件为false,要么使用break语句

2.5.3.5 break语句

break语句用于立即退出循环。当满足某个条件时,可以使用break提前终止循环

示例:

ArkTS 复制代码
let count = 0;
do {
  if (count==5) {
    break
  }
  console.log(count.toString());
  count++;  // 更新计数器
} while (count < 10);
2.5.3.6 continue语句

continue 语句用于跳过当前循环的剩余部分,直接进入下一次循环的判断条件部分。

示例:

ArkTS 复制代码
let count = 0;
do {
  count++;  // 更新计数器
  if (count===6) {
    continue
  }
  console.log(count.toString());
} while (count < 10);

在这个例子中 当count等于6时,continue语句会跳过console.log(count.toString());语句及之后的执行,直接进入下一次循环。

2.5.3.7 循环结构总结

ArkTS中,常见的循环结构包括:

  1. for循环:用于已知次数的循环
  2. for...or循环:用于遍历可迭代对象的元素
  3. while循环:根据条件重复执行循环体
  4. do...while循环:至少执行一次循环,之后根据条件判断是否继续
  5. break语句:提前退出循环
  6. continue语句:跳过当前循环剩余部分,直接进入下一次循环

这些循环结构都可以帮助你在 ArkTS 中处理各种需要重复执行的任务,根 据具体需求选择适合的循环类型。

2.6 函数

ArkTS 中,函数的定义和使用与 TypeScript 大体相同,但由于它是专门为鸿蒙系统(HarmonyOS)设计的,可能会有一些特有的扩展。下面我将展示一些常见的 ArkTS 函数使用方法,帮助你更好地理解如何在 ArkTS 中创建和使用函数。

2.6.1 函数的定义

ArkTS 中,定义函数的语法与 TypeScript 类似,可以通过function关键字定义普通函数,也可以使用箭头函数。

2.6.1.1 普通函数定义
ArkTS 复制代码
function greet1(name:string):string{
  return `Hello,${name}!`
}

console.log(greet1("ArkTS"))
  • 函数声明引入一个函数,包含其名称、参数列表、返回类型和函数体
  • 参数类型标注:name:string 显式声明参数类型为字符串类型
  • 返回值类型:string指定函数返回值为字符串类型
2.6.1.2 箭头函数定义(Lambda函数)
ArkTS 复制代码
const greet2 = (name: string): string => {
  return `Hello,${name}!`
}

console.log(greet2("hqk"))



let sum=(x:number,y:number):number=>{
  return x+y
}
let sum1=(x:number,y:number)=>{
  return x+y
}
let sum2=(x:number,y:number)=>x+y

console.log("sum="+sum(1,2))     //输出3
console.log("sum1="+sum1(1,2))   //输出3
console.log("sum2="+sum2(1,2))   //输出3

注意看sum/sum1/sum2 函数定义有细微差别

  • 箭头函数在没有指定返回类型时,会根据返回的表达式自动推断类型
  • 带函数体 {} 的箭头函数(需要写 显式 return)
  • 只有表达式的箭头函数(不需要写 隐式 return)

因此 sum/sum1/sum2输出都为3

那么现在来个实战

按钮点击事件

ArkTS 复制代码
Button('').onClick(()=>{})

这里面就用到了Lambda函数,相信看了sum/sum1/sum2 函数定义差别,你能看懂这个按钮点击事件案例

2.6.2 函数的参数和返回类型

  • 和 TypeScript 一样,在 ArkTS 中你可以指定函数的参数和返回值类型,确保类型的安全
2.6.2.1 带参数类型和返回类型的函数
ArkTS 复制代码
function addNumber(a:number,b:number):string{
  return (a+b).toString()
}

console.log(`add=${addNumber(1,2)}`)
2.6.2.2 可选参数
ArkTS 复制代码
function greet1(name: string,age?:number): string {
  if (age) {
    return `Hello,${name}! You are ${age} years old.`
  }

  return `Hello,${name}!`
}


console.log(`greet1->${greet1("Bob")}`) //greet1->Hello,Bob!

console.log(`greet1->${greet1("Alice",24)}`)//greet1->Hello,Alice! You are 24 years old.
2.6.2.3 默认参数
ArkTS 复制代码
function greet1(name: string,age:number=18): string {
  return `Hello,${name}! You are ${age} years old.`
}

console.log(`greet1->${greet1("Bob")}`) //greet1->Hello,Bob! You are 18 years old.

console.log(`greet1->${greet1("Alice",24)}`)//greet1->Hello,Alice! You are 24 years old.
2.6.2.4 可变参数(Rest参数)
  • 函数的最后一个参数可以是rest参数。使用rest参数时,允许函数或方法接受任意数量的实参
ArkTS 复制代码
function sumNumber(...numbers: number[]): number {
  let res = 0
  for (let item of numbers) {
    res += item
  }
  return res
}


console.log(`sum1->${sumNumber()}`) //sum1->0
console.log(`sum2->${sumNumber(1, 2, 3, 4, 5, 6, 7)}`) //sum2->28

2.6.3 异步函数(Async/Await)

  • ArkTS 支持异步编程,可以通过 asyncawait 来处理异步操作
2.6.3.1 异步函数定义和调用
ArkTS 复制代码
import fs from '@ohos.file.fs'; //这里引入系统组件并命名为:fs

/**
 * 打开文件:fs:系统提供的用于打开文件的一个组件
 * 异步方法
 */
async function openFile(fileName: string) {
  let file = await fs.open(fileName, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE)
  return file
}

function getData() {
  openFile("userInfo.txt").then((data: fs.File) => {
    console.log(data.name)
  })
}

在这个例子中:

  • openFile是一个异步函数(async),使用await等待文件打开完成并返回结果
  • openFile我这故意没有写返回值,但却有 return file,这里和上面sum/sum1/sum2例子一样,它会根据方法体自动推断方法返回类型为:Promise<fs.File>

2.6.4 回调函数

  • 你也可以在 ArkTS 中传递回调函数作为参数,尤其在 UI 交互中比较常见

示例:

ArkTS 复制代码
function performAction(actionNumber: (a: number, b: number) => number) {
  console.log("Action started.")
  console.log(`Action run->${actionNumber(10, 20)}`)
  console.log("Action completed.")
}


performAction((a, b) => {
  console.log("Action 执行a*b逻辑");
  return a * b;
});



performAction((a, b) => {
  console.log("Action 执行a+b逻辑");
  return a + b;
});

输出内容:

css 复制代码
   Action started.
   Action 执行a*b逻辑
   Action run->200
   Action completed.
   Action started.
   Action 执行a+b逻辑
   Action run->30
   Action completed.

在这个例子中:

  • performAction方法里,actionNumber参数作为未实现的函数/回调函数,(只确定了参数和返回类型)
  • 而在调用 performAction方法的时候,需要实现actionNumber方法的内容

2.6.5 高阶函数

  • 高阶函数是指接受一个或多个函数作为参数,或者返回一个函数的函数。 ArkTS 支持高阶函数

示例:

ArkTS 复制代码
// 高阶函数:接收两个函数,返回一个新函数
function combineActions(
  action1: (x: number) => number,
  action2: (y: number) => number
) {
  // 返回一个新的函数
  return (value: number): number => {
    console.log("执行 action1...");
    const r1 = action1(value);

    console.log("执行 action2...");
    const r2 = action2(r1);

    return r2;
  };
}

// 定义两个普通箭头函数
let add10 = (num: number) => num + 10;
let multiply2 = (num: number) => num * 2;

// 调用高阶函数,得到一个"组合函数"
let combined = combineActions(add10, multiply2);

// 使用返回的函数
console.log("最终结果:" + combined(5));

输出内容

erlang 复制代码
执行 action1...
执行 action2...
最终结果:30

在这个例子中

  • 高阶函数能把"行为"当成参数传递,使逻辑更灵活、更复用、更易扩展。

2.6.6 函数重载

与 TypeScript 相同,ArkTS 也支持函数重载。你可以定义多个具有不同参 数列表的函数,但是要注意,重载必须根据参数类型或数量来区分

示例:

ArkTS 复制代码
//函数重载
function greet(name: string): string;
function greet(name: string, age: number): string;
function greet(name: string, age?: number): string {
  if (age !== undefined) {
    return `Hello, ${name}! You are ${age} yearsold.`;
  } else {
    return `Hello, ${name}!`;
  }
}
console.log(greet('Ali')) //Hello, Ali!
console.log(greet('Bob',20))//Hello, Bob! You are 20 yearsold.
console.log(greet('hqk', 30))//Hello, hqk! You are 30 yearsold.

2.7 类与对象

2.7.1 类声明

类声明引入一个新类型,并定义其字段、方法和构造函数

示例:

ArkTS 复制代码
export class Person {
  name: string = ''
  surName: string = ''

  constructor(name: string, surName: string) {
    this.name = name
    this.surName = surName
  }

  fullName(): string {
    return `${this.name} ${this.surName}`
  }
}

定义类后,可以使用关键字new创建实例

ArkTS 复制代码
import { Person} from '../utils/code'
let p=new Person('John','Smith')
console.log(p.fullName()) //John Smith

2.7.2 字段

字段是直接在类中声明的某种类型的变量。 类可以具有实例字段或者静态字段。

2.7.2.1 实例字段

实例字段存在于类的每个实例上。每个实例都有自己的实例字段集合。 要访问实例字段,需要使用类的实例

ArkTS 复制代码
class Person1 {
  name: string = ''
  age:number=0

  constructor(name: string, age: number) {
    this.name = name
    this.age = age
  }

  public getName(): string {
    return this.name
  }
}


let p1 = new Person1("Alice", 25)
p1.name

let p2 = new Person1("Bob", 28)
p2.name
2.7.2.2 静态字段

使用关键字static将字段声明为静态。静态字段属于类本身,类的所有实例共享一个静态字段

ArkTS 复制代码
class Person1 {
  name: string = ''
  age:number=0

  static numberOfPersons = 0

  constructor(name: string, age: number) {
    this.name = name
    this.age = age
    Person1.numberOfPersons++
  }

  public getName(): string {
    return this.name
  }
}

let p1 = new Person1("Alice", 25)

let p2 = new Person1("Bob", 28)

console.log(`number->${Person1.numberOfPersons}`)

在这个例子中:

  • 要访问静态字段,需要使用类名
2.7.2.3 字段初始化

ArkTS要求所有字段在声明时或者构造函数中显式初始化。这和标准TS中的 strictPropertyInitialization模式一样。

以下示例是在ArkTS中不合法的代码

ArkTS 复制代码
 class Person2{
  name:string  

  setName(name:string):void{
    this.name=name
  }

  getName():string{
    return this.name
  }
}

如果你这样写,编译器直接提示:Property 'name' has no initializer and is not definitely assigned in the constructor. <ArkTSCheck>要么定义时赋值,要么构造器方法赋值

2.7.2.4 getter和setter

setter和getter可用于提供对对象属性的受控访问

ArkTS 复制代码
class Person2{
  name:string=''
  private _age: number = 0

  public set age(value: number) {
    if (value<0) {
      throw Error('Invalid age argument')
    }
    this._age = value
  }

  public get age(): number {
    return this._age
  }

}


let p=new Person2()
console.log(`age=${p.age}`) //age=0 
p.age=-10 // 设置无效age值会抛出错误

在这个例子中:

  • 在类中可以定义getter或者setter
  • setter用于禁止将age属性设置为无效值

2.7.3 方法

方法属于类。类可以定义实例方法或者静态方法。静态方法属于类本身,只能访问静态字段。而实例方法既可以访问静态字段,也可以访问实例字段, 包括类的私有字段。

2.7.3.1 实例方法

示例:

ArkTS 复制代码
class RectangleSize{
  private height:number=0
  private width:number=0

  constructor(height: number, width: number) {
    this.height = height
    this.width = width
  }

  calculateArea():number{
    return this.height*this.width
  }
}


let square=new RectangleSize(10,10)
console.log(`square=>${square.calculateArea()}`) //输出 square=>100

在这个例子中:

  • calculateArea方法通过将高度乘以宽度来计算矩形的面积
  • 必须通过类的实例调用实例方法
2.7.3.2 静态方法

示例:

ArkTS 复制代码
class C1{
  aa:string=''
  
  static a:number=10
  
  static staticMethod():string{
    return `this is a static method. ${C1.a}`
  }
}

console.log(C1.staticMethod())

在这个例子中:

  • 使用static将方法声明为静态。静态方法属于类本身,只能访问静态字段
  • 静态方法定义了类作为一个整体的公共行为
  • 必须通过类名调用静态方法
2.7.3.3 继承

一个类可以继承另一个类(称为基类)

语法:

javascript 复制代码
class [extends BaseClassName]{ 
// ... 

}

示例:

ArkTS 复制代码
export class Employee{
  //属性
  name: string = ''
  private sex: string = ''
  hiredate: Date | null = null
  sal: number = 0
  comm: number = 100
  // 构造方法
  constructor(name: string, sex: string, hiredate: Date, sal: number) {
    this.name = name
    this.sex = sex
    this.hiredate = hiredate
    this.sal = sal
  }
  work(){
    let name = 'hello'
    console.log(`${name},${this.name}:正在努力工作。。。`)
  }
  getSalary(){
    console.log(`${this.name}:正在领取工资`)
  }

}

/*
程序员类
*
 */
export class Programmer extends Employee{

  getSalary(): void {
   //重写父类方法
  }
}


import { Employee, President, Programmer } from '../utils/code1';


const e1 = new Programmer('jett', '男', new Date('2024-1-22'), 8000)
e1.work()  // 打印  hello,jett:正在努力工作。。。
e1.getSalary() //没有任何打印
  • 继承类继承基类的字段和方法,但不能继承构造函数(子类如果没有写构造函数,ArkTS会默认生成了 constructor 来调用 super)。
  • 继承类可以新增定义字段和方法,也可以覆盖其基类定义的方法
  • 基类也称为"父类"或"超类"。继承类也称为"派生类"或"子类"
2.7.3.4 实现

一个类可以实现多个接口

语法:

php 复制代码
class [implements listOfInterfaces] { 
// ... 

}

示例:

ArkTS 复制代码
interface DateInterface {
  now(): string
}

export class MyDate implements DateInterface {
  now(): string {
    return 'now is now'
  }
}

在这个示例中:

  • 包含implements子句的类必须实现列出的接口中定义的所有方法
2.7.3.5 父类访问

关键字super可用于访问父类的实例字段、实例方法和构造函数。在实现子类功能时,可以通过该关键字从父类中获取所需接口

示例:

ArkTS 复制代码
class RectangleSize {
  protected  height: number = 0
  private width: number = 0

  constructor(height: number, width: number) {
    this.height = height
    this.width = width
  }

  calculateArea(): number {
    return this.height * this.width
  }

  /**
   * 绘制
   */
  draw() {
  }
}

class FilledRectangle extends RectangleSize {
  color = ''

  constructor(height: number, width: number, c: string) {
    super(height, width) //父类构造函数调用
    this.color = c
  }

  override draw(): void {
    super.draw() //父类方法调用
    super.height//调用父类属性
    //
  }
}
2.7.3.6 方法重写

子类可以重写其父类中定义的方法的实现。重写的方法可以用关键字 override标记,以提高可读性。重写的方法必须具有与原始方法相同的参数 类型和相同或派生的返回类型

示例:

ArkTS 复制代码
class RectangleSize {
   //....
   
  area(): number {
    return 0
  }
}

class Square extends RectangleSize {
  private side: number = 5

  override area(): number {
    return this.side * this.side
  }
}
2.7.3.7 方法重载

通过重载签名,指定方法的不同调用。具体方法为:同一个方法写入多个同名但签名不同的方法头,方法实现紧随其后。

示例:

ArkTS 复制代码
class C{
  foo(x:number):void  //第一个签名
  foo(x:string):void  //第二个签名
  foo(x:number|string):void{ //实现签名
    
  }
}
let c=new C()
c.foo(100)   //使用第一个签名
c.foo("aaa") //使用第二个签名

2.7.4 构造函数

类声明可以包含用于初始化对象状态的构造函数

语法如下:

javascript 复制代码
constructor ([parameters]) { 
// ...
}

如果未定义构造函数,则会自动创建具有空参数列表的默认构造函数,例如:

ArkTS 复制代码
class Point {
  x: number = 0
  y: number = 0
}

let p = new Point()
p.x
p.y

在这种情况下,默认构造函数使用字段类型的默认值来初始化实例中的字段

2.7.4.1 派生类的构造函数

构造函数函数体的第一条语句可以使用关键字super来显式调用直接父类的构造函数

ArkTS 复制代码
class RectangleSize {
  protected height: number = 0
  private width: number = 0
  constructor(height: number, width: number) {
    this.height = height
    this.width = width
  }
}

class Square extends RectangleSize {
  private side: number = 5
  constructor(side: number) {
    super(side, side)// 使用关键字super
    this.side = side
  }

}
2.7.4.2 构造函数重载

我们可以通过编写重载签名,指定构造函数的不同调用方式。具体方法为, 同一个构造函数写入多个同名但签名不同的构造函数头,构造函数实现紧随其后 (和上面方法重载类似)

ArkTS 复制代码
class C {
  constructor(x:number)
  constructor(x:string)
  constructor(x:number|string) {

  }
}

let c = new C(10)
let c1=new C("aaa")

2.7.5 修饰符

类的方法和属性都可以使用可见性修饰符

可见性修饰符包括:privateprotectedpublic。默认修饰符为:public

2.7.5.1 public与private

示例:

ArkTS 复制代码
class C{
  public x:string=''
  private y: string = ''

  public set_y(value: string) {
    this.y = value // y在类本身可以访问
  }
}
let c=new C()
c.x='asdf' //OK,该字段是公有的
c.y='bbb'  //编译时错误,该字段是私有不可见的

在这个示例中:

  • public (默认:公有)修饰的类成员(字段、方法、构造函数)在程序的任何可访问该类的地方都是可见的
  • private(私有)修饰的成员不能在声明该成员的类之外访问
2.7.5.2 protected

示例:

ArkTS 复制代码
class Base{
  protected x:string=''
  private y:string=''
  z:string=''
}

class Derived extends Base{
  foo(){
    this.x='a' //OK,该字段是protected(受保护的),允许在子类使用
    this.y='b' //编译时错误,该字段是私有不可见的
  }
}

let d=new Derived()
d.z  //OK,该字段是公有的
d.x  //编译时错误,该字段是protected(受保护的)

在这个示例中

  • protected修饰符的作用于private修饰符非常相似,不同点是protected修饰符的成员允许在派生类访问

2.7.6 对象字面量

对象字面量是一个表达式,可用于创建类实例并提供一些初始值。它在某些情况下更方便,可以用来代替new表达式。

对象字面量的表示方式是:封闭在花括号对({})中的'属性名:值'的列表

示例:

ArkTS 复制代码
class C{
  n:number=0
  s:string=''
}

function foo(c:C){}

let c:C={n:42,s:'foo'}   //使用变量的类型


foo({n:42,s:'foo'})//使用参数的类型

function bar():C{
  return {n:42,s:'foo'} //使用返回类型
}

在这个例子中:

  • 对象字面量只能在可以推导出该字面量类型的上下文中使用

也可以在数组元素类型或类字段类型中使用:

ArkTS 复制代码
class C{
  n:number=0
  s:string=''
}

let cc:C[]=[{n:1,s:'a'},{n:2,s:'b'}]
2.7.6.1 Record类型的对象字面量

泛型Record<K,V>用于将类型(键类型)的属性映射到另一个类型(值类型)。常用对象字面量来初始化该类型的值

示例:

ArkTS 复制代码
let map: Record<string, number> = { 'John': 25, "Mary": 21 }

map['John']   //25

类型K可以是字符串类型或数值类型,而V可以是任意类型

示例:

ArkTS 复制代码
interface PersonInfo {
  age: number
  salary: number
}

let map:Record<string,PersonInfo>={
  'John':{age:25,salary:10},
  "Mary":{age:21,salary:20}
}

2.7.7 接口

上面的很多示例都用到接口,这里再统一讲解下

2.7.7.1 接口声明

任何一个类的实例只要实现了特定接口,就可以通过该接口实现多态。

接口通常包含属性和方法声明

示例:

ArkTS 复制代码
interface Style{
  color:string //属性
}

interface AreaSize{
  calculateAreaSize():number //方法的声明
  someMethod():void //方法的声明
}

实现接口示例:

ArkTS 复制代码
interface AreaSize{
  calculateAreaSize():number //方法的声明
  someMethod():void //方法的声明
}

class RectangleSize2 implements AreaSize{
  private width:number=0
  private height:number=0
  
  calculateAreaSize(): number {
    this.someMethod()
    return this.width*this.height
  }

  someMethod(): void {
    console.log('someMethod called')
  }
}
2.7.7.2 接口属性

接口属性可以是字段、getter、setter或getter和setter组合的形式。

属性字段只是getter/setter对的便捷写法。以下表达方式是等价的(示例一、示例二):

示例一:

ArkTS 复制代码
interface Style {
  color: string;
}

示例二:

ArkTS 复制代码
interface Style {
  get color(): string;
  set color(x: string);
}

实现接口的类也可以使用以下两种方式(示例三、示例四):

示例三:

ArkTS 复制代码
interface Style {
  color: string;
}

class StyledRectangle implements Style {
  color: string = '';
}

示例四

ArkTS 复制代码
interface Style {
  color: string;
}

class StyledRectangle implements Style {
  private _color: string = '';
  get color(): string { return this._color; }
  set color(x: string) { this._color = x; }
}
2.7.7.3 接口继承

接口可以继承其他接口

ArkTS 复制代码
interface Style {
  color: string;
}

interface ExtendedStyle extends Style {
  width: number;
}

继承接口包含被继承接口的所有属性和方法,还可以添加自己的属性和方法

2.7.8 泛型

泛型类型和函数允许创建的代码在各种类型上运行,而不仅支持单一类型

2.7.8.1 泛型类和接口

类和接口可以定义为泛型,将参数添加到类型定义中。如以下示例中的类型参数Element:

ArkTS 复制代码
class CustomStack<Element> {
  public push(e: Element):void {
    // ...
  }
}

要使用类型CustomStack,必须为每个类型参数指定类型实参:

ArkTS 复制代码
let s = new CustomStack<string>();
s.push('hello');

编译器在使用泛型类型和函数时会确保类型安全。参见以下示例:

ArkTS 复制代码
let s = new CustomStack<string>();
s.push(55); // 将会产生编译时错误
2.7.8.2 泛型约束

泛型类型的类型参数可以绑定。例如,HashMap<Key,Value>容器中的Key 类型参数必须具有哈希方法,即它应该是可哈希的

示例:

ArkTS 复制代码
interface Hashable {
  hash(): number;
}
class MyHashMap<Key extends Hashable, Value> {
  public set(k: Key, v: Value) {
    let h = k.hash();
    // ...其他代码...
  }
}

在这个例子中:

  • key类型扩展了Hashable,Hashable接口的所有方法都可以被key调用
2.7.8.3 泛型函数

使用泛型函数可编写更通透的代码。

比如返回数组的最后一个元素的函数:

ArkTS 复制代码
function last(x:number[]):number{
  return x[x.length-1]
}

last([1,2,3])  //3

如果需要为任何数组定义相同的函数,使用类型参数讲函数定义为泛型:

ArkTS 复制代码
function last<T>(x:T[]):T{
  return x[x.length-1]
}

//显示设置类型实参
last<string>(['aa','bb'])
last<number>([1,2,3])

//隐式设置类型实参
last([1,2,3])  //3
last(['a','b','c'])

在这个例子中:

  • 在函数调用中,类型实参可以显式或隐式设置
2.7.8.4 泛型默认值

泛型类型的类型参数可以设置默认值。这样可以不指定实际的类型实参,而只使用泛型类型名称

ArkTS 复制代码
class SomeType{

}

interface Interface<T1=SomeType>{

}
class Base<T2=SomeType>{}

class Derived1 extends Base implements Interface{}
//这里 Derived1与Derived2等价
class Derived2 extends Base<SomeType> implements Interface<SomeType>{}

function food<T=number>(): void{}

food() //number
//此函数在语义上等价下面函数调用
food<number>()

//这里自定义泛型为string类型
food<string>()

在这个示例中:

  • InterfaceBase对应泛型默认值为SomeType类型,因此Derived1等价于Derived2
  • 方法food对应泛型默认为number类型,因此food() 等价于 food<number>()

如果说 泛型每次使用时都必须显式指定类型 以及 泛型没有"天然的主流类型" ,那就没有必要使用泛型默认值

2.7.9 空安全、非空断言运算符、空值合并运算符、可选链

2.7.9.1 空安全

默认情况下,ArkTS中的所有类型都不允许为空,这类似于TypeScript的(strictNullChecks)模式,但规则更严格。

在下面的示例中,所有行都会导致编译时错误:

ArkTS 复制代码
let x: number = null;    // 编译时错误
let y: string = null;    // 编译时错误
let z: number[] = null;  // 编译时错误

可以为空值的变量定义为联合类型T|null

ArkTS 复制代码
let x:number|null=null
x=1
x=null
if (x!=null){
  //do something
}
2.7.9.2 非空断言运算符

后缀运算符!可用于断言其操作数为非空。

应用于空值时,运算符将抛出错误。否则,值的类型将从T | null更改为T

示例:

ArkTS 复制代码
class C {
  value: number | null = 1
}

let c1=new C()
let y:number
y=c1.value+1   //编译错误:无法对可空值做加法
y=c1.value!+1  //ok,值为2
2.7.9.3 空值合并运算符

空值合并二元运算符??用于检查左侧表达式的求值是否等于null或者undefined。如果是,则表达式的结果为右侧表达式;否则,结果为左侧表达式。

换句话说,a ?? b等价于三元运算符(a != null && a != undefined) ? a : b。

在以下示例中,getNick方法返回已设置的昵称。如果未设置,则返回空字符串。

ArkTS 复制代码
class Person3 {
  nick: string | null = null

  getNick(): string {
    return this.nick ?? ''
  }
}
2.7.9.4 可选链

在访问对象属性时,如果该属性是undefined或者null,可选链运算符会返回undefined

示例:

ArkTS 复制代码
class Person3 {
  nick: string | null = null;
  spouse?: Person3

  setSpouse(spouse: Person3): void {
    this.spouse = spouse;
  }

  getSpouseNick(): string | null | undefined {
    return this.spouse?.nick;
  }

  constructor(nick: string) {
    this.nick = nick;
    this.spouse = undefined;
  }
}

在这个示例中:

  • getSpouseNick的返回类型必须为string | null | undefined,因为该方法可能返回null或者undefined
  • 可选链可以任意长,可以包含任意数量的?.运算符

在以下示例中,如果Person实例的spouse属性不为空,并且spouse的nick属性也不为空时,输出spouse.nick。否则,输出undefined

ArkTS 复制代码
class Person {
  nick: string | null = null;
  spouse?: Person;

  constructor(nick: string) {
    this.nick = nick;
    this.spouse = undefined;
  }
}

let p: Person = new Person('Alice');
p.spouse?.nick; // undefined

3、结束语

好了,HarmonyOS零基础语法到这就结束了,看到这里的小伙伴恭喜你掌握了ArkTS语法。后期我会持续更新该系列文章。

相关推荐
HarmonyOS_SDK1 小时前
手写套件五大能力,让创作办公高效流畅
harmonyos
AAA阿giao1 小时前
大厂面试之反转字符串:深入解析与实战演练
前端·javascript·数据结构·面试·职场和发展·编程技巧
爱笑的眼睛111 小时前
深入探索HarmonyOS ArkTS异步编程模式
华为·harmonyos
专业抄代码选手1 小时前
告别“屎山”:用 Husky + Prettier + ESLint 打造前端项目的代码基石
前端
想进字节冲啊冲2 小时前
Vibe Coding 实战指南:从“手写代码”到“意图设计”的前端范式转移
前端·ai编程
QuantumLeap丶2 小时前
《Flutter全栈开发实战指南:从零到高级》- 17 -核心动画
android·flutter·ios
2501_915921432 小时前
抓包技术全面指南:原理、工具与应用场景
android·ios·小程序·https·uni-app·iphone·webview
离&染2 小时前
宝塔nginx一个域名部署两个前端和两个后端(VUE3)
前端·nginx
TT哇2 小时前
【计算机网络】经典易错题 1.概述 2 物理层 3.数据链路层 4.网络层
android·服务器·计算机网络