function.md 9.0 KB
Newer Older
Q
qiang 已提交
1
# 函数(Function)
D
DCloud_LXH 已提交
2 3 4 5 6 7 8

函数是编程语言常见的功能,它可以封装一批代码,对外接收参数,然后返回值。被封装的逻辑,可以被不同的其他代码调用,达到共同复用逻辑的目的。

函数用 `function` 关键字定义,后面跟着函数名和圆括号。

同时注意,定义函数涉及作用域。

Q
qiang 已提交
9
## 定义函数
D
DCloud_LXH 已提交
10

Q
qiang 已提交
11 12 13
函数可以通过函数声明和匿名函数表达式两种方式进行定义。这两种方式在语法上有所不同,但都可以用来创建函数。

### 函数声明(Function Declaration)
D
DCloud_LXH 已提交
14 15 16 17 18

一个函数定义(也称为函数声明,或函数语句)由一系列在 function 关键字后的内容组成,依次为:

-   函数的名称。
-   函数参数列表,包围在括号中并由逗号分隔。
Q
qiang 已提交
19
-   函数返回值类型,除返回类型为 void 外,函数必须明确标明返回值类型。
D
DCloud_LXH 已提交
20 21 22 23 24 25 26 27 28 29 30 31 32
-   定义函数的 uts 语句,用大括号 `{}` 括起来。

例如,以下的代码定义了一个简单的函数。函数名为 add,有2个参数 x 和 y,都是 string类型,函数的返回值类型也是 string。

函数的内容是将入参 x 和 y 相加,赋值给变量z,然后通过 return关键字返回z。

```ts
function add(x :string, y :string) :string {
    let z : string = x + " " + y
	return z;
}
```

Q
qiang 已提交
33
### 函数表达式(Function Expression)
D
DCloud_LXH 已提交
34

Q
qiang 已提交
35 36 37
虽然上面的函数声明在语法上是一个语句,但函数也可以由函数表达式创建。这样的函数可以是匿名的。

例如,函数 add 也可这样来定义:
D
DCloud_LXH 已提交
38 39

```ts
Q
qiang 已提交
40 41
const add = function (x: string, y: string): string {
    return x + " " + y
D
DCloud_LXH 已提交
42 43 44
}
```

Q
qiang 已提交
45
### 箭头函数
D
DCloud_LXH 已提交
46

Q
qiang 已提交
47
箭头函数表达式(也称胖箭头函数)相比函数表达式具有较短的语法。箭头函数总是匿名的,也就是不需要函数名。
D
DCloud_LXH 已提交
48 49

```ts
Q
qiang 已提交
50 51 52 53 54 55 56 57 58 59
// 以下是不使用箭头函数的写法
const arr = ["Hydrogen", "Helium", "Lithium", "Beryllium"];
const a2 = arr.map(function (s): number {
    return s.length;
});
console.log(a2); // logs [ 8, 6, 7, 9 ]

// 以下是使用箭头函数的写法,逻辑是等价的
const a3 = arr.map((s): number => s.length);
console.log(a3); // logs [ 8, 6, 7, 9 ]
D
DCloud_LXH 已提交
60 61
```

Q
qiang 已提交
62 63 64 65 66 67 68 69 70 71 72
### 无返回值的函数(void)

如果这个函数不需要返回值,可以省略返回值类型也可以使用 void 关键字进行显式声明,同时函数内部末尾也可以省略 return。

```ts
function add(x: string, y: string): void {
    let z: string = x + " " + y
    console.log(z)
    // return
}
```
D
DCloud_LXH 已提交
73

Q
qiang 已提交
74
## 调用函数
D
DCloud_LXH 已提交
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99

定义一个函数并不会自动的执行它。定义了函数仅仅是赋予函数以名称并明确函数被调用时该做些什么。调用函数才会以给定的参数真正执行这些动作。

定义了函数 add 后,你可以如下这样调用它:

```ts
function add(x :string, y :string) :string {
    let z :string = x + " " + y
	return z;
}
add("hello", "world"); // 调用add函数
```

上述语句通过提供参数 "hello" 和 "world" 来调用函数。

虽然调用了add函数,但并没有获取到返回值。如需要获取返回值,需要再赋值:
```ts
function add(x :string, y :string) :string {
	let z :string = x + " " + y
	return z;
}
let s :string = add("hello", "world");
console.log(s) // hello world
```

Q
qiang 已提交
100
## 函数作用域
D
DCloud_LXH 已提交
101 102 103 104 105 106 107 108 109 110 111 112 113

在函数内定义的变量不能在函数之外的任何地方访问,因为变量仅仅在该函数的域的内部有定义。相对应的,一个函数可以访问定义在其范围内的任何变量和函数。

```ts
const hello :string = "hello";
const world :string = "world";

function add(): string {
	let s1 :string = "123";
    return hello + world; // 可以访问到 hello 和 world
}
```

Q
qiang 已提交
114
### 嵌套函数
D
DCloud_LXH 已提交
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138

你可以在一个函数里面嵌套另外一个函数。嵌套(内部)函数对其容器(外部)函数是私有的。它自身也形成了一个闭包。一个闭包是一个可以自己拥有独立的环境与变量的表达式(通常是函数)。

既然嵌套函数是一个闭包,就意味着一个嵌套函数可以”继承“容器函数的参数和变量。换句话说,内部函数包含外部函数的作用域。

可以总结如下:

-   内部函数只可以在外部函数中访问。
-   内部函数形成了一个闭包:它可以访问外部函数的参数和变量,但是外部函数却不能使用它的参数和变量。

举例:

```ts
function addSquares(a: number, b: number): number {
    function square(x: number): number {
        return x * x;
    }
    return square(a) + square(b);
}
addSquares(2, 3); // returns 13
addSquares(3, 4); // returns 25
addSquares(4, 5); // returns 41
```

Q
qiang 已提交
139
### 命名冲突
D
DCloud_LXH 已提交
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158

当同一个闭包作用域下两个参数或者变量同名时,就会产生命名冲突。更近的作用域有更高的优先权,所以最近的优先级最高,最远的优先级最低。这就是作用域链。链的第一个元素就是最里面的作用域,最后一个元素便是最外层的作用域。

举例:

```ts
function outside(): (x: number) => number {
    let x = 5;
    const inside = function (x: number): number {
        return x * 2;
    };
    return inside;
}

outside()(10); // 返回值为 20 而不是 10
```

命名冲突发生在 `return x` 上,inside 的参数 x 和 outside 变量 x 发生了冲突。这里的作用链域是 `{inside, outside}`。因此 inside 的 x 具有最高优先权,返回了 20(inside 的 x)而不是 10(outside 的 x)。

Q
qiang 已提交
159
## 闭包
D
DCloud_LXH 已提交
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182

uts 允许函数嵌套,并且内部函数可以访问定义在外部函数中的所有变量和函数,以及外部函数能访问的所有变量和函数。

但是,外部函数却不能够访问定义在内部函数中的变量和函数。这给内部函数的变量提供了一定的安全性。

此外,由于内部函数可以访问外部函数的作用域,因此当内部函数生存周期大于外部函数时,外部函数中定义的变量和函数的生存周期将比内部函数执行时间长。当内部函数以某一种方式被任何一个外部函数作用域访问时,一个闭包就产生了。

举例:

```ts
const pet = function (name: string): () => string {
    //外部函数定义了一个变量"name"
    const getName = function (): string {
        //内部函数可以访问 外部函数定义的"name"
        return name;
    };
    //返回这个内部函数,从而将其暴露在外部函数作用域
    return getName;
};
const myPet = pet("Vivie");
myPet(); // 返回结果 "Vivie"
```

Q
qiang 已提交
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
## 作为值传递

使用函数表达式(Function Expression)的创建的函数可以作为值进行传递,使用函数声明(Function Declaration)创建的函数不能作为一个值进行传递。

```ts
function fn1() {

}

const fn2 = function () {

}

const fn3 = fn2 // 正确,值为函数表达式
const fn4 = fn1 // 错误,值为函数声明

console.log(fn1) // 错误,值为函数声明
console.log(fn2) // 正确,值为函数表达式
console.log(function () {

}) // 正确,值为函数声明
```

## 函数类型

函数表达式也拥有类型,其类型表达式为 `(参数名:参数类型)=>返回值类型`,如:

```ts
const fn: (x: string) => void
```

函数表达式必须与其类型定义中的参数类型、参数个数以及返回值类型严格匹配

```ts
fn = function (x: string) { } // 正确,类型匹配
fn = function (x: string, y: string) { } // 错误,参数类型不匹配
fn = function (x: string): string { return x } // 错误,返回类型不匹配
```

Q
qiang 已提交
222
## 默认参数
D
DCloud_LXH 已提交
223

W
wanganxp 已提交
224
函数参数可以设默认值,当省略相应的参数时使用默认值。此时该参数也就成了可选参数。
D
DCloud_LXH 已提交
225 226 227 228 229

```ts
function multiply(a:number, b:number = 1):number {
  return a*b;
}
W
wanganxp 已提交
230
multiply(5); // a为5,未传b的值,b默认为1,结果为5
D
DCloud_LXH 已提交
231
```
lizhongyi_'s avatar
lizhongyi_ 已提交
232

W
wanganxp 已提交
233
可以在常规的函数定义的场景下使用默认参数,例如:
lizhongyi_'s avatar
lizhongyi_ 已提交
234 235 236 237 238 239 240 241
```ts
function print(msg:string = "") {
  console.log(msg)
}
print(); // ""


class Persion {
W
wanganxp 已提交
242
	test(msg: string | null = null) { // 默认值可以为null
lizhongyi_'s avatar
lizhongyi_ 已提交
243 244 245 246 247
		
	}
}
```

Q
qiang 已提交
248
通过函数表达式定义的函数不支持使用默认参数
lizhongyi_'s avatar
lizhongyi_ 已提交
249 250 251

```ts
// 该变量会被编译成Swift或者Kottlin的闭包表达式,其不支持使用默认参数。
Q
qiang 已提交
252
const test = function(msg: string | null) { }
lizhongyi_'s avatar
lizhongyi_ 已提交
253 254
```

Q
qiang 已提交
255
因为需要作为值进行传递,对象字面量中定义的方法会自动转换为函数表达式,所以也不支持默认参数
lizhongyi_'s avatar
lizhongyi_ 已提交
256 257 258 259 260 261 262 263 264 265 266 267

```ts
// 该属性会被编译成Swift或者Kottlin的闭包表达式,其不支持使用默认参数。
export  {
	data: {
		test(msg: string | null) {
			
		}
	}
}

```
Q
qiang 已提交
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292

## 剩余参数

剩余参数语法允许我们将一个不定数量的参数表示为一个数组。

使用 `...` 操作符定义函数的最后一个参数为剩余参数。

```ts
function fn(str: string, ...args: string[]) {
  console.log(str, args)
}
```

在这种情况下可以传递可变数量的参数给函数:

```ts
fn('a') // 'a' []
fn('a', 'b', 'c') // 'a' ['b', 'c']
```

也可以使用 `...` 操作符传递一个数组值给函数的剩余参数(转到 swift 时未支持):

```ts
fn('a', ...['b', 'c']) // 'a' ['b', 'c']
```