声明
变量声明
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
和导出实体X
和Y
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包含的所有模块的接口能力。例如:
其中,“module”为别名,可自定义,然后通过该名称调用模块的接口。import * as module from '@kit.AbilityKit';
特别注意:方式三可能会导入过多无需使用的模块,导致编译后的HAP包太大,占用过多资源,请谨慎使用。