赞
踩
作为鸿蒙开发的编程语言,我们先来一图看看这个语言,我们可以看到ArkTS是在TS(TypeScript)的基础上改造的,而TS又是在JS(JavaSript)上改造的,一句话总结就是ArkTS是TS的超集,TS是JS的超集。但ArkTS虽然基于TS,但在某些方面可能施加更严格的类型约束,以适应跨端开发的需求
废话不多说,我们紧接着来说语法,在语法上,其与其他编程语言在语法上并没有很本质的不同,毕竟都是高级的开发编程语言,大家都是有各自的特点,但目的都是为了开发软件,我们就仅在语言层面进行总结
基础的变量声明方式
let arr = new Array<string>();
let obj:Object = new Stack<number>();
联合类型
联合类型即给声明能够接受多种类型的变量,通过竖线(“|”)将不同的类型联合起来,自定义类型也可以联合
class Cat { // ... } class Dog { // ... } class Frog { // ... } type Animal = Cat | Dog | Frog | number // Cat、Dog、Frog是一些类型(类或接口) let animal: Animal = new Cat(); animal = new Frog(); animal = 42; // 可以将类型为联合类型的变量赋值为任何组成类型的有效值
void类型
一般void类型都是用于作为函数返回值
null和undefined
总结来说,null是一个明确的“空”值,而undefined表示一个变量或值尚未被定义或赋值。
enum类型
枚举类型即包含一组常量的集合,如
enum Color {
Red,
Green,
Blue
}
定义了该类型后可以在声明类型时使用该类型进行声明,如
let favoriteColor: Color = Color.Green;
console.log(favoriteColor); // 输出:1
元组类型(tuple)
本质也是一个数组,不过是知道元素类型的数组,如:
let tuple: [string, number, boolean] = ["hello", 42, true];
访问元素时也是通过数组下标进行访问,不过需要注意对应的类型匹配,元组的元素数量和类型必须与定义时一致,否则会导致编译错误。
type int = number;
let i: int = 1;
type Matrix = number[][];
type Handler = (s: string, no: number) => string;
type Predicate <T> = (x: T) => Boolean;
type NullableObject = Object | null;
class C {
value: number | null = 1;
}
let c = new C();
let y: number;
y = c.value + 1; // 编译时错误:无法对可空值作做加法
y = c.value! + 1; // ok,值为2
// 若c.value为空时则值为1
// 如果一个Person的实例有不为空的spouse属性, // 且spouse有不为空的nick属性,则输出spouse.nick。否则,输出undefined class Person { nick: string | null = null spouse?: Person constructor(nick: string) { this.nick = nick; this.spouse = undefined; } } let p: Person = new Person('Alice'); console.log(p.spouse?.nick); // undefined) p.spouse = p; console.log(p.spouse?.nick);
此处仅列举与C++有些区别的语句
一般的循环:for(let i = 0; i < 10; i+= 2){…}
范围for: for (forVar of expression) {statements}
forVar: expression的一个元素;expression:一个“容器” 如:
for (let ch of 'a string object') {/* process ch */}
一般正常使用
如果break语句后带有标识符,则将控制流转移到该标识符所包含的语句块之外。如:
let x = 1
label: while (true) {
switch (x) {
case 1: // statements
break label // 中断while语句
}
}
函数声明中,函数的返回值类型可以省略
tips:返回值的类型也可以是一个联合类型
function hello(name?: string) {
if (name == undefined) {
console.log('Hello!');
} else {
console.log(`Hello, ${name}!`);
}
}
function multiply(n: number, coeff: number = 2): number {
return n * coeff;
}
multiply(2); // 返回22
multiply(2, 3); // 返回23
function sum(...numbers: number[]): number { let res = 0; for (let n of numbers) res += n; return res; } sum() // 返回0 sum(1, 2, 3) // 返回6 // 使用Object类型时的数组,可传入多种不同类型的参数,都会保存到给的数组里 function exampleFunction(...args: Object[]) { args.forEach(arg => { console.log(arg.toString()); }); } exampleFunction(1, 'string', true);
语法:(参数列表):返回值=>{函数体}
我们可以将函数类型保存:type trigFunc = (x: number) => number // 这是一个函数类型
lambda的返回值可省略,返回值省略时根据函数体推导返回值,函数体仅一行时可以去掉大括号,所以指向类型为number时说明返回值的类型是number,代码含义即为一个函数类型为参数是一个number类型,返回值为number类型的函数
即在函数体内部又包含一个函数,那么这个函数从声明到内部这包含的另一个函数之间的作用域的变量会被保存下来(类似变成了局部全局)
function f(): () => number {
let count = 0;
let g = (): number => { count++; return count; };
return g;
}
let z = f();
z(); // 返回:1
z(); // 返回:2
函数重载(重载方式与其它语言不完全相同,实现只能有一个,定义可以重载多个,即签名。注意这里的参数也可以使用联合类型)
function foo(x: number): void; /* 第一个函数定义 */
function foo(x: string): void; /* 第二个函数定义 */
function foo(x: number | string): void { /* 函数实现 */
}
foo(123); // 使用第一个定义
foo('aa'); // 使用第二个定义
类的基本操作都与C++相似,通过上面的变量、函数的内容即可封装一个类,下面主要介绍一些不同的地方
ArkTS中,要求所有成员必须在声明时就进行定义初始化(或在构造函数中进行初始化),即所有字段在声明时或者构造函数中显式初始化
但也可以通过可选链的方式表示为undefined,但此时使用值时则需要对应注意
在类内实现getter和setter时,首先可以使用deveco一键生成
我们要关注一点的是此处生成的函数形式是get/set 属性(): 返回类型{return 返回值} 而不是一般的声明函数的方式,如:
class Person {
name: string = ''
private _age: number = 0
get age(): number { return this._age; }
set age(x: number) {
if (x < 0) {
throw Error('Invalid age argument');
}
this._age = x;
}
}
let p = new Person();
p.age; // 输出0
p.age = -42; // 设置无效age值会抛出错误
构造函数在实现上与其他语言也大体相似,未提供构造时默认会自动创建具有空参数列表的默认构造函数
但需要注意的是,构造函数也可以重载多个形式,但与函数重载相同,重载时实现在类中只能有一个,重载定义可以有多个,如:
class C { constructor(x: number) /* 第一个签名 */ constructor(x: string) /* 第二个签名 */ constructor(x: number | string) { /* 实现签名 */ } } let c1 = new C(123); // OK,使用第一个签名 let c2 = new C('abc'); // OK,使用第二个签名 // 以下方式则是错误的 class A{ constructor(x: number) { } constructor(x: string) { } }
类都是使用new进行实例的创建
可以直接使用构造函数构建实例对象
let p = new Person('John', 'Smith');
console.log(p.fullName());
class Point {
x: number = 0
y: number = 0
}
let p: Point = {x: 42, y: 42};
静态字段:使用static声明变量即可(而不是用let或const)
继承
继承时,父类中受访问限定符成员也会相同继承到子类
多态在继承后直接重写同名函数即可
类的创建
使用{…}(对象字面量)创建,内部的参数为类内的成员(按照顺序进行初始化)
但是要确保对象字面量的结构符合类的结构,但并没有创建类的实例。
对象字面量只能在可以推导出该字面量类型的上下文中使用。其他正确的例子:
class C {
n: number = 0
s: string = ''
}
let c: C = {n: 42, s: 'foo'};
// 若类C为下述方式,则无法保证对应的对象字面量是C类的实例
class C {
a: number = 0;
b: string = '';
constructor(a: number, b : string) {
}
}
Record类型(就是哈希表的一种,但该语言中也有Map类型)
let hash = new Map<Number, string>();//哈希表类型
泛型Record<K, V>用于将类型(键类型)的属性映射到另一个类型(值类型)
tips:使用时需要先进行初始化
// 创建一个哈希表
let hashTable: NumberStringHashTable = {};
// 访问和修改哈希表中的值
console.log(hashTable[1]);
// 输出: onehashTable[1] = 'ONE';
console.log(hashTable[1]); // 输出: ONE
// 添加新的键值对
hashTable[4] = 'four';
// 遍历哈希表
Object.keys(hashTable).forEach(key => {\
console.log(`${key} = ${hashTable[key]}`);
用于定义模板类、模板接口以及模板函数等,直接用尖括号表示即可
模板类和接口
class CustomStack<Element> {
public push(e: Element):void {
// ...
}
}
// 要使用类型CustomStack,必须为每个类型参数指定类型实参
let s = new CustomStack<string>();
s.push('hello');
// 在使用泛型类型和函数时会确保类型安全
s.push(55); // 将会产生编译时错误
泛型类型的类型参数可以绑定.例如,HashMap<Key, Value>容器中的Key类型参数必须具有哈希方法,即它应该是可哈希的。
即通过继承接口的方式,使得对应的泛型参数(类型)在传递过来时必须实现对应的接口,
如Key
类型参数被约束为 Hashable
接口的实现。这意味着只有那些实现了 hash
方法的类型才能用作这个哈希映射的键。
interface Hashable {
hash(): number
}
class HasMap<Key extends Hashable, Value> {
public set(k: Key, v: Value) {
let h = k.hash();
// ...其他代码...
}
}
泛型函数
语法:function last<T>(x: T): T
在函数名后加上尖括号,之后即可在参数列表、返回类型以及函数体中使用对应的模板参数
返回数组最后一个元素的函数
function last(x: number[]): number {
return x[x.length - 1];
}
last([1, 2, 3]); // 3
// 模板函数:
function last<T>(x: T[]): T {
return x[x.length - 1];
}
泛型的默认值
在泛型的类型参数可以设置默认值,此时使用时则可以不指定泛型的形参而直接使用泛型类型的名称,如下
class SomeType {}
interface Interface <T1 = SomeType> { }
class Base <T2 = SomeType> { }
class Derived1 extends Base implements Interface { }
// Derived1在语义上等价于Derived2
class Derived2 extends Base<SomeType> implements Interface<SomeType> { }
function foo<T = number>(): T {
// ...
}
foo();
// 此函数在语义上等价于下面的调用
foo<number>();
常见的为ets文件中的模块的导入与导出
模块即可以理解是一个作用域,小到函数体、类,大到文件等,每个模块都有自己的作用域,该模块之外都不可见,除非被显式导出
与此相对,从另一个模块导出的变量、函数、类、接口等必须首先导入到模块中
导出(类、变量、函数都可导出)
注意:通过export方式导出,在导入时要加{}。
export class Point {
x: number = 0
y: number = 0
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
}
export let Origin = new Point(0, 0);
export function Distance(p1: Point, p2: Point): number {
return Math.sqrt((p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y));
}
导入
静态导入
即直接使用导入声明对想要的模块直接先进行导入,导入声明用于导入从其他模块导出的实体,并在当前模块中提供其绑定。导入声明由两部分组成:
直接导入,并且绑定名称
import * as Utils from './utils'
Utils.X // 表示来自Utils的X
Utils.Y // 表示来自Utils的Y
绑定对应的导入实体名称
import { X, Y } from './utils'
X // 表示来自utils的X
Y // 表示来自utils的Y
给绑定的导入实体起别名
import { X as Z, Y } from './utils'
Z // 表示来自Utils的X
Y // 表示来自Utils的Y
X // 编译时错误:'X'不可见
动态导入(后续再进行详解)
动态导入即是根据条件导入模块(或按需导入),使用import()语法实现
import()语法通常称为动态导入dynamic import,是一种类似函数的表达式,用来动态导入模块。以这种方式调用,将返回一个promise。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。