【前端,TypeScript】TypeScript速成(六):函数

函数

函数的定义

定义一个最简单的加法函数:

typescript 复制代码
function add(a: number, b: number): number {
    return a + b
}

(可以看到 JavaScript/TypeScript 的语法与 Golang 也非常的相似)

调用该函数:

typescript 复制代码
console.log(add(2, 3))
// out
[LOG]: 5

可选参数、默认参数和可变参数列表

在函数定义时,假定我们有一个可选的参数,可以在形参列表该参数后加上?。注意可选参数和默认参数一样都应该放在参数列表的后面:

typescript 复制代码
function add(a: number, b: number, c?: number, d: number = 0): number {
    return a + b + (c || 0) + d
}

console.log(add(2, 3))
console.log(add(2, 3, 9))
console.log(add(2, 3, 9, 15))
// out
[LOG]: 5 
[LOG]: 14 
[LOG]: 29

还可以加入一个可变参数列表:

typescript 复制代码
function add(a: number, b: number, c?: number, d: number = 0,
            ...e: number[]): number {
    let sum = a + b + (c || 0) + d
    for(let i = 0; i < e.length; i ++) {
        sum += e[i]
    }
    return sum
}

console.log(add(1, 2, 3, 4, 5, 6, 7, 8, 9, 10))
// out
[LOG]: 55 

函数的重载

TypeScript 支持函数的重载(Golang 不允许函数的重载),但是不建议使用。

对象类型参数

假设我们现在有下述的函数定义:

typescript 复制代码
function sendRequest(
    url: string, 
    method: 'GET' | 'POST' | 'PUT',
    header: object,
    data: string,
    requireAuth: boolean,
    retry: boolean,
    retryTimeOut: number) {
        // ... ... ...
}

函数掉调用者在使用这个函数的时候,可能会给出非常冗长的参数列表,且我们无法从参数列表得知这个函数在做什么。

TypeScript 的一个解决办法是使用对象类型参数:

typescript 复制代码
function sendRequest(params: {
    url: string, 
    method: 'GET' | 'POST' | 'PUT',
    header: object,
    data: string,
    requireAuth: boolean,
    retry: boolean,
    retryTimeOut?: number,}) {
	// ... ... ...
}

参数直接是一个对象。函数调用时:

typescript 复制代码
sendRequest({
    url: 'https://www.test.com',
    method: 'GET',
    header: {
        contentType: '... ... ...'
    },
    data: '{}',
    requireAuth: true,
    retry: true,
    retryTimeOut: 3000
})

这种做法使得函数调用者非常的方便。

通过函数为对象定义方法

将函数定义在对象内部,即可完成对象方法的定义:

typescript 复制代码
const emp1 = {
    name: 'John',
    salary: 8000,
    performance : 3.5,
    bonus: undefined as (number | undefined),
    updataBonus() {
        if(!emp1.bonus) {
            emp1.bonus = emp1.salary * emp1.performance
        }
    },
}

emp1.updataBonus()
console.log(emp1)

// out
[LOG]: {
  "name": "John",
  "salary": 8000,
  "performance": 3.5,
  "bonus": 28000
} 

观察对象方法的定义:

typescript 复制代码
updataBonus() {
    if(!emp1.bonus) {
        emp1.bonus = emp1.salary * emp1.performance
    }
}

我们看到,必须通过 emp1 才能访问 bonus,但如果我们使用 emp2、emp3 来保存对象该怎么办呢?可以通过使用保留的关键字 this 来解决上述问题,修改后的方法如下:

typescript 复制代码
updataBonus() {
    if(!this.bonus) {
        this.bonus = this.salary * this.performance
    }
}

函数式编程

函数式编程是 TypeScript/JavaScript 非常大的一个亮点,这个特性非常适配于前端开发。TypeScript/JavaScript 的 Promise 也是基于函数式编程的。

使用函数式编程辅助数组的排序

之前在对数组的学习过程当中我们提到过,直接对 number 类型的数组使用 sort 方法之后,数组将按照字典顺序排序,而不是按照数字大小的顺序排序。现在我们希望借助函数实现按照 number 大小进行排序:

typescript 复制代码
function compareNumber(a: number, b: number) {
    // a < b -> 返回负数
    // a === b -> 返回 0
    // a > b -> 返回正数
    return a - b
}

let a = [5, 2, 1, 6, 8, 10, 5, 25, 16, 23, 11]
a.sort(compareNumber)
console.log(a)

👆将函数名称传递给函数,就是函数式编程。(看起来和 C++ 当中的函数指针非常的像,但函数式编程比函数指针复杂很多,唯一和函数指针相似的地方就是在上述例子当中)

在函数式编程中,函数是一等公民

函数作为一等公民时:

  • 变量类型可以是函数;
  • 值(literal)可以是函数;
  • 对象的字段可以是函数;
  • 函数的参数也可以是函数;
  • 函数的返回值可以是函数。

变量类型可以是函数

上述的 compareNumber 函数的另一种定义形式如下:

typescript 复制代码
const compareNumber = function(a: number, b: number) {
    return a - b
}

此时,compareNumber 是一个对象,它的类型是函数。

值(literal)可以是函数

上述 compareNumber 可以是一个变量,并被赋予其它的值(其它函数):

typescript 复制代码
let compareNumber = function(a: number, b: number) {
    return a - b
}
compareNumber = function(a: number, b: number) {
	return b - a
}	// 实现降序排序

