07-ArkTS语法入门(1)
07-ArkTS语法入门(1)
ArkTS介绍
ArkTS是HarmonyOS优选的主力应用开发语言。ArkTS围绕应用开发在TypeScript(简称TS)生态基础上做了进一步扩展,保持了TS的基
本风格,同时通过规范定义强化开发期静态检查和分析,提升程序执行稳定性和性能。
从API version 10开始,ArkTS进一步通过规范强化静态检查和分析,对比标准TS的差异可以参考从TypeScript到ArkTS的适配规则:
- 强制使用静态类型:静态类型是ArkTS最重要的特性之一。如果使用静态类型,那么程序中变量的类型就是确定的。同时,由于所有类型在程序实际运行前都是已知的,编译器可以验证代码的正确性,从而减少运行时的类型检查,有助于性能提升。
- 禁止在运行时改变对象布局:为实现最大性能,ArkTS要求在程序执行期间不能更改对象布局。
- 限制运算符语义:为获得更好的性能并鼓励开发者编写更清晰的代码,ArkTS限制了一些运算符的语义。比如,一元加法运算符只能作用于数字,不能用于其他类型的变量。
- 不支持Structural typing:对Structural typing的支持需要在语言、编译器和运行时进行大量的考虑和仔细的实现,当前ArkTS不支持该特性。根据实际场景的需求和反馈,我们后续会重新考虑。
当前,在UI开发框架中,ArkTS主要扩展了如下能力:
- 基本语法:ArkTS定义了声明式UI描述、自定义组件和动态扩展UI元素的能力,再配合ArkUI开发框架中的系统组件及其相关的事件方法、属性方法等共同构成了UI开发的主体。
- 状态管理:ArkTS提供了多维度的状态管理机制。在UI开发框架中,与UI相关联的数据可以在组件内使用,也可以在不同组件层级间传递,比如父子组件之间、爷孙组件之间,还可以在应用全局范围内传递或跨设备传递。另外,从数据的传递形式来看,可分为只读的单向传递和可变更的双向传递。开发者可以灵活地利用这些能力来实现数据和UI的联动。
- 渲染控制:ArkTS提供了渲染控制的能力。条件渲染可根据应用的不同状态,渲染对应状态下的UI内容。循环渲染可从数据源中迭代获取数据,并在每次迭代过程中创建相应的组件。数据懒加载从数据源中按需迭代数据,并在每次迭代过程中创建相应的组件。
ArkTS兼容TS/JavaScript(简称JS)生态,开发者可以使用TS/JS进行开发或复用已有代码。HarmonyOS系统对TS/JS支持的详细情况参见兼容TS/JS的约束。
未来,ArkTS会结合应用开发/运行的需求持续演进,逐步提供并行和并发能力增强、系统类型增强、分布式开发范式等更多特性。
如需更详细了解ArkTS语言,可见ArkTS具体指南。
优点
ArkTS是一种为构建高性能应用而设计的编程语言。ArkTS在继承TypeScript语法的基础上进行了优化,以提供更高的性能和开发效率。
随着移动设备在人们的日常生活中变得越来越普遍,许多编程语言在设计之初没有考虑到移动设备,导致应用的运行缓慢、低效、功耗
大,针对移动环境的编程语言优化需求也越来越大。ArkTS是专为解决这些问题而设计的,聚焦于提高运行效率。
目前流行的编程语言TypeScript是在JavaScript基础上通过添加类型定义扩展而来的,而ArkTS则是TypeScript的进一步扩展。TypeScript
深受开发者的喜爱,因为它提供了一种更结构化的JavaScript编码方法。ArkTS旨在保持TypeScript的大部分语法,为现有的TypeScript开
发者实现无缝过渡,让移动开发者快速上手ArkTS。
ArkTS的一大特性是它专注于低运行时开销。ArkTS对TypeScript的动态类型特性施加了更严格的限制,以减少运行时开销,提高执行效
率。通过取消动态类型特性,ArkTS代码能更有效地被运行前编译和优化,从而实现更快的应用启动和更低的功耗。
声明
ArkTS通过声明引入变量、常量、函数和类型。
变量声明
以关键字let开头的声明引入变量,该变量在程序执行期间可以具有不同的值。
let hi: string = 'hello';hi = 'hello, world';
常量声明
以关键字const开头的声明引入只读常量,该常量只能被赋值一次。
const hello: string = 'hello';
对常量重新赋值会造成编译时错误。
自动类型推断
由于ArkTS是一种静态类型语言,所有数据的类型都必须在编译时确定。
但是,如果一个变量或常量的声明包含了初始值,那么开发者就不需要显式指定其类型。ArkTS规范中列举了所有允许自动推断类型的场景。
以下示例中,两条声明语句都是有效的,两个变量都是string类型:
let hi1: string = 'hello';let hi2 = 'hello, world';
类型
Number类型
ArkTS提供number和Number类型,任何整数和浮点数都可以被赋给此类型的变量。
数字字面量包括整数字面量和十进制浮点数字面量。
整数字面量包括以下类别:
- 由数字序列组成的十进制整数。例如:0、117、-345
- 以0x(或0X)开头的十六进制整数,可以包含数字(0-9)和字母a-f或A-F。例如:0x1123、0x00111、-0xF1A7
- 以0o(或0O)开头的八进制整数,只能包含数字(0-7)。例如:0o777
- 以0b(或0B)开头的二进制整数,只能包含数字0和1。例如:0b11、0b0011、-0b11
浮点字面量包括以下:
- 十进制整数,可为有符号数(即,前缀为“+”或“-”);
- 小数点(“.”)
- 小数部分(由十进制数字字符串表示)
- 以“e”或“E”开头的指数部分,后跟有符号(即,前缀为“+”或“-”)或无符号整数。
示例:
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类型
boolean类型由true和false两个逻辑值组成。
通常在条件语句中使用boolean类型的变量:
let isDone: boolean = false;
// ...
if (isDone) { console.log ('Done!');}
String类型
string代表字符序列;可以使用转义字符来表示字符。
字符串字面量由单引号(')或双引号(")之间括起来的零个或多个字符组成。字符串字面量还有一特殊形式,是用反向单引号(`)括起来的模板字面量。
let s1 = 'Hello, world!\n';let s2 = 'this is a string';let a = 'Success';let s3 = `The result is ${a}`;
Void类型
void类型用于指定函数没有返回值。
此类型只有一个值,同样是void。由于void是引用类型,因此它可以用于泛型类型参数。
class Class<T> { //...}let instance: Class <void>
Object类型
Object类型是所有引用类型的基类型。任何值,包括基本类型的值(它们会被自动装箱),都可以直接被赋给Object类型的变量。object类型则用于表示除非基本类型外的类型。
Array类型
array,即数组,是由可赋值给数组声明中指定的元素类型的数据组成的对象。
数组可由数组复合字面量(即用方括号括起来的零个或多个表达式的列表,其中每个表达式为数组中的一个元素)来赋值。数组的长度由数组中元素的个数来确定。数组中第一个元素的索引为0。
以下示例将创建包含三个元素的数组:
let names: string[] = ['Alice', 'Bob', 'Carol'];
Enum类型
enum类型,又称枚举类型,是预先定义的一组命名值的值类型,其中命名值又称为枚举常量。
使用枚举常量时必须以枚举类型名称为前缀。
enum ColorSet { Red, Green, Blue }let c: ColorSet = ColorSet.Red;
常量表达式可以用于显式设置枚举常量的值。
enum ColorSet { White = 0xFF, Grey = 0x7F, Black = 0x00 }let c: ColorSet = ColorSet.Black;
Union类型
union类型,即联合类型,是由多个类型组合成的引用类型。联合类型包含了变量可能的所有类型。
class Cat { name: string = 'cat'; // ...}class Dog { name: string = 'dog'; // ...}class Frog { name: string = 'frog'; // ...}type Animal = Cat | Dog | Frog | number;// Cat、Dog、Frog是一些类型(类或接口)
let animal: Animal = new Cat();animal = new Frog();animal = 42;// 可以将类型为联合类型的变量赋值为任何组成类型的有效值
可以用不同的机制获取联合类型中特定类型的值。
示例:
class Cat { sleep () {}; meow () {} }class Dog { sleep () {}; bark () {} }class Frog { sleep () {}; leap () {} }
type Animal = Cat | Dog | Frog;
function foo(animal: Animal) { if (animal instanceof Frog) { animal.leap(); // animal在这里是Frog类型 } animal.sleep(); // Animal具有sleep方法}
Aliases类型
Aliases类型为匿名类型(数组、函数、对象字面量或联合类型)提供名称,或为已有类型提供替代名称。
type Matrix = number[][];type Handler = (s: string, no: number) => string;type Predicate <T> = (x: T) => boolean;type NullableObject = Object | null;
运算符
赋值运算符
赋值运算符=,使用方式如x=y。
复合赋值运算符将赋值与运算符组合在一起,其中x op = y等于x = x op y。
复合赋值运算符列举如下:+=、-=、*=、/=、%=、<<=、>>=、>>>=、&=、|=、^=。
比较运算符
运算符 | 说明 |
---|---|
=== | 如果两个操作数严格相等(对于不同类型的操作数认为是不相等的),则返回true。 |
!== | 如果两个操作数严格不相等(对于不同类型的操作数认为是不相等的),则返回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语句用于需要根据逻辑条件执行不同语句的场景。当逻辑条件为真时,执行对应的一组语句,否则执行另一组语句(如果有的话)。
else部分也可能包含if语句。
if语句如下所示:
if (condition1) { // 语句1} else if (condition2) { // 语句2} else { // else语句}
条件表达式可以是任何类型。但是对于boolean以外的类型,会进行隐式类型转换:
let s1 = 'Hello';if (s1) { console.log(s1); // 打印“Hello”}
let s2 = 'World';if (s2.length != 0) { console.log(s2); // 打印“World”}
Switch语句
使用switch语句来执行与switch表达式值匹配的代码块。
switch语句如下所示:
switch (expression) { case label1: // 如果label1匹配,则执行 // ... // 语句1 // ... break; // 可省略 case label2: case label3: // 如果label2或label3匹配,则执行 // ... // 语句23 // ... break; // 可省略 default: // 默认语句}
如果switch表达式的值等于某个label的值,则执行相应的语句。
如果没有任何一个label值与表达式值相匹配,并且switch具有default子句,那么程序会执行default子句对应的代码块。
break语句(可选的)允许跳出switch语句并继续执行switch语句之后的语句。
如果没有break语句,则执行switch中的下一个label对应的代码块。
条件表达式
条件表达式由第一个表达式的布尔值来决定返回其它两个表达式中的哪一个。
示例如下:
condition ? expression1 : expression2
如果condition的为真值(转换后为true的值),则使用expression1作为该表达式的结果;否则,使用expression2。
示例:
let isValid = Math.random() > 0.5 ? true : false;let message = isValid ? 'Valid' : 'Failed';
For语句
for语句会被重复执行,直到循环退出语句值为false。
for语句如下所示:
for ([init]; [condition]; [update]) { statements}
for语句的执行流程如下:
1、 执行init表达式(如有)。此表达式通常初始化一个或多个循环计数器。
2、 计算condition。如果它为真值(转换后为true的值),则执行循环主体的语句。如果它为假值(转换后为false的值),则for循环终止。
3、 执行循环主体的语句。
4、 如果有update表达式,则执行该表达式。
5、 回到步骤2。
示例:
let sum = 0;for (let i = 0; i < 10; i += 2) { sum += i;}
For-of语句
使用for-of语句可遍历数组或字符串。示例如下:
for (forVar of expression) { statements}
示例:
for (let ch of 'a string object') { /* process ch */}
While语句
只要condition为真值(转换后为true的值),while语句就会执行statements语句。示例如下:
while (condition) { statements}
示例:
let n = 0;let x = 0;while (n < 3) { n++; x += n;}
Do-while语句
如果condition的值为真值(转换后为true的值),那么statements语句会重复执行。示例如下:
do { statements} while (condition)
示例:
let i = 0;do { i += 1;} while (i < 10)
Break语句
使用break语句可以终止循环语句或switch。
示例:
let x = 0;while (true) { x++; if (x > 5) { break; }}
如果break语句后带有标识符,则将控制流转移到该标识符所包含的语句块之外。
示例:
let x = 1;label: while (true) { switch (x) { case 1: // statements break label; // 中断while语句 }}
Continue语句
continue语句会停止当前循环迭代的执行,并将控制传递给下一个迭代。
示例:
let sum = 0;for (let x = 0; x < 100; x++) { if (x % 2 == 0) { continue; } sum += x;}
Throw和Try语句
throw语句用于抛出异常或错误:
throw new Error('this error')
try语句用于捕获和处理异常或错误:
try { // 可能发生异常的语句块} catch (e) { // 异常处理}
下面的示例中throw和try语句用于处理除数为0的错误:
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.log('result: ' + res); } catch (x) { console.log('some error'); }}
支持finally语句:
function processData(s: string) { let error: Error | null = null;
try { console.log('Data processed: ' + s); // ... // 可能发生异常的语句 // ... } catch (e) { error = e as Error; // ... // 异常处理 // ... } finally { if (error != null) { console.log(`Error caught: input='${s}', message='${error.message}'`); } }}