ArkTS是HarmonyOS应用开发的官方高级语言。语法类似于TypeScript,ArkTS提供了声明式UI范式、状态管理、渲染控制等相应的能力,让开发者能够以更简洁、更自然的方式开发应用。

ArkTS在TypeScript(简称TS)生态基础上做了进一步扩展,保持了TS的基本风格,同时通过规范定义强化开发期静态检查和分析,提升代码健壮性,并实现更好的程序执行稳定性和性能。对比标准TS的差异可以参考从TypeScript到ArkTS的适配规则。ArkTS同时也支持与TS/JavaScript(简称JS)高效互操作。

ArkTS基础类库和容器类库增强了语言的基础功能,提供包括高精度浮点运算、二进制Buffer、XML生成解析转换和多种容器库等能力,协助开发者简化开发工作,提升开发效率。

针对TS/JS并发能力支持有限的问题,ArkTS对并发编程API和能力进行了增强,提供了TaskPool和Worker两种并发API供开发者选择。另外,ArkTS进一步提出了Sendable的概念来支持对象在并发实例间的引用传递,提升ArkTS对象在并发实例间的通信性能。

方舟编译运行时(ArkCompiler)支持ArkTS、TS、JS的编译运行,目前它主要分为ArkTS编译工具链和ArkTS运行时两部分。其中ArkTS编译工具链负责在开发侧将高级语言编译为方舟字节码文件(*.abc),而ArkTS运行时则负责在设备侧运行字节码文件执行程序逻辑。

基础语法

Hello World

console.log('我说', 'Hello World')

数据类型和变量,常量定义

变量常量的注意事项(命名规则):

① 只能包含数字、字母、下划线、$,不能以数字开头

② 不能使用内置关键字或保留字 (比如 let、const)

严格区分大小写

三种常见的基础数据类型:

string 字符串:描述信息

number 数字:计算

boolean 布尔:判断 (真、假)

变量:专门用来存储数据容器 (可变)

let 变量名: 类型 = 值
let title: string = '奥利奥'
let price: number = 3.14
let isSelect: boolean = true
title = '燕麦'

常量用来存储数据 (不可变)

const 常量名: 类型 = 值
const PI: number = 3.1415926

数组

数组:是一个容器,可以存储多个数据

let 数组名: 类型[] = [数据1, 数据2,...]
let names: string[] = ['小红', '小明', '大强']

注意:数组指定的类型和存储的数据类型要必须一致,否则会报错

获得数组元素: 数组名[索引]

let names: string[] = ['小红', '小明', '大强']
console.log('取出小明', names[1])

注意:索引号是从 0 开始的

对象数组

// 1. 定义接口
interface Person {
  stuId: number
  name: string
  gender: string
  age: number
}
// 2. 基于接口构建对象数组
let pArr: Person[] = [
  { stuId: 1, name: '小丽', gender: '女', age: 12 },
  { stuId: 2, name: '小王', gender: '男', age: 11 },
  { stuId: 3, name: '大强', gender: '男', age: 13 },
  { stuId: 4, name: '小张', gender: '男', age: 11 },
  { stuId: 5, name: '小美', gender: '女', age: 12 },
]

通过下标即可访问,通过 for of 可以遍历

数组的操作

查找 & 修改

查找: 数组名[下标]

修改: 数组名[下标] = 新值

数组长度:数组名.length

let names: string[] = ['小明', '小红', '大强', '小飞']
// 1. 查找
console.log('查找姓名', names[0])
// 2. 长度
console.log('数组长度为', names.length)
// 3. 修改
names[1] = 'Jack'
console.log('names数组', names)

增加数组元素

往开头加: 数组名.unshift(数据1, 数据2, 数据3, ......)

结尾添加:数组名.push(数据1, 数据2, 数据3, ......)

let songs: string[] = ['告白气球', '洋葱', '吻别']
// unshift():开头新增 (返回操作后数组的长度)
songs.unshift('你是我的眼')
// push():结尾新增 (返回操作后数组的长度)
songs.push('光辉岁月', '海阔天空')
console.log('数组songs', songs)

