前言
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 就只能是 red、green、red这三者中的其中之一,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中,常见的循环结构包括:
for循环:用于已知次数的循环for...or循环:用于遍历可迭代对象的元素while循环:根据条件重复执行循环体do...while循环:至少执行一次循环,之后根据条件判断是否继续break语句:提前退出循环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支持异步编程,可以通过async和await来处理异步操作
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 修饰符
类的方法和属性都可以使用可见性修饰符
可见性修饰符包括:private、protected、public。默认修饰符为: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>()
在这个示例中:
Interface与Base对应泛型默认值为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语法。后期我会持续更新该系列文章。