鸿蒙ArkTS零基础实战:掌握核心语法,开启高效开发之旅!
目录
ArkTS:鸿蒙生态的现代化语言利剑
-
诞生背景与核心定位
-
解决移动开发痛点: 传统开发中逻辑与UI紧密耦合、状态管理复杂、多设备适配困难等挑战。
-
继承与超越: 基于TypeScript (TS),提供静态类型检查、现代语法特性;深度融入鸿蒙声明式UI范式、状态管理、原生能力调用。
-
核心目标: 提升开发效率、应用性能、代码可维护性与跨设备体验一致性。
-
-
ArkTS vs TypeScript:关键增强
-
声明式UI范式: 核心差异!ArkTS使用简洁的声明式语法描述UI(基于
ArkUI框架),而非TS常用的命令式操作DOM。 -
状态管理内置支持: 提供
@State,@Prop,@Link,@Observed,@ObjectLink等装饰器,实现数据变化驱动UI自动更新。 -
渲染控制语法: 内置
if/else,ForEach等条件渲染和循环渲染语法,直接在UI描述中使用。 -
原生能力友好: 提供更符合鸿蒙API调用习惯的封装和类型定义。
-
特定运行时与编译器: 针对鸿蒙系统进行深度优化,追求更高性能。
-
-
ArkTS核心优势
-
开发高效: 声明式UI+响应式编程大幅减少样板代码。
-
性能卓越: 精细化UI更新、跨语言调用优化、AOT编译(部分场景)。
-
多设备归一: 一套ArkTS代码,通过自适应布局、响应式设计、资源管理等机制,高效适配手机、平板、智慧屏、车机等多种设备。
-
生态兼容: 兼容Web开发主流工具链(VS Code等)和部分JS/TS生态库。
-
ArkTS 语法基石:从TypeScript出发
基础知识
声明
声明:ArkTS通过声明引入变量、常量、类型和函数。
声明变量:let 变量名: 变量类型 = 变量值
let abc: string = "hello world";
声明常量:const 常量名: 常量类型 = 常量值,常量无法重新赋值。
const abc: string = "abc";
自动类型推动:如果变量或常量的声明包含了初始值,开发者就不需要显式指定其类型。
// 变量类型为number
let abc = 123;
// 变量类型为字符串
const cba = "cba";
类型
数据类型包括基本数据类型和引用数据类型。基本数据类型包括number、string等简单类型,它们可以准确地表示单一的数据类型。引用类型包括对象、数组和函数等复杂数据结构。
number类型:任何整数和浮点数都可以被赋给number类型变量。
// 十进制整数
let number1 = 123;
// 十六进制整数
let number2 = 0x1F;
// 八进制整数
let number3 = 0o17;
// 二进制整数
let number4 = 0b11;
// 浮点数
let number5 = 3.14;
// 指数
let number6 = 1e2;
boolean类型:由true和false两个逻辑值组成。
let isTrue: boolean = false;
string类型:代表字符序列,可以使用转义字符来表示字符。
let string1: string = "hello world\n";
void类型:void是引用类型,因此它可以用于泛型类型参数。
class Person {
name: string
setName(name: string): void {
this.name = name;
}
}
Object类型:Object类型是所有引用类型的基类型。任何值,包括基本类型的值,都可以直接被赋给Object类型的变量(基本类型值会被自动装箱)。Object类型用于表示除基本类型外的类型。
let object1:object = "Person";
array类型:由可赋值给数组声明中指定的元素类型的数据组成的对象。
let abc: string[] = ['abc', 'bcd', 'cdf']
enum类型:枚举类型,是预先定义的一组命名值的值类型,其中命名值又称为枚举常量。
enum Colors {Red, Green, Blue};
let color: Colors = Colors.Red;
Union类型:联合类型,是由多个类型组合成的引用类型。
class Apple {
name: string = "apple";
}
class Banana {
name: string = "banana";
}
class Orange {
name: string = "orange";
}
type Fruit = Apple | Banana | Orange;
Aliases类型:匿名类型(数组、函数、对象字面量或联合类型)提供名称,或为已有类型提供替代名称。
type Matrix = number[][];
运算符
比较运算符:
| 运算符 | 说明 |
|---|---|
| === | 如果两个操作数严格相等(对于不同类型的操作数认为是不相等的,如string和number),则返回true。 |
| !== | 如果两个操作数严格不相等(对于不同类型的操作数认为是不相等的,如string和number),则返回true。 |
| == | 如果两个操作数相等,则返回true。 |
| != | 如果两个操作数不相等,则返回true。 |
| > | 如果左操作数大于右操作数,则返回true。 |
| >= | 如果左操作数大于或等于右操作数,则返回true。 |
| < | 如果左操作数小于右操作数,则返回true。 |
| <= | 如果左操作数小于或等于右操作数,则返回true。 |
算数运算符:
| 运算符 | 说明 |
|---|---|
| + | 加法 |
| - | 减法 |
| * | 乘法 |
| / | 除法 |
| % | 除法后余数 |
位运算符:
| 运算符 | 说明 |
|---|---|
| a & b | 按位与:如果两个操作数的对应位都为1,则将这个位设置为1,否则设置为0。 |
| a | b | 按位或:如果两个操作数的相应位中至少有一个为1,则将这个位设置为1,否则设置为0。 |
| a ^ b | 按位异或:如果两个操作数的对应位不同,则将这个位设置为1,否则设置为0。 |
| ~ a | 按位非:反转操作数的位。 |
| a << b | 左移:将a的二进制表示向左移b位。 |
| a >> b | 算术右移:将a的二进制表示向右移b位,带符号扩展。 |
| a >>> b | 逻辑右移:将a的二进制表示向右移b位,左边补0。 |
逻辑运算符:
| 运算符 | 说明 |
|---|---|
| a && b | 逻辑与 |
| a || b | 逻辑或 |
| ! a | 逻辑非 |
语句
if语句:if语句用于需要根据逻辑条件执行不同语句的场景。当逻辑条件为真时,执行对应的一组语句,否则执行另一组语句。
let a: number = 3;
let b: number = 2;
if (a > b) {
console.info("a 大于 b")
} else if (a == b) {
console.info("a 等于 b")
} else {
console.info("a 小于 b")
}
switch语句:使用switch语句执行与switch表达式值匹配的代码块。
switch(light) {
case 'red': console.log("当前是红灯")
break;
case 'yellow': console.log("当前是黄灯")
break;
case 'green': console.log("当前是绿灯")
break;
default: console.log("未亮灯")
}
条件表达式:根据第一个表达式的布尔值来返回其他两个表达式之一。
let result = a > b ? "Yes" : "No";
for语句:for语句会被重复执行,直到循环退出语句值为false。
let result = 0;
for (let i = 0; i < 10; i++) {
result += i;
}
for-of语句:使用for-of语句可遍历数组、Set、Map、字符串等可迭代的类型。
for (let ch of 'hello world') {
console.info(ch);
}
while语句:只要condition为真值(转换后为true的值),while语句就会执行statements语句。
let a = 0;
let result = 0;
while (a < 10) {
a++;
result += a;
}
do-while语句:如果condition的值为真值(转换后为true的值),那么statements语句会重复执行。
let a = 0;
let result = 0;
do {
a++;
result += a;
} while (a < 10)
break语句:使用break语句可以终止循环语句或switch。
let a = 5;
while (true) {
a++;
if (a > 10) {
break;
}
}
continue语句:continue语句会停止当前循环迭代的执行,并将控制传递给下一个迭代。
let a = 0;
let result = 0;
while (a < 20) {
if (a % 2 == 1) {
continue;
}
result += a;
}
throw和try语句:throw语句用于抛出异常或错误,try语句用于捕获和处理异常或错误。
class ZeroDivisor extends Error {}
function divide (a: number, b: number): number{
if (b == 0) throw new ZeroDivisor();
return a / b;
}
function process (a: number, b: number) {
try {
let res = divide(a, b);
console.info('result: ' + res);
} catch (x) {
console.error('some error');
}
}
函数
函数声明
函数声明引入一个函数,包含其名称、参数列表、返回类型和函数体。
函数声明:function 函数名(参数): 返回值类型 {}
function add(num1: number, num2: number): number {
return num1 + num2;
}
可选参数
可选参数的格式可为name?: Type。
function callname(name?: string) {
if (name == undefined) {
console.log('hello')
} else {
console.log(`hi, ${name}!`);
}
}
Rest参数
函数的最后一个参数可以是rest参数。rest参数的格式为...restArgs。rest参数允许函数接收一个由剩余实参组成的数组,类型为任意指定类型,用于处理不定数量的参数输入。
function sum(...nums: number[]): number {
let result = 0;
for (let n of nums) {
result += n;
}
return result;
}
sum(1);
sum(1, 2);
sum(1, 2, 3);
返回类型
如果可以从函数体内推断出函数返回类型,则可在函数声明中省略标注返回类型。
// 显示指定回返回类型
function hi(): string {
return 'hi';
}
// 推断返回类型为string
function hello() {
return "hello";
}
函数的作用域
函数中定义的变量和其他实例仅可以在函数内部访问,不能从外部访问。
如果函数中定义的变量与外部作用域中已有实例同名,则函数内的局部变量定义将覆盖外部定义。
let name = "Jack";
function hi() {
let name = "Anny";
console.log(name);
}
hi();
函数调用
调用函数以执行其函数体,实参值会赋值给函数的形参。
function add(num1: number, num2: number): number {
return num1 + num2;
}
add(1, 2);
函数类型
函数类型通常用于定义回调函数。
type addone = (x: number) => number + 1 // 这是一个函数类型
function do_action(f: addone) {
f(1)
}
do_action(Math.cos);
Lambda函数
let sum = (x: number, y: number): number => {
return x + y;
}
闭包
闭包是由函数及声明该函数的环境组合而成的。该环境包含了这个闭包创建时作用域内的任何局部变量。
在下例中,f函数返回了一个闭包,它捕获了count变量,每次调用z,count的值会被保留并递增。
function f(): () => number {
let count = 0;
let g = (): number => { count++; return count; };
return g;
}
let z = f();
z(); // 返回:1
z(); // 返回:2
函数重载
可以通过编写重载,指定函数的不同调用方式。具体方法是,为同一个函数写入多个同名但签名不同的函数头,函数实现紧随其后。
function foo(x: number): void; /* 第一个函数定义 */
function foo(x: string): void; /* 第二个函数定义 */
function foo(x: number | string): void { /* 函数实现 */
}
foo(123); // OK,使用第一个定义
foo('aa'); // OK,使用第二个定义
类
声明类
类声明引入一个新类型,并定义其字段、方法和构造函数。
class Person {
name: string = '';
age: number = 0;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
创建实例
定义类后,可以使用关键字new创建实例。
let p = new Person('jack', 30)
字段
字段是直接在类中声明的某种类型的变量。类可以具有实例字段或者静态字段。
实例字段:实例字段存在于类的每个实例上。每个实例都有自己的实例字段集合。
要访问实例字段,需要使用类的实例。
class Person {
name: string = "";
age: number = 0;
consturctor(name: string, age: number) {
this.name = name;
this.age = age;
}
getName(): string {
return this.name;
}
}
let person1 = new Person('jack', 30);
person1.name;
let person2 = new Person('anny', 29);
person2.getName();
静态字段:使用关键字static将字段声明为静态。静态字段属于类本身,类的所有实例共享一个静态字段。
class Person {
name: string = '';
age: number = 0;
static numberofPerson = 0;
consturctor(name: string, age: number) {
this.name = name;
this.age = age;
Person.numberofPerson++;
}
}
字段初始化:为了减少运行时错误并提高执行性能,ArkTS要求所有字段在声明时或者构造函数中显式初始化。
class Person {
name: string = '';
setName(name: string): void {
this.name = name;
}
getName(): string {
return this.name;
}
}
let person = new Person();
person.getName().length;
接下来的代码展示了当name的值可以是undefined时,应该如何编写代码。
class Person {
name?: string;
setName(name: string): void {
this.name = name
}
getName(): string | undefined {
return this.name
}
}
let person1 = new Person();
person1.getName()?.length();
getter和setter:setter和getter可用于提供对对象属性的受控访问。
class Person {
name: string = '';
private _age: number = 0;
get age(): number { return this._age }
set age(age: number) {
if (age < 0) {
throw Error('无效的年龄参数');
}
this._age = age;
}
}
let p = new Person();
p.age; // 输出0
p.age = -42; // 设置无效age值会抛出错误
方法
类可以定义实例方法或者静态方法。静态方法属于类本身,只能访问静态字段。而实例方法既可以访问静态字段,也可以访问实例字段,包括类的私有字段。
实例方法:
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;
}
}
静态方法:使用关键字static将方法声明为静态。静态方法属于类本身,只能访问静态字段。
class Cl {
static staticMethod(): string {
return 'this is a static method.';
}
}
console.info(Cl.staticMethod());
继承:一个类可以继承另一个类(称为基类),继承类继承基类的字段和方法,但不继承构造函数。继承类可以新增定义字段和方法,也可以覆盖其基类定义的方法。基类也称为“父类”或“超类”。继承类也称为“派生类”或“子类”。
class Person {
name: string = '';
private _age = 0;
get age(): number {
return this._age;
}
}
class Student extends Person {
score: number = 0;
calculateScore(): number {
return score + 100;
}
}
父类访问:关键字super可用于访问父类的实例字段、实例方法和构造函数。
class RectangleSize {
protected height: number = 0;
protected width: number = 0;
constructor (h: number, w: number) {
this.height = h;
this.width = w;
}
draw() {
/* 绘制边界 */
}
}
class FilledRectangle extends RectangleSize {
color = ''
constructor (h: number, w: number, c: string) {
super(h, w); // 父类构造函数的调用
this.color = c;
}
draw() {
super.draw(); // 父类方法的调用
// super.height -可在此处使用
/* 填充矩形 */
}
}
方法重写:子类可以重写其父类中定义的方法的实现。
class RectangleSize {
// ...
area(): number {
// 实现
return 0;
}
}
class Square extends RectangleSize {
private side: number = 0;
area(): number {
return this.side * this.side;
}
}
方法重载签名:通过重载签名,指定方法的不同调用。
class C {
foo(x: number): void; /* 第一个签名 */
foo(x: string): void; /* 第二个签名 */
foo(x: number | string): void { /* 实现签名 */
}
}
let c = new C();
c.foo(123); // OK,使用第一个签名
c.foo('aa'); // OK,使用第二个签名
构造函数
类声明可以包含用于初始化对象状态的构造函数。
class Person {
name: string = "";
age: number = 0;
consturctor(name: string, age: number) {
this.name = name;
this.age = age;
}
getName(): string {
return this.name;
}
}
派生类的构造函数:构造函数函数体的第一条语句可以使用关键字super来显式调用直接父类的构造函数。
class RectangleSize {
constructor(width: number, height: number) {
// ...
}
}
class Square extends RectangleSize {
constructor(side: number) {
super(side, side);
}
}
构造函数重载签名:可以通过编写重载签名,指定构造函数的不同调用方式。
class C {
constructor(x: number) /* 第一个签名 */
constructor(x: string) /* 第二个签名 */
constructor(x: number | string) { /* 实现签名 */
}
}
let c1 = new C(123); // OK,使用第一个签名
let c2 = new C('abc'); // OK,使用第二个签名
可见性修饰符
类的方法和属性都可以使用可见性修饰符。可见性修饰符包括:private、protected和public。默认可见性为public。
Public(公有):public修饰的类成员(字段、方法、构造函数)在程序的任何可访问该类的地方都是可见的。
Private(私有):private修饰的成员不能在声明该成员的类之外访问
class C {
public x: string = '';
private y: string = '';
set_y (new_y: string) {
this.y = new_y; // OK,因为y在类本身中可以访问
}
}
let c = new C();
c.x = 'a'; // OK,该字段是公有的
c.y = 'b'; // 编译时错误:'y'不可见
Protected(受保护):protected修饰符的作用与private修饰符非常相似,不同点是protected修饰的成员允许在派生类中访问
class Base {
protected x: string = '';
private y: string = '';
}
class Derived extends Base {
foo() {
this.x = 'a'; // OK,访问受保护成员
this.y = 'b'; // 编译时错误,'y'不可见,因为它是私有的
}
}
对象字面量
对象字面量是一个表达式,可用于创建类实例并提供一些初始值。它在某些情况下更方便,可以用来代替new表达式。对象字面量的表示方式是:封闭在花括号对({})中的'属性名:值'的列表。
class C {
n: number = 0;
s: string = '';
}
let c: C = {n: 42, s: 'foo'};
Record类型的对象字面量:泛型Record<K, V>用于将类型(键类型)的属性映射到另一个类型(值类型)。
let map: Record<string, number> = {
'John': 25,
'Mary': 21,
}
map['John']; // 25
抽象类
带有abstract修饰符的类称为抽象类。抽象类可用于表示一组更具体的概念所共有的概念。
abstract class X {
field: number;
constructor(p: number) {
this.field = p;
}
}
let x = new X(666) //编译时错误:不能创建抽象类的具体实例
抽象类的子类可以是抽象类也可以是非抽象类。抽象父类的非抽象子类可以实例化。
abstract class Base {
field: number;
constructor(p: number) {
this.field = p;
}
}
class Derived extends Base {
constructor(p: number) {
super(p);
}
}
let x = new Derived(666);
抽象方法:带有abstract修饰符的方法称为抽象方法,抽象方法可以被声明但不能被实现。只有抽象类内才能有抽象方法,如果非抽象类具有抽象方法,则会发生编译时错误。
class Y {
abstract method(p: string) //编译时错误:抽象方法只能在抽象类内。
}
总结
ArkTS不仅是鸿蒙应用开发的语言,更是开启高效、优雅开发之门的钥匙。掌握其声明式UI思想与响应式状态管理精髓,将使您在多设备鸿蒙生态开发中游刃有余。本章为您奠定了坚实的语法基础与核心概念,接下来的旅程中,我们将深入探索UI组件、布局、动画、网络、数据存储等更多精彩内容,助您快速成长为鸿蒙开发高手!拿起键盘,用ArkTS书写您的鸿蒙应用新篇章吧!
更多推荐

所有评论(0)