ArkTS语法---函数、方法和接口
函数声明引入一个函数,包含其名称、参数列表、返回类型和函数体。在函数声明中,必须为每个参数标记类型。如果参数为可选参数,那么允许在调用函数时省略该参数。函数的最后一个参数可以是rest参数。
一、函数
1、函数声明
函数声明引入一个函数,包含其名称、参数列表、返回类型和函数体。
在函数声明中,必须为每个参数标记类型。如果参数为可选参数,那么允许在调用函数时省略该参数。函数的最后一个参数可以是rest参数。
function add(x: string, y: string): string {
let z: string = `${x} ${y}`;
return z;
}
2、可选参数
可选参数的格式可为name?: Type。
可选参数的另一种形式为设置的参数默认值。如果在函数调用中这个参数被省略了,则会使用此参数的默认值作为实参。
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); // 返回2*2
multiply(2, 3); // 返回2*3
3、rest参数
函数的最后一个参数可以是rest参数。使用rest参数时,允许函数或方法接受任意数量的实参。
function sum(...numbers: number[]): number {
let res = 0;
for (let n of numbers)
res += n;
return res;
}
sum() // 返回0
sum(1, 2, 3) // 返回6
4、返回类型
如果可以从函数体内推断出函数返回类型,则可在函数声明中省略标注返回类型。
不需要返回值的函数的返回类型可以显式指定为void或省略标注。这类函数不需要返回语句。
// 显式指定返回类型
function foo(): string { return 'foo'; }
// 推断返回类型为string
function goo() { return 'goo'; }
function hi1() { console.log('hi'); }
function hi2(): void { console.log('hi'); }
4、函数的作用域
函数中定义的变量和其他实例仅可以在函数内部访问,不能从外部访问。
如果函数中定义的变量与外部作用域中已有实例同名,则函数内的局部变量定义将覆盖外部定义。
5、函数类型
函数类型通常用于定义回调:
type trigFunc = (x: number) => number // 这是一个函数类型
function do_action(f: trigFunc) {
f(3.141592653589); // 调用函数
}
do_action(Math.sin); // 将函数作为参数传入
6、箭头函数或Lambda函数
let sum = (x: number, y: number): number => {
return x + y;
}
7、闭包
闭包是由函数及声明该函数的环境组合而成的。该环境包含了这个闭包创建时作用域内的任何局部变量。
在下例中,z是执行f时创建的g箭头函数实例的引用。g的实例维持了对它的环境的引用(变量count存在其中)。因此,当z被调用时,变量count仍可用。
function f(): () => number {
let count = 0;
let g = (): number => { count++; return count; };
return g;
}
let z = f();
z(); // 返回:1
z(); // 返回:2
8、函数重载
我们可以通过编写重载,指定函数的不同调用方式。具体方法为,为同一个函数写入多个同名但签名不同的函数头,函数实现紧随其后。
function foo(x: number): void; /* 第一个函数定义 */
function foo(x: string): void; /* 第二个函数定义 */
function foo(x: number | string): void { /* 函数实现 */
}
foo(123); // OK,使用第一个定义
foo('aa'); // OK,使用第二个定义
二、类
类声明引入一个新类型,并定义其字段、方法和构造函数。
class Person {
name: string = ''
surname: string = ''
constructor (n: string, sn: string) {
this.name = n;
this.surname = sn;
}
fullName(): string {
return this.name + ' ' + this.surname;
}
}
定义类后,可以使用关键字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};
三、字段
字段是直接在类中声明的某种类型的变量。
类可以具有实例字段或者静态字段。
1、实例字段
实例字段存在于类的每个实例上。每个实例都有自己的实例字段集合。
要访问实例字段,需要使用类的实例。
class Person {
name: string = ''
age: number = 0
constructor(n: string, a: number) {
this.name = n;
this.age = a;
}
getName(): string {
return this.name;
}
}
let p1 = new Person('Alice', 25);
p1.name;
let p2 = new Person('Bob', 28);
p2.getName();
2、静态字段
使用关键字static将字段声明为静态。静态字段属于类本身,类的所有实例共享一个静态字段。要访问静态字段,需要使用类名:
class Person {
static numberOfPersons = 0
constructor() {
// ...
Person.numberOfPersons++;
// ...
}
}
Person.numberOfPersons;
3、字段初始化
为了减少运行时的错误和获得更好的执行性能,
ArkTS要求所有字段在声明时或者构造函数中显式初始化。
class Person {
name: string = ''
setName(n:string): void {
this.name = n;
}
// 类型为'string',不可能为"null"或者"undefined"
getName(): string {
return this.name;
}
}
let jack = new Person();
// 假设没有对name赋值,例如调用"jack.setName('Jack')"
jack.getName().length; // 0, 没有运行时异常
4、getter和setter
setter和getter可用于提供对对象属性的受控访问。
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值会抛出错误
四、方法
方法属于类。类可以定义实例方法或者静态方法。静态方法属于类本身,只能访问静态字段。而实例方法既可以访问静态字段,也可以访问实例字段,包括类的私有字段。
1、实例方法
class RectangleSize {
private height: number = 0
private width: number = 0
constructor(height: number, width: number) {
// ...
}
calculateArea(): number {
return this.height * this.width;
}
}
必须通过类的实例调用实例方法:
let square = new RectangleSize(10, 10);
square.calculateArea(); // 输出:100
2、静态方法
使用关键字static将方法声明为静态。静态方法属于类本身,只能访问静态字段。静态方法定义了类作为一个整体的公共行为。
必须通过类名调用静态方法:
class Cl {
static staticMethod(): string {
return 'this is a static method.';
}
}
console.log(Cl.staticMethod());
3、继承
继承类继承基类的字段和方法,但不继承构造函数。继承类可以新增定义字段和方法,也可以覆盖其基类定义的方法。基类也称为“父类”或“超类”。继承类也称为“派生类”或“子类”。
一个类可以继承另一个类(称为基类),并使用以下语法实现多个接口:
class [extends BaseClassName] [implements listOfInterfaces] {
// ...
}
4、父类访问
关键字super可用于访问父类的实例字段、实例方法和构造函数。在实现子类功能时,可以通过该关键字从父类中获取所需接口:
class RectangleSize {
protected height: number = 0
protected width: number = 0
constructor (h: number, w: number) {
this.height = h;
this.width = w;
}
draw() {
/* 绘制边界 */
}
}
class FilledRectangle extends RectangleSize {
color = ''
constructor (h: number, w: number, c: string) {
super(h, w); // 父类构造函数的调用
this.color = c;
}
draw() {
super.draw(); // 父类方法的调用
// super.height -可在此处使用
/* 填充矩形 */
}
}
5、方法重写
子类可以重写其父类中定义的方法的实现。重写的方法必须具有与原始方法相同的参数类型和相同或派生的返回类型。
class RectangleSize {
// ...
area(): number {
// 实现
return 0;
}
}
class Square extends RectangleSize {
private side: number = 0
area(): number {
return this.side * this.side;
}
}
6、方法重载签名
通过重载签名,指定方法的不同调用。具体方法为,为同一个方法写入多个同名但签名不同的方法头,方法实现紧随其后。
class C {
foo(x: number): void; /* 第一个签名 */
foo(x: string): void; /* 第二个签名 */
foo(x: number | string): void { /* 实现签名 */
}
}
let c = new C();
c.foo(123); // OK,使用第一个签名
c.foo('aa'); // OK,使用第二个签名
五、构造函数
类声明可以包含用于初始化对象状态的构造函数。
构造函数定义如下:
constructor ([parameters]) {
// ...
}
如果未定义构造函数,则会自动创建具有空参数列表的默认构造函数,例如:
class Point {
x: number = 0
y: number = 0
}
let p = new Point();
1、派生类的构造函数
构造函数函数体的第一条语句可以使用关键字super来显式调用直接父类的构造函数。
class RectangleSize {
constructor(width: number, height: number) {
// ...
}
}
class Square extends RectangleSize {
constructor(side: number) {
super(side, side);
}
}
2、构造函数重载签名
我们可以通过编写重载签名,指定构造函数的不同调用方式。具体方法为,为同一个构造函数写入多个同名但签名不同的构造函数头,构造函数实现紧随其后。
class C {
constructor(x: number) /* 第一个签名 */
constructor(x: string) /* 第二个签名 */
constructor(x: number | string) { /* 实现签名 */
}
}
let c1 = new C(123); // OK,使用第一个签名
let c2 = new C('abc'); // OK,使用第二个签名
六、接口
接口声明引入新类型。接口是定义协定的常见方式。
任何一个类的实例只要实现了特定接口,就可以通过该接口实现多态。
接口通常包含属性和方法的声明。
// 接口:
interface AreaSize {
calculateAreaSize(): number // 方法的声明
someMethod(): void; // 方法的声明
}
// 实现:
class RectangleSize implements AreaSize {
private width: number = 0
private height: number = 0
someMethod(): void {
console.log('someMethod called');
}
calculateAreaSize(): number {
this.someMethod(); // 调用另一个方法并返回结果
return this.width * this.height;
}
}
1、接口属性
接口属性可以是字段、getter、setter或getter和setter组合的形式。
属性字段只是getter/setter对的便捷写法。以下表达方式是等价的:
interface Style {
color: string
}
interface Style {
get color(): string
set color(x: string)
}
2、接口继承
继承接口包含被继承接口的所有属性和方法,还可以添加自己的属性和方法。
接口可以继承其他接口,如下面的示例所示:
interface Style {
color: string
}
interface ExtendedStyle extends Style {
width: number
}
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)