环境
安装版本:DevEco Studio NEXT Developer Beta2 (2024/07/20)
创建工程
文件>新建项目>Application>Empty Ability
Hello World
// 导入页面路由模块
import { router } from '@kit.ArkUI';
import { BusinessError } from '@kit.BasicServicesKit';
@Entry
@Component
struct Index {
@State message: string = 'Hello World'
build() {
Row() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
// 添加按钮,以响应用户点击
Button() {
Text('Next')
.fontSize(30)
.fontWeight(FontWeight.Bold)
}
.type(ButtonType.Capsule)
.margin({
top: 20
})
.backgroundColor('#0D9FFB')
.width('40%')
.height('5%')
// 跳转按钮绑定onClick事件,点击时跳转到第二页
.onClick(() => {
console.info(`Succeeded in clicking the 'Next' button.`)
// 跳转到第二页
router.pushUrl({ url: 'pages/Second' }).then(() => {
console.info('Succeeded in jumping to the second page.')
}).catch((err: BusinessError) => {
console.error(`Failed to jump to the second page. Code is ${err.code}, message is ${err.message}`)
})
})
}
.width('100%')
}
.height('100%')
}
}
添加第二个页面
配置路由entry / src / main / resources / base / profile / main_pages.json:
{
"src": [
"pages/Index",
"pages/Second"
]
}
第二个页面:
// Second.ets
// 导入页面路由模块
import { router } from '@kit.ArkUI';
import { BusinessError } from '@kit.BasicServicesKit';
@Entry
@Component
struct Second {
@State message: string = 'Hi there'
build() {
Row() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
Button() {
Text('Back')
.fontSize(25)
.fontWeight(FontWeight.Bold)
}
.type(ButtonType.Capsule)
.margin({
top: 20
})
.backgroundColor('#0D9FFB')
.width('40%')
.height('5%')
// 返回按钮绑定onClick事件,点击按钮时返回到第一页
.onClick(() => {
console.info(`Succeeded in clicking the 'Back' button.`)
try {
// 返回第一页
router.back()
console.info('Succeeded in returning to the first page.')
} catch (err) {
let code = (err as BusinessError).code;
let message = (err as BusinessError).message;
console.error(`Failed to return to the first page. Code is ${code}, message is ${message}`)
}
})
}
.width('100%')
}
.height('100%')
}
}
页面跳转
在 Index.ets 导入页面路由模块 router:
// Index.ets
// 导入页面路由模块
import { router } from '@kit.ArkUI';
import { BusinessError } from '@kit.BasicServicesKit';
将 Next 按钮绑定 onClick 事件:
// Index.ets
// ...
// 添加按钮,以响应用户点击
Button() {
Text('Next')
.fontSize(30)
.fontWeight(FontWeight.Bold)
}
.type(ButtonType.Capsule)
.margin({
top: 20
})
.backgroundColor('#0D9FFB')
.width('40%')
.height('5%')
// 跳转按钮绑定onClick事件,点击时跳转到第二页
.onClick(() => {
console.info(`Succeeded in clicking the 'Next' button.`)
// 跳转到第二页
router.pushUrl({ url: 'pages/Second' }).then(() => {
console.info('Succeeded in jumping to the second page.')
}).catch((err: BusinessError) => {
console.error(`Failed to jump to the second page. Code is ${err.code}, message is ${err.message}`)
})
})
Second.ets 的 back 按钮类似。
效果
ArkTS 基本组成
基础
声明
变量
关键字 let
let hi: string = 'hello';
hi = 'hello, world';
常量
关键字 const
const hi: string = 'hello';
自动类型推断
有初始值时,不用显示指定类型
let hi = 'hello';
类型
Number 类型
包括整数和浮点数
let n1 = 3.14;
let n2 = 3.141592;
let n3 = .5;
let n4 = 1e10;
// 阶乘
function factorial(n: number): number {
if (n <= 1) {
return 1;
}
return n * factorial(n - 1);
}
Boolean 类型
true 和 false
let isDone: boolean = false;
// ...
if (isDone) {
console.log ('Done!');
}
String 类型
(’)或(")或(`)
let s1 = 'Hello, world!\n';
let s2 = 'this is a string';
let a = 'Success';
let s3 = `The result is ${a}`;
Void 类型
用于指定函数没有返回值
// 定义一个返回类型为 void 的函数
function logMessage(message: string): void {
console.info(message);
}
Object 类型
对象
Array 类型
数组
let names: string[] = ['Alice', 'Bob', 'Carol'];
Enum 类型 | 枚举类型
enum ColorSet { White = 0xFF, Grey = 0x7F, Black = 0x00 }
let c: ColorSet = ColorSet.Black;
Union 类型
联合类型,由多个类型组合成的引用类型
class Cat { sleep () {}; meow () {} }
class Dog { sleep () {}; bark () {} }
class Frog { sleep () {}; leap () {} }
type Animal = Cat | Dog | Frog | number
let animal: Animal = new Frog();
if (animal instanceof Frog) {
let frog: Frog = animal as Frog; // animal在这里是Frog类型
animal.leap();
frog.leap();
// 结果:青蛙跳了两次
}
animal.sleep (); // 任何动物都可以睡觉
Aliases 类型
为匿名类型(数组、函数、对象字面量或联合类型)提供名称,或为已有类型提供替代名称。
type Matrix = number[][];
type Handler = (s: string, no: number) => string;
type Predicate <T> = (x: T) => Boolean;
type NullableObject = Object | null;
运算符
赋值
=
复合赋值
+=、-=、*=、/=、%=、<<=、>>=、>>>=、&=、|=、^=
比较
==、!=、>、>=、<、<=
算术
一元
-、+、–、++
二元
-、+、*、/、%
位运算符
运算符 | 说明 |
---|---|
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。 |
逻辑运算符
&&、|| 、!
语句
If 语句
if (condition1) {
// 语句1
} else if (condition2) {
// 语句2
} else {
// else语句
}
Switch 语句
switch (expression) {
case label1: // 如果label1匹配,则执行
// ...
// 语句1
// ...
break; // 跳出 switch
case label2:
case label3: // 如果label2或label3匹配,则执行
// ...
// 语句2, 语句3
// ...
break; // 可省略
default:
// 默认语句
}
条件表达式
// 真为 1 ,假为 2
condition ? expression1 : expression2
For 语句
let sum = 0;
for (let i = 0; i < 10; i += 2) {
sum += i;
}
For – of 语句
用于遍历数组或字符串
for (let char of 'a string object') {
// 语句
}
While 语句
let n = 0;
let x = 0;
while (n < 3) {
n++;
x += n;
}
Do-while 语句
let i = 0;
do {
i += 1;
} while (i < 10)
Break 语句
终止
Continue语句
停止当前循环迭代,并将控制传递给下一个迭代
Throw 和 Try语句
throw 语句用于抛出异常或错误
throw new Error('this error')
try语句用于捕获和处理异常或错误
try {
// 可能发生异常的语句块
} catch (e) {
// 异常处理
} finally {
// 语句
}
函数
函数声明
function add(x: string, y: string): string {
let z: string = `${x} ${y}`;
return z;
}
可选参数 | name?: Type
调用函数时可省略的参数
可为可选参数设置参数默认值
function multiply(n: number, coeff: number = 2): number {
return n * coeff;
}
multiply(2); // 返回2*2
multiply(2, 3); // 返回2*3
Rest 参数
用在函数的最后一个参数,允许函数或方法接受任意数量的实参
function sum(...numbers: number[]): number {
let res = 0;
for (let n of numbers)
res += n;
return res;
}
sum() // 返回0
sum(1, 2, 3) // 返回6
返回类型
可以显示指定,可推断
// 显式指定返回类型
function foo(): string { return 'foo'; }
// 推断返回类型为string
function goo() { return 'goo'; }
函数的作用域
函数中定义的变量和其他实例仅可以在函数内部访问,不能从外部访问。
如果函数中定义的变量与外部作用域中已有实例同名,则函数内的局部变量定义将覆盖外部定义。
函数类型
函数类型通常用于定义回调:
type trigFunc = (x: number) => number // 这是一个函数类型
function do_action(f: trigFunc) {
f(3.141592653589); // 调用函数
}
// 将函数作为参数传入, 相当于 Math.sin(3.141592653589)
do_action(Math.sin);
箭头函数
// 返回类型可省略
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
闭包
// 外部函数, 外部参数
function outerFunction(outerVariable: string) {
// 返回内部函数, 且该内部函数保持了对外部参数、内部参数的引用
return function innerFunction(innerVariable: string) {
console.log('Outer Variable: ' + outerVariable);
console.log('Inner Variable: ' + innerVariable);
}
}
// 执行外部函数 outerFunction()
// 然后 newFunction 对其返回的 innerFunction() 实例引用
// 同时维持 innerFunction() 实例的环境引用, 包括 'outside'
const newFunction = outerFunction('outside');
newFunction('inside');
计数器
// 外部函数
function createCounter() {
let count = 0;
return function() {
count++;
return count;
}
}
// counter 维持了对 count 的引用
const counter = createCounter();
console.log(counter()); // 输出: 1
console.log(counter()); // 输出: 2
console.log(counter()); // 输出: 3
函数重载
// 定义重载签名
function foo(x: number): void;
function foo(x: string): void;
// 定义实现签名
function foo(x: number | string): void {
if (typeof x === 'number') {
console.log(x.toString());
}else {
console.log(x)
}
}
foo(123); // 使用第一个定义
foo('aa'); // 使用第二个定义
类
类声明引入一个新类型,并定义其字段、方法和构造函数。
class Person {
name: string = ''
age: number = 0
constructor (n: string) {
this.name = n;
}
fullName(): string {
return this.name;
}
}
字段
在类中声明的某种类型的变量。
实例字段
存于实例,使用实例名访问。
静态字段
使用关键字 static
将字段声明为静态。
类的所有实例共享一个静态字段。
使用类名访问静态字段。
class Person {
static numberOfPersons = 0
constructor() {
// ...
Person.numberOfPersons++;
// ...
}
}
Person.numberOfPersons;
字段初始化
所有字段在声明时或者构造函数中显式初始化。
class Person {
name: string // 没有初始化, 可能为 undefined
setName(n:string): void {
this.name = n;
}
getName(): string {
// 开发者使用"string"作为返回类型,这隐藏了name可能为"undefined"的事实。
// 更合适的做法是将返回类型标注为"string | undefined",以告诉开发者这个API所有可能的返回值。
return this.name;
}
}
let jack = new Person();
// 假设代码中没有对name赋值,例如调用"jack.setName('Jack')"
jack.getName().length; // 运行时异常:name is undefined
getter 和 setter
class Person {
name: string = ''
private _age: number = 0
get age(): number { return this._age; }
set age(x: number) {
if (x < 0) {
throw Error('Invalid age argument');
}
this._age = x;
}
}
let p = new Person();
p.age; // 输出0
p.age = -42; // 设置无效age值会抛出错误
方法
实例方法
存于实例,使用实例名访问。
静态方法
使用关键字 static
将方法声明为静态。
通过类名调用静态方法。
class Cl {
static staticMethod(): string {
return 'this is a static method.';
}
}
console.log(Cl.staticMethod());
继承
一个类可以继承另一个类(称为基类),并使用实现多个接口:
class [extends BaseClassName] [implements listOfInterfaces] {
// ...
}
继承类继承基类的字段和方法,但不继承构造函数。
继承类可以新增定义字段和方法,也可以覆盖其基类定义的方法。
class Person {
name: string = ''
private _age = 0
get age(): number {
return this._age;
}
}
class Employee extends Person {
salary: number = 0
calculateTaxes(): number {
return this.salary * 0.42;
}
}
实现接口的类必须实现列出的接口中定义的所有方法。
父类访问
关键字 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 {
constructor ([parameters]) {
// ...
}
}
如果未定义构造函数,则会自动创建具有空参数列表的默认构造函数。
可见性修饰符
Public(公有)
public 修饰的类成员(字段、方法、构造函数)在程序的任何可访问该类的地方都是可见的。
Private(私有)
private 修饰的成员不能在声明该成员的类之外访问。
Protected(受保护)
protected 修饰符的作用与 private 修饰符非常相似,不同点是 protected修饰的成员允许在派生类(子类)中访问。
class Base {
protected x: string = ''
private y: string = ''
}
class Derived extends Base {
foo() {
this.x = 'a'; // 访问受保护成员
this.y = 'b'; // 编译时错误,'y'不可见,因为它是私有的
}
}
对象字面量
用于更方便地创建类实例并提供一些初始值。
// 定义
class C {
n: number = 0
s: string = ''
}
// 创建实例
let c: C = {n: 42, s: 'foo'};
Record类型的对象字面量
用于将类型(键类型)的属性映射到另一个类型(值类型)。
let map: Record<string, number> = {
'John': 25,
'Mary': 21,
}
map['John']; // 25
interface PersonInfo {
age: number
salary: number
}
let map: Record<string, PersonInfo> = {
'John': { age: 25, salary: 10},
'Mary': { age: 21, salary: 20}
}
接口
// 接口:
interface AreaSize {
area: number // 属性
calculateAreaSize(): number // 方法的声明
someMethod(): void; // 方法的声明
}
// 接口实现:
class RectangleSize implements AreaSize {
area: number = 0;
private width: number = 0
private height: number = 0
someMethod(): void {
console.log('someMethod called');
}
calculateAreaSize(): number {
this.someMethod(); // 调用另一个方法并返回结果
this.area = this.width * this.height
return this.area;
}
}
接口继承
interface Style {
color: string
}
interface ExtendedStyle extends Style {
width: number
}
泛型类型和函数
使其支持多种数据类型。
泛型类和接口
class CustomStack<Element> {
public push(e: Element):void {
console.log(`${e}`)
}
}
let s = new CustomStack<string>();
s.push('hello');
interface Pair<T, U> {
first: T;
second: U;
}
let pair: Pair<number, string> = { first: 1, second: "two" };
<>
可以对类型进行约束。
泛型函数
function last<T>(x: T[]): T {
return x[x.length - 1];
}
last([1, 2, 3]);
last<string>(['aa', 'bb']);
空安全
默认情况下,ArkTS中的所有类型都是不可为空的。
let x: number = null; // 编译时错误
let y: string = null; // 编译时错误
let z: number[] = null; // 编译时错误
let x: number | null = null;
x = 1; // ok
x = null; // ok
if (x != null) { /* do something */ }
非空断言运算符
使类型不为空,用法为后缀感叹号 !
let b = null;
let x: number;
x = b! + 1;
class C {
value: number | null = 1;
}
let c = new C();
let y: number;
y = c.value + 1; // 编译时错误:无法对可空值作做加法
y = c.value! + 1; // ok,值为2
空值合并运算符
??
,检查左侧表达式的求值是否等于null
或者undefined
。
如果是,则表达式的结果为右侧表达式;否则,结果为左侧表达式。
class Person {
// ...
nick: string | null = null
getNick(): string {
return this.nick ?? ''; // 若昵称为空,则返回空字符串
}
}
可选链
?.
,用于访问对象属性、调用方法以及访问数组元素等场景,
如果该属性是undefined或者null,可选链运算符会返回undefined
let user: { address?: { street?: string } } = {};
console.log(user.address?.street); // 输出: undefined
let user = {
name: "Alice",
greet: () => "Hello"
};
console.log(user.greet?.()); // 输出: "Hello"
let anotherUser = { name: "Bob" };
console.log(anotherUser.greet?.()); // 输出: undefined
模块
程序可划分为多组编译单元或模块。
导出
使用关键字export导出顶层的声明。
export class Point {
x: number = 0
y: number = 0
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
}
export let Origin = new Point(0, 0);
export function Distance(p1: Point, p2: Point): number {
return Math.sqrt((p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y));
}
导入
静态导入
import * as Utils from './utils'
Utils.X // 表示来自Utils的X
Utils.Y // 表示来自Utils的Y
import { X, Y } from './utils'
X // 表示来自utils的X
Y // 表示来自utils的Y
import { X as Z, Y } from './utils'
Z // 表示来自Utils的X
Y // 表示来自Utils的Y
X // 编译时错误:'X'不可见
动态导入
用于实现根据条件导入模块或者按需导入模块。
// say.ts
export function hi() {
console.log('Hello');
}
export function bye() {
console.log('Bye');
}
// index.ets
async function test() {
let ns = await import('./say');
let hi = ns.hi;
let bye = ns.bye;
hi();
bye();
}
导入HarmonyOS SDK的开放能力
导入接口模块:
import UIAbility from '@ohos.app.ability.UIAbility';
从HarmonyOS NEXT Developer Preview 1版本开始引入Kit概念。SDK对同一个Kit下的接口模块进行了封装,开发者在示例代码中可通过导入Kit的方式来使用Kit所包含的接口能力。
其中,Kit封装的接口模块可查看SDK目录下Kit子目录中各Kit的定义。
通过导入Kit方式使用开放能力有三种方式:
-
方式一:导入Kit下单个模块的接口能力。例如:
import { UIAbility } from '@kit.AbilityKit'; // AbilityKit - 程序框架服务 // UIAbility - 包含UI界面的应用组件
-
方式二:导入Kit下多个模块的接口能力。例如:
import { UIAbility, Ability, Context } from '@kit.AbilityKit'; // Ability - UIAbility和ExtensionAbility的基类, // 提供系统配置更新回调和系统内存调整回调
-
方式三:导入Kit包含的所有模块的接口能力。例如:
import * as module from '@kit.AbilityKit';
其中,“module”为别名,可自定义,然后通过该名称调用模块的接口。