TS 小记(6)TypeScript 函数

141 阅读5分钟

在编程中,函数用于封装一段逻辑,可以调用这个函数来执行这些逻辑,使用函数可以做到代码的复用。这篇文章来学习一下 TypeScript 函数的相关语法知识。

函数的定义

TypeScript 中用 function 关键字来定义函数,函数包含以下这些元素:

  • 函数名:函数名用来标识一个函数。可选,有些函数没有函数名,后面会介绍到。

  • 参数列表:参数列表是传递到函数中的数据,分为参数名和参数类型。

  • 函数体:函数体是是指这个函数中具体执行的代码逻辑。

  • 返回值:返回值是函数给调用方返回的数据。

完整的语法如下:

function 函数名(参数1名字: 参数1类型, 参数2名字: 参数2类型, ..., 参数N名字: 参数N类型): 返回值类型  {
	函数体;
	return 返回值;
}

看一个加法的例子:

function add(x: number, y: number): number {
	let result = x + y;
	return result;
}
// 或者简化一下
function add(x: number, y: number): number {
	return x + y;
}

函数调用

使用函数名 + () 来调用某个函数,括号中可以传入参数列表。比如调用上面的 add 函数:

let result = add(1, 2);
console.log(result); // 输出:3

函数返回

在函数中使用 return 关键字来表示函数代码执行结束,返回到函数调用处。

函数的参数

可选参数

在 TypeScript 中,如果在函数中定义了参数,那么掉用这个函数时,就必须传入这个参数,否则编译器在编译时会报错。

function buildName(firstName: string, lastName: string) {
  return firstName + " " + lastName;
}
 
let result1 = buildName("Bob");                  // 错误,缺少参数
let result2 = buildName("Bob", "Adams", "Sr.");  // 错误,应有2个参数,实际有3个
let result3 = buildName("Bob", "Adams");         // 正确

如果我们希望部分参数是可选的,可以在参数名后面加上 ? 符号将这个参数指定为可选参数。

function buildName(firstName: string, lastName?: string) {
	if (lastName) {
  	return firstName + " " + lastName;
  } else {
  	return firstName;
  }
}

let result1 = buildName("Bob");  // 正确 
let result2 = buildName("Bob", "Adams", "Sr.");  // 错误,应有1-2个参数,实际有3个
let result3 = buildName("Bob", "Adams");  // 正确

注意:可选参数必须放在必需参数的后面。

参数默认值

可以在参数名后面加 = 默认值 给参数设置默认值,在调用函数时,如果不传入这个参数的值,就会使用默认值。

注意:一个参数不能同时指定为可选和有默认值

function calculateDiscount(price: number, discount: number = 0.5): void {
  var finalPrice = price * discount;
  console.log("final price = " + finalPrice);
}

calculateDiscount(1000);          
calculateDiscount(1000, 0.7);

// 运行结果
500
700

上面的函数编译成 JavaScript 的结果是:

function calculateDiscount(price, discount) {
  if (discount === void 0) { discount = 0.5; }
  var finalPrice = price * discount;
  console.log("final price = " + finalPrice);
}

剩余参数

有时不知道会给函数传入多少个参数,这时可以使用剩余参数,剩余参数的类型是个数组,并且要在参数名前加 ... ,它可以将不确定数量的参数作为一个数组传入函数中。

function buildName(firstName: string, ...restOfName: string[]) {
    return firstName + " " + restOfName.join(" ");
}
  
let employeeName = buildName("Joseph", "Samuel", "Lucas", "MacKinzie");
console.log(employeeName);
// 运行结果: Joseph Samuel Lucas MacKinzie

匿名函数

匿名函数是一个没有名字的函数,其它组成元素和普通函数一样。可以将匿名函数赋值给一个变量,通过这个变量再去调用函数。

var myFunc = function([参数列表]) { ... };

例如:

let msg = function() {
  return "Hello World";
}
console.log(msg());
// 输出:Hello World

匿名函数也可以不赋值给变量,而是直接调用。

let res = (function(x: number, y: number): number {
    return x + y;
  })(1, 2);
console.log(res);

// 输出:3

构造函数

TypeScript 可以使用 JavaScript 内置的构造函数 Function() 来定义函数。

语法如下:

var res = new Function([参数列表], 函数体);
// 函数体是一段代码的字符串

比如:

let myFunction = new Function("a", "b", "let c = a + 1; return c * b;");
console.log(myFunction(3, 4));
// 输出 16

Lambda 函数

Lambda 函数 也叫 箭头函数,形如 () => { something }() => something

箭头函数和普通函数的一个好处是可以自动将函数中的 this 附加到上下文中。

var shape = {
    name: "rectangle",
    popup: function() {

        console.log('This inside popup(): ' + this.name);

        setTimeout(function() {
            console.log('This inside setTimeout(): ' + this.name);
            console.log("I'm a " + this.name + "!");
        }, 3000);

    }
};

shape.popup();

输出结果为:

This inside popup(): rectangle
This inside setTimeout(): undefined
I'm a undefined!

可以看到在 setTimeout 中,获取不到对象的 this 了,所以在内部通过 this.name 得到的值是 undefined。

将普通的匿名函数变为箭头函数:

var shape = {
    name: "rectangle",
    popup: function() {

        console.log('This inside popup(): ' + this.name);

        setTimeout( () => {
            console.log('This inside setTimeout(): ' + this.name);
            console.log("I'm a " + this.name + "!");
        }, 3000);

    }
};

shape.popup();

输出结果为:

This inside popup(): rectangle
This inside setTimeout(): rectangle
I'm a rectangle!

可以看到使用箭头函数,在 setTimeout 中也能获取到 this 对象。

使用箭头函数的代码编译成 JavaScript 的代码:

var shape = {
    name: "rectangle",
    popup: function () {
        var _this = this;
        console.log('This inside popup(): ' + this.name);
        setTimeout(function () {
            console.log('This inside setTimeout(): ' + _this.name);
            console.log("I'm a " + _this.name + "!");
        }, 3000);
    }
};
shape.popup();

可以看到第 4 行 var _this = this; 在函数内部捕获了 this 对象。

函数重载

重载是指函数名相同,但参数不同的一组函数。函数是否重载和返回值类型是否相同无关。

参数不同可以是:

  1. 参数类型不同:
function foo(string): void;
function foo(number): void;
  1. 参数个数不同:
function foo(string): void;
function foo(string, number): void;

定义函数重载需要定义 重载签名 和一个 实现签名。重载签名定义函数的形参和返回类型,没有函数体,一个函数可以有多个重载签名。实现签名则是包含实际的函数体代码。

// 定义重载签名
function greet(person: string): string;
function greet(persons: string[]): string[];
// 定义实现签名
function greet(person: unknown): unknown {
    if (typeof person === 'string') {
        return `Hello, ${person}!`;
    } else if (Array.isArray(person)) {
        let names = person.join(", ")
        return `Hello, ${names}!`;
    }
    throw new Error('Unable to greet');
}

let persons = ["Bob", "Sam", "Owen"];
console.log(greet(persons[0]));
console.log(greet(persons));

// 运行结果
Hello, Bob!
Hello, Bob, Sam, Owen!

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 6 天,点击查看活动详情