OpenHarmony 开发语言简介
OpenHarmony 为应用开发提供了一套 UI 开发框架,即方舟开发框架(ArkUI 开发框架),ArkUI 开发框架针对不同技术背景的开发者提供了两种开发范式,分别是基于 JS 扩展的类 Web 开发范式和基于 TS 扩展的声明式开发范式,它们之间的简单对比如下所示
名称 | 语言 | UI 更新方式 | 使用场景 | 使用对象 |
---|---|---|---|---|
类 Web 开发范式 | JS 语言 | 数据驱动更新 | 界面较为简单的程序应用和卡片 | web 前端开发人员 |
声明式开发范式 | 扩展的 TS 语言 | 数据驱动更新 | 复杂度较大,团队合作度较高的程序 | 移动系统应用开发人员,系统应用开发人员 |
UI 框架的发展趋势,从 Android 和 iOS 的发展历程看,Android 从 View 框架到 Jetpack Compose,iOS 的 UIKits 到 SwiftUI,都是由命令式 UI 往声明式 UI 发展,如果仓颉推出的 UI 框架底层对接的是 ACE ,那么也应该是声明式 UI 框架,并且和 ArkUI 框架的声明式 UI 的语法很类似,因此本书笔者只介绍基于 TS 扩展的声明式开发方式。
TS 介绍
什么是 TS
TypeScript 简称 TS ,它是 JavaScript 的一个超集并支持 ECMAScript 6 标准,如果读者已经熟悉 TS 语法可以直接跳过本节了,如果有过其它编程语言经验的话会很容易上手,语言都是相通的,充其量就是熟悉一下不同语言间的语法,本节只是简单介绍一下 TS 语法
基础类型
- Any
TypeScript 提供了 any 关键字表示任意数据类型,声明为该类型的变量可以赋予任意类型的值。
var data: any; // 声明data为any类型
data = true;
console.log(typeof data); // boolean
data = "OpenHarmony";
console.log(typeof data); // string
data = 100;
console.log(typeof data); // number
data = 10.5;
console.log(typeof data); // number
- number
TypeScript 提供了 number 关键字来表示数字类型,它是双精度 64 位浮点值,既可以表示整数,又可以表示小数。
var data: number;
data = 100;
console.log(typeof data); // number
data = -10;
console.log(typeof data); // number
data = 3.14;
console.log(typeof data); // number
data = 0b10001;
console.log(typeof data); // number
- string
TypeScript 提供了 string 关键字来表示字符串类型,使用单引号(')或双引号(")来表示字符串类型,也可以使用反引号(`)来定义多行文本和内嵌表达式。
var data: string;
data = "Hello, OpenHarmony";
data = "Hello, OpenHarmony";
data = `Hello, ${data}`;
console.log(data);
- boolean
TypeScript 提供了 boolean 关键字来表示逻辑值 true 和 false。
var data: boolean = false;
data = true;
data = false;
- 数组类型
TypeScript 没有提供专门的关键字来表示数组类型,声明一个数组可以使用元素类型后边加 [] 或者数组泛型的方式。
var scores: number[] = [90, 88]; // 声明一个number数组
var names: string[] = ["张三", "李四"]; // 声明一个string数组
var address: Array<string> = ["Beijing", "Tianjin"]; // 声明一个string数组
console.log(names[0]); // 访问数组
console.log(scores[0].toString()); // 访问数组
console.log(address[0]); // 访问数组
for (var i = 0; i < address.length; i++) {
// 遍历数组
console.log(address[i]);
}
for (var index in address) {
// 遍历数组
console.log(address[index]);
}
- 元组类型
TypeScript 提供了元组来表示已知元素数量和类型的数组,元组内的各元素的类型不必相同,但是对应位置的类型必须一致。
var user: [string, number, string]; // 定义一个元组
user = ["张三", 18, "Beijing"]; // 初始化元组,对应位置类型必须一致
console.log(`姓名:${user[0]}`); // 姓名:张三
console.log(`年龄:${user[1]}`); // 年龄:18
console.log(`住址:${user[2]}`); // 住址:Beijing
- enum
TypeScript 提供了 enum 关键字表示枚举类型,枚举类型主要用于定义数值的集合。
enum Color { // 定义一个枚举
Red,
Green,
Blue,
}
var c: Color = Color.Blue; // 定义枚举类型
console.log(c.toString()); // 2
- void
TypeScript 提供了 void 关键字表示函数的返回类型为空,也就是函数没有返回值。
function print(msg: string): void {
// 函数没有返回值
console.log(msg);
}
- undefined
TypeScript 提供了 undefined 关键字表示声明了一个变量但并没有赋值的情况。
var data; // 声明了data,但是没有赋值
console.log(typeof data); // undefined
- null
TypeScript 提供了 null 关键字表示一个对象没有初始化。
class Person {}
var person: Person; // 声明一个person,但是没有初始化
if (null == person) {
console.log("person is null"); // person is null
}
- 联合类型(非常重要)
TypeScript 允许通过 | 将一个变量设置成多种类型,赋值的时候可以根据设置的类型来赋值。
var data: string | number; // 设置data为联合类型
data = "OpenHarmony"; // 正确
data = 99; // 正确
data = true; // 编译报错,类型不匹配
联合类型很重要,ArkUI框架里大量使用了联合类型。
变量声明
- var
变量在使用前必须先声明,TS 使用 var
声明一个变量,我们可以使用一下四种方式来声明变量:
- 声明变量的类型并赋值初始值,格式:var [变量名] : [类型] = 值;
var osName: string = "OpenHarmony";
- 声明变量的类型但不赋值初始值,格式:var [变量名] : [类型];
var osName: string;
- 声明变量并赋值初始值,但不设置类型,格式:var [变量名] = 值;
var osName = "OpenHarmony";
- 声明变量并没有设置类型和初始值,该类型可以是任意类型,默认值为 undefined,格式:var [变量名];
var osName;
简单样例如下:
var osName: string = "OpenHarmony";
var price1: number = 5;
var price2: number = 5.5;
var sum = price1 + price2;
console.log("操作系统名字: " + osName); // 操作系统名字: OpenHarmony
console.log("第一个价格是: " + price1); // 第一个价格是: 5
console.log("第二个价格是: " + price2); // 第二个价格是: 5.5
console.log("总价格: " + sum); // 总价格: 10.5
TypeScript 遵循强类型,如果将不同的类型赋值给变量会编译错误,样例如下:
var count: number = "hello"; // 这个代码会编译错误
函数
- 1.函数声明
函数就是包裹在花括号中的代码块,前边使用 function 关键字,语法格式如下:
function function_name() {
// 执行代码
}
例如声明函数如下:
function log(msg: string) {
// 声明一个函数
console.log(msg); // 代码块
}
- 2.函数调用
函数只有通过调用才可以执行函数内的代码,语法格式如下:
function_name();
样例如下:
function log(msg: string) {
// 声明一个函数
console.log(msg); // 代码块
}
log("Hello, OpenHarmony"); // 调用函数
- 3.函数返回值
如果希望得到函数的执行结果,可以使用 return 语句,语法如下:
function function_name(): return_type {
return value; // return语句
}
样例如下:
function sayHi(): string {
// 定义sayHi函数,该函数的返回类型为string
return "Hello!";
}
function execute() {
// 定义execute函数
var msg = sayHi(); // 调用sayHi()函数
console.log(msg); // 打印sayHi()函数的返回值
}
execute(); // 调用execute()函数
- 4.带参数函数
在调用函数时可以向函数传递值,这些值被称为参数,语法如下:
function func_name(param1: paramType, param2: paramType) {}
样例如下:
function add(x: number, y: number): number {
// 定义add函数,该函数返回类型为nubmer, 接收两个number类型的参数x
return x + y;
}
console.log(add(1, 2));
- 5.可选参数
如果函数定义了参数则必须传递这些参数否则报错,如果不想传递这些参数,可以添加 ? ,语法如下:
function func_name(param1: paramType, param2?: paramType) {}
样例如下:
function add(x: number, y?: number) {
if (y) {
return x + y;
} else {
return x;
}
}
console.log(add(10).toString()); // 10
console.log(add(10, 10).toString()); // 20
如果参数不全是可选参数,那么可选参数的位置必须放在最后。
- 默认参数
函数定义了参数则必须传递这些参数否则报错,如果不想传递这些参数除了使用可选参数外,也可以使用默认参数,当不传入该参数时则使用默认值,语法如下:
function func_name(param1: paramType, param2: paramType = default_value) {}
样例如下:
function add(x: number = 20, y: number = 50) {
// 设置x和y的默认值
return x + y;
}
console.log(add(10).toString()); // 60
console.log(add(10, 10).toString()); // 20
函数的参数不能同时是默认参数和可选参数。
- 剩余参数
在不确定要向函数传递多个参数的情况下,可以使用剩余参数,剩余参数前边以 ... 为前缀数据类型为数组的形式提供,语法如下:
function func_name(
param1: paramType,
param2: paramType,
...params: paramType[]
) {}
样例如下所示:
function add(param1: number, param2: number, ...params: number[]) {
// 剩余参数
var result = param1 + param2;
for (var i = 0; i < params.length; i++) {
// 遍历剩余参数
result += params[i];
}
return result;
}
console.log(add(1, 2, 3, 4, 5).toString()); // 15
类
- 定义类
TypeScript 是面向对象的 JavaScript,定义一个类使用关键字 class
,类可以包含字段、构造方法和方法。语法如下:
class class_name {
// 类作用域
}
样例如下:
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
info(): string {
return "name: " + this.name + ", age: " + this.age;
}
}
- 创建类对象
类定义完后,可以通过 new
关键字实例化一个类的对象,实例化类对象即调用类的构造方法,语法如下:
var object_name = new class_name([args]);
样例如下:
var person = new Person("harmony", 10); // 创建一个Person对象
- 访问类属性和方法
访问类的属性和方法以.
号的形式,语法如下:
obj.field_name; // 访问属性
obj.function_name(); // 访问方法
样例如下:
var person = new Person("harmony", 10); // 创建一个person
console.log(person.name); // harmony
console.log(person.age.toString()); // 10
console.log(person.info()); // name: harmony, age: 10
- 类的继承
TypeScript 支持继承类,创建类的时候可以使用关键字 extends
继承一个已有的类,这个已有的类称为父类,继承它的类称为子类。子类除了不能继承父类的私有成员(方法和属性)和构造函数,其他的都可以继承。语法如下:
class child_class_name extends parent_class_name {}
样例如下:
class Zhangsan extends Person {
sayHello() {
console.log("Hello, " + this.name);
}
}
var person = new Zhangsan("harmony", 10); // 创建person
console.log(person.name); // harmony
console.log(person.age.toString()); // 10
console.log(person.info()); // name: harmony, age: 10
类的继承只支持单继承,不支持多继承。也就是说子类只能继承一个父类。
- 方法重写
子类可以重写父类的方法,在重写父类方法的时候也可以使用 super 关键字调用父类的方法。样例如下
class Zhangsan extends Person {
info(): string {
// 重写父类方法
console.log(super.info()); // 调用父类方法
return "Hello, " + this.name; // 重新实现info方法
}
}
var person = new Zhangsan("harmony", 10);
console.log(person.name); // harmony
console.log(person.age.toString()); // 10
console.log(person.info()); // name: harmony, age: 10
// Hello, harmony
- 访问修饰符
TypeScript 中,可以使用访问控制符来保护对类、变量、方法和构造方法的访问。TypeScript 支持 3 种不同的访问权限。
public(默认):公有,可以在任何地方被访问。
protected:受保护,可以被其自身以及其子类访问。
private:私有,只能被其定义所在的类访问。
样例如下所示:
class Person {
name: string;
private age: number; // age为private,外界无法访问
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
info(): string {
return "name: " + this.name + ", age: " + this.age;
}
}
var person = new Person("harmony", 10);
console.log(person.name); // harmony
console.log(person.age.toString()); // 编译报错,age为private
接口
- 定义接口
接口是一系列抽象方法的声明,接口定义后需要具体的类实现,语法如下:
interface interface_name {
// 抽象方法
}
样例如下:
interface IPerson {
// 定义一个接口
name: string; // 定义接口的一个属性
say: () => string; // 定义接口的一个方法
}
var person: IPerson = {
// 创建一个接口的实例
name: "OpenHarmony", // 设置属性值
say: () => {
// 实现接口方法
return "Hello, " + person.name;
},
};
console.log(person.name); // OpenHarmony
console.log(person.say()); // Hello, OpenHarmony
- 接口继承
接口可以使用 extends
关键字继承其它接口来扩展自己,接口既支持单继承又支持多继承,多继承时接口间使用逗号, 分隔。语法如下:
// 接口单继承
interface Child_interface_name extends super_interface_name {}
// 接口多继承
interface Child_interface_name
extends super_interface_name1,
super_interface_name2 {}
样例如下:
interface IPerson {
// 定义接口IPerson
name: string;
say: () => string;
}
interface ITeacher extends IPerson {
// 定义接口ITeacher并继承IPerson
age: number;
teach: () => void;
}
var teacher = <ITeacher>{};
teacher.name = "张三";
teacher.age = 18;
teacher.say = () => {
return "你好,我是" + teacher.name;
};
teacher.teach = () => {
console.log("同学们好,我们开始上课啦");
};
console.log("name: " + teacher.name);
console.log("age : " + teacher.age);
console.log("say :" + teacher.say());
teacher.teach();
- 类实现接口
类可以使用 implements
关键字实现一个接口,一个类实现接口后必须声明和实现接口的所有属性和方法
interface IPerson {
// 定义一个接口
name: string; // 定义接口的属性
say: () => string; // 定义接口的方法
}
class Person implements IPerson {
// 类型实现接口
name: string; // 必须声明接口属性
constructor(name: string) {
// 在构造方法对属性初始化
this.name = name;
}
say(): string {
// 实现接口的方法
return `Hello, I'm ${this.name}`;
}
}
class Teacher implements IPerson {
// 类型实现接口
constructor(public name: string) {
// 声明接口属性简化方式
}
say(): string {
// 实现接口的方法
return `Hello, I'm ${this.name}`;
}
}
var person: IPerson = new Person("王大爷"); // 创建IPerson实现类
console.log(person.say()); // Hello, I'm 王大爷
console.log(person.name); // 王大爷
person = new Teacher("王老师"); // 创建IPerson实现类
console.log(person.say()); // Hello, I'm 王老师
console.log(person.name); // 王老师