对象的字段也可以是函数

一个例子如下:

typescript 复制代码
const emp1 = {
    name: 'John',
    salary: 8000,
    increaseSalary: function(p: number) {	// 此处不能使用箭头函数
    	this.salary *= p					// 箭头函数和 this 之间有坑
    }
}

函数的参数可以是函数

比如 sort 的参数是 compareNumber。

函数的返回值可以是参数

一个例子如下:

typescript 复制代码
function createComparer(greater: boolean = false) {
    return greater ? (a: number, b: number) => b - a : (a: number, b: number) => a - b
}

let a = [5, 2, 1, 6, 8, 10, 5, 25, 16, 23, 11]
a.sort(createComparer())
console.log(a)

lambda 表达式

上述 compareNumber 函数更简单的写法如下:

typescript 复制代码
let compareNumber = (a: number, b: number) => a - b

它是 lambda 表达式,在 TypeScript / JavaScript 中也被称为箭头函数。

一个快速实现排序的方法是:

typescript 复制代码
a.sort((a: number, b: number) => a - b)

=>后面可以像函数体一样使用{ ... }包裹,但此时必须有返回值。

高阶函数

高阶函数指的就是返回值是函数的函数,这个概念类似于函数的叠加与嵌套。一个例子如下:

typescript 复制代码
function loggingComparer(comp: (a: number, b: number) => number) {
    return (a: number, b: number) => {  // 对作为参数的函数进行包装
        console.log('comparing', a, b)  // 首先打印 log
        return comp(a, b)               // 再调用作为参数传入的函数
    }                                   // 看起来很像 Python 的 decorator
}

函数的闭包

在上述高阶函数 loggingComparer 的基础上,我们希望知道排序函数总共进行了多少次比较操作。可以通过函数的闭包(而不是设置全局变量)来实现上述功能。(使用全局变量的缺点在于全局变量或对象的状态字段需要维护,此外,打印同样改变了前端 UI 元素的状态,它同样也是一个副作用,我们应该尽可能地减少副作用,以提高用户体验)

一个函数闭包的例子如下,在下述代码片段中,函数 processArray 当中的局部函数 logger 和变量 compCount 是一个闭包,compCount 是它所携带的自由变量:

typescript 复制代码
function loggingComparer(logger: (a: number, b: number) => void, comp: (a: number, b: number) => number) {
    return (a: number, b: number) => {  
        logger(a, b)  
        return comp(a, b)               
    }                                   
}

function createComparer(params: {greater: boolean}) {
    return params.greater ? (a: number, b: number) => b - a : (a: number, b: number) => a - b
}

let compareNumber = (a: number, b: number) => a - b

function processArray(a: number[]) {
    let compCount = 0
    const logger = (a: number, b: number) => {
        console.log('comparing', a, b)
        compCount ++	
    }
    const comp = createComparer({greater: false})
    a.sort(loggingComparer(logger, comp))
    return compCount
}

let a = [5, 2, 1, 6, 8, 10, 5, 25, 16, 23, 11]

const compCount = processArray(a)
console.log(a)
console.log('Compare Count: ', compCount)

在上述例子中,随着 processArray 函数调用的结束,logger 函数也随之结束,compCount 作为返回值返回。但是如果 processArray 函数调用结束时,其内部的闭包由于某种原因尚未停止运行(比如一个线程),那么其所携带的自由变量的生命周期将会超越该函数的局部作用域。

部分应用函数

基于闭包可以实现部分应用函数。

一个部分应用的例子如下:

typescript 复制代码
const a = [1, 2, 3, 4, 5, 6, 7, 8, 9]
console.log(a.filter((v) => v % 2 == 0))
// out
[LOG]: [2, 4, 6, 8] 

一个等价的实现如下:

typescript 复制代码
function isGoodNumber(goodFactor: number, v: number) {
    return v % goodFactor === 0
}

function filterArray(a: number[], f: (v: number) => boolean) {
    return a.filter(f)
}

const GOOD_FATOR = 2

const a = [1, 2, 3, 4, 5, 6, 7, 8, 9]

console.log(filterArray(a, (v) => isGoodNumber(GOOD_FATOR, v)))
相关推荐
诗书画唱2 分钟前
【前端面试题】JavaScript 核心知识点解析(第二十二题到第六十一题)
开发语言·前端·javascript
excel8 分钟前
前端必备:从能力检测到 UA-CH,浏览器客户端检测的完整指南
前端
前端小巷子15 分钟前
Vue 3全面提速剖析
前端·vue.js·面试
悟空聊架构22 分钟前
我的网站被攻击了,被干掉了 120G 流量,还在持续攻击中...
java·前端·架构
CodeSheep23 分钟前
国内 IT 公司时薪排行榜。
前端·后端·程序员
尖椒土豆sss27 分钟前
踩坑vue项目中使用 iframe 嵌套子系统无法登录,不报错问题!
前端·vue.js
遗悲风28 分钟前
html二次作业
前端·html
江城开朗的豌豆31 分钟前
React输入框优化:如何精准获取用户输入完成后的最终值?
前端·javascript·全栈
CF14年老兵31 分钟前
从卡顿到飞驰:我是如何用WebAssembly引爆React性能的
前端·react.js·trae
画月的亮34 分钟前
前端处理导出PDF。Vue导出pdf
前端·vue.js·pdf