删除数组元素

从开头删: 数组名.shift()

从结尾删: 数组名.pop()

let songs: string[] = ['告白气球', '洋葱', '吻别']
// shift(): 开头删除 (返回值: 删除的项)
songs.shift()
// pop(): 结尾删除 (返回值: 删除的项)
songs.pop()
console.log('数组songs', songs)

任意位置添加 / 删除数组元素

语法:数组名.splice(起始位置, 删除的个数, 新增元素1, 新增元素2, ......)

let songs: string[] = ['告白气球', '洋葱', '吻别']
// 删除下标为2的元素 ['告白气球', '洋葱']
songs.splice(2, 1)
console.log('数组songs', songs)

函数-Function

函数:是可以被重复使用的代码块

1. 定义函数
function 函数名() {
    函数体
}
2. 调用函数
函数名()

函数的完整用法

function 函数名(需要处理的数据//形参) {
   编写代码对数据进行处理
   return 处理后的结果
}
let 变量名: 类型 = 函数名(实际要处理的数据//实参)

根据我们传入不同的数据,进行处理返回处理后的结果

function 函数名(形参1:类型, 形参2:类型 ...) {
   编写代码对数据进行处理
   return 处理后的结果
}
let 变量名: 类型 = 函数名(实参1, 实参2, ..)

箭头函数

箭头函数是 比普通函数 更简洁 的一种函数写法

let 函数名 = () => {
   // 函数体

}
函数名()

let 函数名 = (形参1: 类型, 形参2: 类型) => {
   // 函数体
}
函数名(实参1, 实参2)

let 函数名 = (形参1: 类型, 形参2: 类型) => {
 // 函数体
 // 1. 计算过程
 // 2. 返回结果
 return 计算的结果
}
函数名(实参1, 实参2)

示例:
let buy = (price: number, weight: number) => {
    let result: number = price * weight
    return result
}
let apple: number = buy(2, 3)

对象

作用:用于描述一个物体的特征行为

对象:是一个可以存储多个数据的容器

对象-定义和使用

let 对象名称: 对象结构类型 = 值

1. 通过 interface 接口约定 对象结构类型

interface 接口名 {
  属性1: 类型1
  属性2: 类型2
  属性3: 类型3
}
interface Person {
  name: string
  age: number
  weight: number
}

2. 定义对象并使用(通过 . 访问)

let person: Person = {
  name: '小明',
  age: 18,
  weight: 120
}
console.log('名字', person.name)
console.log('年纪', person.age)
console.log('体重', person.weight)

对象 – 方法

方法作用:描述对象的具体行为

1. 约定方法类型

interface 接口名称 {
  方法名: (参数:类型) => 返回值类型
}
interface Person{
  run: () => void
  sing: (song: string) => void
}

2. 添加方法(箭头函数

对象名.方法名(实参)

let xm: Person = {
  run: () => {
  console.log('小明说', '我在跑步')
 },
  sing: (song: string) => {
  console.log('小明说', '我来唱首', song)
 }
}
ym.dance()
ym.sing('七月上')

联合类型

联合类型是一种灵活的数据类型,它修饰的变量可以存储不同类型的数据。

let 变量: 类型1 | 类型2 | 类型3 = 值
let judge: number | string = 100
judge = 'A'
把变量值限定在一组数据范围内选择
let gender: 'man' | 'woman' | 'secret'

基于联合类型,变量可存不同类型数据

枚举类型

枚举类型是一种特殊的数据类型,约定变量只能在一组数据范围内选择值。

1. 定义枚举类型(常量列表

enum 枚举名 {
  常量1 = 值,
  常量2 = 值,
  ......
}
enum ThemeColor {
  Red = '#ff0f29',
  Orange = '#ff7100',
  Green = '#30b30e'
}

2. 使用枚举类型,约束变量

let color: ThemeColor = ThemeColor.Red
console.log('主页颜色', color)

取值从枚举中(常量列表中)取

字符串拼接

作用:把两个或多个字符串,拼成一个字符串。(通常拼接的是字符串和变量)

加号的作用:拼接

'hello' + 'world' => 'helloworld'
let name: string = '小明'
console.log('简介信息', '名字是' + name)

注意: 加法两端只要有字符串,就是拼接

模板字符串

例如`hello`

作用:拼接字符串和变量

优势:更适合于 多个变量 的字符串拼接

适合复杂场景
let name: string = '小明'
let age: number = 18
console.log('简介信息', `姓名是${name},今年${age}岁了`)
适合简易场景
let name: string = '小明'
let age: number = 18
console.log('简介信息', '姓名是' + name + ',今年' + age + '岁了')

类型转换

1. 字符串转数字

Number():字符串 直接转数字,转换失败返回NaN(字符串中包含非数字)

let str1:string ='1.1'
let str2: string ='1.9'
let str3:string ='1.1a
console.log('数字是',Number(str1))// 1.1
console.log('数字是',Number(str2))// 1.9
console.log('数字是',Number(str3))// NaN

parseInt():去掉小数部分 转数字,转换失败返回NaN

let strl: string= '1.1'
let str2: string='1.9'
let str3:string ='1.1a'
let str4:string ='a'

console.log('数字是',parseInt(str1))// 1
console.log('数字是',parseInt(str2))// 1
console.log('数字是',parseInt(str3))// 1
console.log('数字是',parseInt(str4))// NaN

parseFloat():保留小数部分 转数字,转换失败返回NaN

let strl:string='1.1'
let str2:string ='1.9'
let str3:string ='1.1a
let str4:string ='a'

console.log('数字是',parseFloat(str1))// 1.1
console.log('数字是',parseFloat(str2))// 1.9
console.log('数字是',parseFloat(str3))//1.1
console.log('数字是',parseInt(str4)) // NaN

2.数字转字符串

toString():数字直接转字符串

let numl:number=1.1
let num2:number=1.9

console.log('字符串是',num1.tostring())//'1.1'
console.log('字符串是',num2.toString())//'1.9'

toFixed()四舍五入转字符串,可设置保留几位小数

let numl:number= 1.1
let num2:number = 1.9
let num3:number=1.9152

console.log('字符串是',num1.toFixed()) //'1'
console.log('字符串是',num2.toFixed()) //'2'
console.log('字符串是',num3.toFixed(2)) //'1.92'

运算符

算术运算符

算术运算符:也叫数学运算符,主要包括加、减、乘、除、取余(求模)等

let num1:number = 9
let num2:number = 4

console.log('加法计算',num1 + num2)
console.log('减法计算',num1 - num2)
console.log('乘法计算',num1 * num2)
console.log('除法计算',num1 / num2)// 2.25
console.log('取余计算',num1 % num2)// 1

赋值运算符

赋值运算符:对变量进行赋值的运算符,如:=

+=加法赋值,-=减法赋值,*=乘法赋值,/=除法赋值,%=取余赋值

let num1:number=1
num1 += 1

console.log('加等后的结果',num1)

一元运算符

常见一元运算符:++和--

后置写法:先赋值后自增/自减

前置写法:先自增/自减再赋值

let num: number = 10
let res: number = num++//后自增
let num2: number = 10
let res2: number = ++num//先自增

比较运算符

作用:用来判断比较两个数据大小,返回一个布尔值(true / false)

let num1: number = 9
let num2: number = 5

console.log('比较运算的结果是', num1 > num2)
console.log('比较运算的结果是', num1 >= num2)
console.log('比较运算的结果是', num1 == num2)
console.log('比较运算的结果是', num1 != num2)

逻辑运算符

作用:扩充判断条件

&&与,都真才真,||或,一真则真,!非,取反

运算符优先级

优先级

1小括号( )

2一元++、--、!

3算数先*、/、% 后+、-

4比较>、>=、<、<=

5比较==、!=

6逻辑运算符先&& 后||

7赋值=

语句

语句概念

语句: 一段可以执行的代码,是一个行为 ( num = a + b )

表达式: 可以 被求值 的代码,并将其计算出 一个结果 (1 + 1、3 * 5、3 > 2)

语句执行结构

分支语句

if 分支语句

if 分支语句:根据 逻辑条件 不同,执行不同语句。

小括号条件结果为 true,则执行大括号里面的代码

小括号结果不是布尔类型时,会类型转换为布尔值

if (逻辑条件) {
  条件成立执行的代码
}

根据 逻辑条件 不同,执行不同语句。

if (逻辑条件) {
  条件成立执行的代码
}
else {
  条件不成立执行的代码
}

if 多分支

if (条件1) {
  条件1成立执行的代码
}
else if (条件2) {
  条件2成立执行的代码
}
else if (条件3) {
  条件3成立执行的代码
}
else {
  都不成立执行的代码
}

switch 分支

switch 分支一般用于精确匹配,不同的执行不同的代码

switch (表达式) {
  case 值1:
    与值1匹配执行的语句
    break
  case 值2:
    与值2匹配执行的语句
    break
  default:
以上都未成功匹配执行的代码
}

注意:如果没有break语句,则会直接执行 switch 中的下一个代码块(无论是否匹配成功)

三元条件表达式

语法:条件 ?条件成立执行的表达式 :条件不成立执行的表达式

let num1: number = 5
let num2: number = 10
// 返回较大值
let res: number = num1 > num2 ? num1 : num2
console.log('結果是', res)

循环语句

Tips:循环三要素

1. 初始值(变量)

2. 循环条件

3. 变化量(变量计数,自增或自减)

while 语句

作用:重复执行指定的一段代码

while (条件) {
  条件成立重复执行的代码
}
/*
while (true) {
  console.log('while', '重复执行的代码')
}
*/
// 指定循环次数
let i: number = 1
while (i < 5) {
  console.log('while~i', '重复执行的代码')
  i++
}

for 语句

作用:重复执行指定的一段代码

while (条件) {
  条件成立重复执行的代码
}
/*
while (true) {
  console.log('while', '重复执行的代码')
}
*/
// 指定循环次数
let i: number = 1
while (i < 5) {
  console.log('while~i', '重复执行的代码')
  i++
}

退出循环

作用:满足指定条件,可以退出循环

break:终止整个循环

continue: 退出当前一次循环的执行,继续执行下一次循环

遍历数组

遍历:将数组里面的每个数据,按顺序访问一遍

let names: string[] = ['小红', '小明', '大强']
for(let i = 0; i < names.length; i++) {
  console.log('名字是', names[i])
}

遍历数组 – for ... of

语法: for (let item of 数组名) {}

for ... of : 在 ... 之中 进行循环

item: 声明的一个变量, 用来在循环的时候接收 每一个数组元素

let names: string[] = ['小红', '小明', '大强']
for(let i = 0; i < names.length; i++) {
  console.log('名字是', names[i])
}
for (let item of names) {
  console.log('for...of...名字是', item)
}

class 类

类是用于 创建对象 模板。同时类声明也会引入一个 新类型,可定义其 实例属性方法 构造函数

// 类名 首字母大写(规范)
class 类名{
 // 1. 实例属性(字段)
 // 2. 构造函数
 // 3. 方法
}
// 使用类 实例化对象 基于类 创建对象
const p:类名 = new 类名()

1. Class类 实例属性

通过实例属性(字段),可以保存各种类型的数据

// 类
class 类名{
 // 字段名、类型、初始值
 字段名1:类型='xxx'
 // 可选字段可以不设置初始值
 字段名2?:类型
}
// 可选字段在使用时需要配合 可选链操作符 避免出错
const p: 类名 = new 类名()
p.字段名1
p?.字段名2

class Person {
  name: string = 'jack'
  food?: string
}
const p = new Person()
p.name = 'tom'

console.log(p.name)
console.log('字符串', p.food?.length)

2. Class类 构造函数

不同实例,将来需要有不同的字段初始值,就需要通过构造函数实现

class 类{
  字段A:类型
  字段B:类型
 constructor(参数...) {
  // 通过 new 实例化的时候 会调用 constructor
  // 通过关键字 this 可以获取到实例对象
 this.字段名A = 参数
 }
}
const 实例1 = new 类(参数...)
const 实例2 = new 类(参数...)

class Food {
  name: string
  price: number
  constructor(name:string, price:number) {
   this.name = name
   this.price = price
 }
}
const f1 = new Food('西红柿鸡蛋', 15)
const f2 = new Food('土豆炖鸡块', 24)

3. Class类 定义方法

类中可以定义方法并且在内部编写逻辑

class 类名{
  方法名(参数...):返回值类型{
   // 逻辑...
   // 可以通过 this 获取实例对象
 }
}

class Person{
  name:string
  constructor(name:string) {
   this.name = name
}
  sayHi(name:string){
  console.log(`你好${name}, 我是:${this.name}`)
 }
}
const p:Person = new Person('jack')
p.sayHi('rose')

4. 静态属性 和 静态方法

类还可以添加 静态属性、方法,后续访问需要通过 来完成

// 定义
class 类{
  static 字段:类型
  static 方法(){}
}
// 使用
类.字段
类.方法()

// 静态属性和方法
class Robot {
  // 如果不设置值, 默认是 undefined
  static version: string = '10.12'
  // 工具方法
  static getRandomNumber () {
   return Math.random()
 }
}
Robot.version
Robot.getRandomNumber()

5. 继承 extends 和 super 关键字

类可以通过 继承 快速获取另外一个类的 字段 方法

子类通过 super 可以访问父类的实例字段、实例方法和构造函数。

class 父类 {
  // 字段
  // 方法
  // 构造函数
}
class 子类 extends 父类{
  // 自己的字段(属性)
  // 自己的方法
  // 可以重写父类方法
}

class 父类 {
  func(){
 }
}
class 子类 extends 父类 {
  constructor() {
  super() // 调用父类构造函数
 }
方法(){
  super.方法() // 调用父类方法
 }
}

6. instanceof 检测是否实例

instanceof 运算符可以用来检测某个对象是否是某个类的实例

实例对象 instanceof Class

typeof 表达式

7. 修饰符(readonly、private ...)

类的 方法 属性 可以通过修饰符来 限制 访问

修饰符包括:readonly、private、protected 和 public。省略不写默认为 public

readonly

class 类{
  readonly 属性:类型
}
只可以取值,无法修改

private

private 修饰的成员不能在声明该成员的类之外访问, 包括子类

class 类{
  private 属性:类型
  private 方法(){}
}
class Person {
  private name: string = ''
  private age: number = 0
  sayHi() {
   // 内部可以访问
   console.log(`你好,我叫:${this.name}`)
  }
}
class Student extends Person{
  sayHello() {
   // 无法访问 报错
   console.log(`你好,我叫:${super.name}`)
  }
}
const p = new Person()
// p.name // 无法访问 报错
p.sayHi()

protected

protected修饰符的作用与private修饰符非常相似

不同点是protected修饰的成员允许在 派生类(子类) 中访问

class 类{
  protected 属性:类型
  protected 方法(){}
}
class Person {
  protected name: string = ''
  protected age: number = 0
  sayHi() {
  // 内部可以访问
    console.log(`你好,我叫:${this.name}`)
 }
}
class Student extends Person{
  sayHello() {
  // 可以访问到父类的protected属性
    console.log(`你好,我叫:${super.name}`)
 }
}
const p = new Person()
// p.name // 无法访问 报错
p.sayHi()

public

public 修饰的类成员(字段、方法、构造函数)

在程序的任何可访问该类的地方都是可见的 (默认)

class 类{
  public 属性:类型
  public 方法(){}
}
class Person {
  public name: string = ''
  public age: number = 0
  sayHi() {
   // 内部可以访问
   console.log(`你好,我叫:${this.name}`)
 }
}
class Student extends Person{
  sayHello() {
  // 可以访问到父类的protected属性
  console.log(`你好,我叫:${super.name}`)
 }
}
const p = new Person()
// p.name // 可以访问
p.sayHi()

readonly只读,无限制

private私有类,内部可以访问

protected保护,类及子类可以访问

public公共,无限制

剩余参数 和 展开运算符

剩余参数的语法,我们可以将 函数 方法 中一个不定数量的参数表示为一个数组

// 剩余参数只能写在最后一位
function 函数名(参数1,参数2,...剩余参数数组){
 // 逻辑代码
 // 之前的参数:挨个获取即可
 // 剩余参数:以数组的形式获取
}

functionsum(numA:number,numB:number,...theArgs:number[]) {
  let total = numA+numbB;
  for (const arg of theArgs) {
  total += arg;
 }
 return total;
}
console.log(sum(1, 2, 3).toString()) // 6
console.log(sum(1, 2, 3, 4).toString()) // 10

出于程序稳定性,以及运行性能考虑,在 ArkTS 中 ...(展开运算符) 只能用在数组上

const numArr1: number[] = [1, 2, 3, 4]
const numArr2: number[] = [5, 6, 7]
// 合并到一起
const totalArr: number[] = [...numArr1,...numArr2]

接口补充

接口继承使用的关键字是 extends

interface 接口1{
  属性1:类型
}
interface 接口2 extends 接口1 {
  属性2:类型
}

interface IAnimal {
  name: string
}
interface ICat extends IAnimal {
  color: string
}
const cat: ICat = {
  name: '加菲猫',
  color: '黑色'
}

可以通过接口结合 implements 来限制 必须要有 某些属性 和 方法

interface 接口{
  属性:类型
  方法:方法类型
}
class 类 implements 接口{
  // 必须实现 接口中定义的 属性、方法,
  // 否则会报错
}

interface IDog {
  name: string
  bark: () => void
}
class Dog implements IDog {
  name: string = ''
  food: string = ''
  bark() {
  }
}

泛型

泛型函数

泛型可以让【函数】等, 与多种【不同的类型】一起工作,灵活可复用

通俗一点就是:类型是 可变 的!

function 函数名<Type>(temp:Type):Type{
  return temp
}
fn<string>('123')
fn<number>(1)

泛型约束

之前的类型参数,可以传递任何类型,没有限制。

如果希望有限制 → 泛型约束

interface 接口{
  属性:类型
}
function 函数<Type extends 接口>(){}
// 传入的类型必须要有 接口中的属性

interface ILength {
  length: number
}
function fn<T extends ILength>(param: T) {
  console.log('', param.length)
}

多个泛型参数

日常开发的时候,如果有需要,可以添加多个 类型变量

function funcA<T, T2>(param1: T, param2: T2) {
  console.log('参数 1', param1)
  console.log('参数 2', param2)
}
funcA<string, number>('大白菜', 998)
funcA<string[], boolean[]>(['小老虎'], [false])

泛型接口

定义接口的时候,结合泛型定义,就是泛型接口

interface 接口<Type>{
  // 内部使用Type
}

interface IdFunc<Type> {
  id: (value: Type) => Type
  ids: () => Type[]
}
let obj: IdFunc<number> = {
  id(value) { return value },
  ids() { return [1, 3, 5] }
}

泛型类

定义类的时候,结合泛型定义,就是泛型类。

class 类名<Type>{
  // 内部可以使用 Type
}
// 定义
class Person <T> {
  id: T
  constructor(id: T) {
  this.id = id
 }
 getId(): T {
  return this.id
 }
}
// 使用
let p = new Person<number>(10)