Bootstrap

ArkTs基础语法

声明

变量声明

let s: string = 'hello';
s = 'hello world';

常量声明

const s:string = 'hello world';

自动类型推断

ArkTs声明变量的时候,如果存在初始值,那么就可以省略变量类型,ArkTs会根据初始值自动推断变量类型。ArkTs是静态类型语言,变量类型在编译时就已经确定,不允许在代码中动态修改变量类型。

let s: string = 'hello';
let s = 'hello';

类型

基础类型

Number

ArkTs提供number和Number类型, 任何整数和浮点数都可以被赋予这两种类型。

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 

Boolean

let isDone: boolean = false;

String

string类型可以用单引号’或者双引号“来包裹,如果要使用字符串模板,那么字符串需要用反引号`来包裹。

let s1 = 'hello';
let s2 = "hello";
let s3 = `hello ${a}`;

引用类型

Void

void一般用于表示函数没有返回值,也可以用于泛型中。

Object

Object是所有类型的基类,任何类型都可以被赋予Object类型的变量。

Array

数组

Enum

枚举类型

enum ClolrSet {Red, Green, Black}
let color : ColorSet = ColorSet.Red;

也可以给枚举常量显示赋值

enum ColorSet {Red = 0xff}

Union

union,联合类型,由多个类型联合组成的引用类型,联合类型的变量,可能是联合类型中任意一种类型。联合类型需要用type关键字来进行定义。

type A = number | string
let a : A = 3;
a = 'abc'; 

据鸿蒙官方文档,在联合类型中,可以用instanceof关键字来进行类型判断

if (a instanceof string) {
	//do something
} else if (a instanceof number) {
	//do something
}

实际在DevEco studio中实测,instanceof关键字不可用,会报错提示instanceof左侧类型必须为any

在这里插入图片描述

实际上arkTs中也没有any类型,只有Object类型
在这里插入图片描述

实测,可以用typeof关键字来确认联合类型中的具体类型
在这里插入图片描述

Aliases

aliases类型主要是为匿名函数、联合类型或者已有类型进行新的命名,使用type关键字

type Matrix = number[][]
type Callback = (s: string, num: number) => string  //回调函数
type Callback1<T> = (x: T) => boolean;  //带泛型的回调函数
type nullableString = string | null    //联合类型,定义可空字符串

语句

if语句

switch语句

条件表达式 ?:

for语句

for of语句

for of 语句可以用来遍历数组、map、字符串等。

let arr = [10, 20, 30];

for(let a of arr) {
	console.log(a);
}

while语句

do while语句

break

continue

throw和try catch finally语句

函数

函数声明

一个函数包括关键字、函数名称、参数、返回值、方法体。

function add(a:number, b:number) : number {
	return a + b;
}

可选参数

可选参数有两种形式,name?: Type 或者 a : number = 2

function a (name?:string) {
	if(name == undefined) {
		console.log('没有传入可选参数');
	} else {
		console,log(name);
	}
}

function b (name : string = 'abc') {
	console.log(name);
}

b(); //输出abc
b('def');//输出def

Rest参数

函数的最后一个参数可以是rest参数,允许函数接收任意数量的实参。

// rest参数由... 参数名称:数组形式的参数类型组成
function c (... s : string[]) {

}

返回类型

ArkTs允许函数省略返回类型,由编译器从函数中推断返回类型

function a() : string {return 'abc';}
等价于
function b() {return 'abc';}

function c() :void {console.log('abc');}
等价于
function d() {console.log('abc');}

函数的作用域

函数内定义的变量仅能从函数内部方位,无法从函数外部访问。如果函数内部定义的变量与函数外部定义的函数同名,则函数内部定义的变量将会覆盖外部定义。

函数类型

函数类型通常用于定义回调

//定义了一个Callback类型,该类型是一个接收string入参,没有返回值的函数
type Callback = (message : string) => void;

//创建一个使用回调的函数
function doSomething(callback: Callback) {
	callback('abc');
}

//使用回调函数,打印出abc
doSomething((message) => {
	console.log(message);
}

箭头函数

箭头函数又称为lambda函数,例如

// 箭头函数可以通过将函数赋值给一个变量,可以省略function关键字
// 用`=>`连接函数体
let a = (a:number, b:number) : number => { return a+b;}

//同时也可以省略返回类型,由编译器进行类型推断,等价于
let b = (a:number, b:number) => {return a+b;}

//如果函数只有一行的话,花括号和return也可以省略,等价于
let c = (a:number,b:number) => a+b;

闭包

在一个函数内部调用/创建另一个函数。把内嵌的函数称为闭包,它可以访问外部函数的局部变量。

闭包通常用于回调函数。

闭包的使用场景

用来返回某个值
function a() {
	let s = 'hello';
	return () => s; //arkTs没有函数表达式,只能使用箭头函数
}
let c = a();
let d = c(); 
console.log(d); //打印d的结果为hello,实际调用的是b函数(d是c()的返回结果,c是a()的引用,a()实际返回的是b(),b()的返回值是a()内部的变量s)
函数赋值,在函数内部定义函数表达式,ArkTs不支持该使用方式
将闭包作为函数的参数,形成回调函数
let a = (num: number,) => {
  return console.log(num.toString());
}

function b(number1: number, number2: number, callback: (num: number) => void) {
  let sum = number1 + number2;
  callback(sum);
}

b(1, 2, a)

与函数类型结合使用,演进为

type Callback = (num: number) => void;

let a = (num: number,) => {
  return console.log(num.toString());
}

function c(number1: number, number2: number, callback: Callback) {
  let sum = number1 + number2;
  callback(sum);
}

c(1, 2, a)

进而演进,形成我们经常使用的回调函数

type Callback = (num: number) => void;

function d(number1: number, number2: number, callback: Callback) {
  let sum = number1 + number2;
  callback(sum);
}

d(1, 2, (num) => {
  console.log(num.toString())
})

函数重载

我们可以通过编写重载,指定函数的不同调用方式。具体方法为,为同一个函数写入多个同名但签名不同的函数头,函数实现紧随其后,函数定义和实现需要跟在一起

function foo(x: number): void;            /* 第一个函数定义 */
function foo(x: string): void;            /* 第二个函数定义 */
function foo(x: number | string): void {  /* 函数实现 */
	consolo.log(x.toString());
}

foo(123);     //  OK,使用第一个定义
foo('aa'); // OK,使用第二个定义

类初始化、字段、创建实例

以下是一个示例

class Person {
  private name: string
  private age: number

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

  say() {
    console.log(`my name is ${this.name}, i am ${this.age}`)
  }
}

arkTs中,类中的方法直接定义,不需要function关键字。

arkTs中,类的属性值默认情况下是需要在定义的时候进行赋值,或者在构造函数中进行赋值。以保证尽可能少的返回undefined的情况,如果确实需要有undefined情况,可以按照如下写法

class Person1 {
  private name?: string

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

let person1 = new Person1()

person1.getName()?.length

在上述示例中,字段上边使用?:关键字,表示该字段可以是undefined,这样可以不用进行初始化。
此时该字段的实际类型为联合类型stirng|undefined,可以看到get方法中的返回值。
在实例化该对象后,调用getName方法时,需要进行判空或者使用?.可选链语法

arkTs不支持多构造函数,会报错,仅允许相同参数数量,但多种类型的构造方法重载,与函数重载类似
在这里插入图片描述

创建对象需要用到new关键字

let person = new Person("name",15);
person.say();

也可以用字面量的方式来创建对象,仅限于简单类型,没有自定义方法的类

class Point {
  x: number = 0
  y: number = 0
}
let p: Point = {x: 42, y: 42};

get和set方法

class Person {
  private name: string
  private _age: number
  static a: string

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

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

  set age(num:number) {
    //可以在这里做一些校验类的操作
    this._age = num;
  }

  say() {
    console.log(`my name is ${this.name}, i am ${this.age}`)
  }
}

let person = new Person("name", 15);
person.say();

person.age;//输出15
person.age = 100;

上述例子中,我们对age属性设置了get和set方法,arkTs中,get和set方法,最终也是通过字段的形式去读取和设置的,所以在定义get和set方法的时候,虽然有前置的get和set关键字,但是方法名还是不能和字段名重复,否则会报错重复定义。
同时,加上前置的get和set关键字后,该方法就可以直接用字段的形式进行访问,方法名字和对应的字段名之间没有具体的关联,可以随意设置,但不能相同。

继承

class extends A implements b {
 
}

可见性修饰符

private protected public 默认为public

泛型默认值

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

空安全

默认情况下,arkTs中的所有类型都是不可为空的。 不经处理的情况下,直接赋值null会报错。

可选链

arkTs中使用?.来表示可选链,在访问对象属性时,如果该属性是null或者undefined时,可选链上后续代码将不在执行,直接返回undefined

非空断言

在调用链中,我们可以使用!.的方式来进行非空断言,此时编译器将不在对可空对象进行校验。在实际运行中,如果使用断言的位置出现了null,将抛出异常。

空值合并运算符

在arkTs中,使用??作为空值合并运算符,当??左侧的值为null或者undefined时,返回右侧结果,否则返回左侧结果,等价于使用三元运算符进行判断。

模块

一个程序中,可以被划分为多组编译单元或者模块。

每个模块都有自己的作用域,在模块中创建的任何声明,在模块外都不可见,除非他们被显示的导出。

使用另一个模块中的声明,也需要进行显示的导入操作。

导出

导出使用export关键字

导入

静态导入

导入声明分为两部分
导入路径:用于指定导入的模块
导入绑定:用于定义导入的模块中的可用实体集及使用形式

导入绑定

导入绑定可以有几种形式

假设模块具有路径./utils和导出实体XY

import * as Utils from './utils'

表示将路径中所有导出的实体都引入进来,并且设置了别名Utils,可以直接使用别名访问对应导出实体。

import {X, Y} from './utils'

引入实体X和Y,可在当前模块中直接使用X和Y

import {X as Z, Y} from './utils'

引入实体X和Y,同时对X设置了别名Z,此时在模块中可以直接使用Y和Z,但不能使用X,编译时会报错。

动态导入

import()语法通常称为动态导入,用来动态导入模块。以这种方式调用,将返回一个promise。

如下例所示,import(modulePath)可以加载模块并返回一个promise,该promise resolve为一个包含其所有导出的模块对象。该表达式可以在代码中的任意位置调用。

let modulePath = prompt("Which module to load?");
import(modulePath)
.then(obj => <module object>)
.catch(err => <loading error, e.g. if no such module>)

如果在异步函数中,可以使用let module = await import(modulePath)。

// say.ts
export function hi() {
  console.log('Hello');
}
export function bye() {
  console.log('Bye');
}

那么,可以像下面这样进行动态导入:

async function test() {
  let ns = await import('./say');
  let hi = ns.hi;
  let bye = ns.bye;
  hi();
  bye();
}

导入kit

从HarmonyOS NEXT Developer Preview 1版本开始引入Kit概念。SDK对同一个Kit下的接口模块进行了封装,开发者在示例代码中可通过导入Kit的方式来使用Kit所包含的接口能力。其中,Kit封装的接口模块可查看SDK目录下Kit子目录中各Kit的定义。

通过导入Kit方式使用开放能力有三种方式:

  • 方式一:导入Kit下单个模块的接口能力。例如:
    import { UIAbility } from '@kit.AbilityKit';
    
  • 方式二:导入Kit下多个模块的接口能力。例如:
    import { UIAbility, Ability, Context } from '@kit.AbilityKit';
    
  • 方式三:导入Kit包含的所有模块的接口能力。例如:
    import * as module from '@kit.AbilityKit';
    
    其中,“module”为别名,可自定义,然后通过该名称调用模块的接口。

特别注意:方式三可能会导入过多无需使用的模块,导致编译后的HAP包太大,占用过多资源,请谨慎使用。

;