Webcourse/10-ES6/05-ES6:函数扩展.md
2019-09-04 22:11:20 +08:00

258 lines
7.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## 前言
ES6在**函数扩展**方面,新增了很多特性。例如:
- 箭头函数
- 参数默认值
- 参数结构赋值
- 扩展运算符
- rest参数
- this绑定
- 尾调用
## 箭头函数
定义和调用函数:(传统写法)
```javascript
function fn1(a, b) {
return a + b;
}
console.log(fn1(1, 2)); //输出结果3
```
定义和调用函数ES6中的写法
```javascript
var fn2 = (a, b) => a + b;
console.log(fn2(1, 2)); //输出结果3
```
二者的效果是一样的。
在箭头函数中,如果方法体内有两句话,那就需要在方法体外边加上{}括号。如下:
```javascript
var fn2 = (a, b) => {
console.log('haha');
return a + b;
};
console.log(fn2(1, 2)); //输出结果3
```
从上面的箭头函数中,我们可以很清晰地找到函数名、参数名、方法体。
上方代码中:
- 如果有且仅有1个参数则`()`可以省略
- 如果方法体内有且仅有1条语句则`{}`可以省略,但前提是,这条语句必须是 return。
### this的指向
> 箭头函数只是为了让函数写起来更优雅吗当然不是还有一个很大的作用是与this的指向有关。
ES5 中this指向的是函数被调用的对象而 ES6 的箭头函数中this指向的是函数被定义时。
简单来说箭头函数中的this是不会变的是永远绑定在当前的环境下。
## 参数默认值
**传统写法**
```javascript
function fn(param) {
let p = param || 'hello';
console.log(p);
}
```
上方代码中,函数体内的写法是:如果 param 不存在,就用 `hello`字符串做兜底。这样写比较啰嗦。
**ES6 写法**:(参数默认值的写法,很简洁)
```javascript
function fn(param = 'hello') {
console.log(param);
}
```
在 ES6 中定义方法时,我们可以给方法里的参数加一个**默认值**(缺省值):
- 方法被调用时,如果没有给参数赋值,那就是用默认值;
- 方法被调用时,如果给参数赋值了新的值,那就用新的值。
如下:
```javascript
var fn2 = (a, b = 5) => {
console.log('haha');
return a + b;
};
console.log(fn2(1)); //第二个参数使用默认值 5。输出结果6
console.log(fn2(1, 8)); //输出结果9
```
**提醒1**:默认值的后面,不能再有**没有默认值的变量**。比如`(a,b,c)`这三个参数如果我给b设置了默认值那么就一定要给c设置默认值。
**提醒2**
我们来看下面这段代码:
```javascript
let x = 'smyh';
function fn(x, y = x) {
console.log(x, y);
}
fn('vae');
```
注意第二行代码我们给y赋值为`x`,这里的`x`是括号里的第一个参数,并不是第一行代码里定义的`x`。打印结果:`vae vae`。
如果我把第一个参数改一下,改成:
```javascript
let x = "smyh";
function fn(z, y = x) {
console.log(z, y);
}
fn("vae");
```
此时打印结果是:`vae smyh`。
## 扩展运算符
注意区分:
- 扩展运算符的格式为`...`
- rest运算符的格式为`...变量名`
有了ES6当我们在定义一个方法但是不确定其参数的个数时我们就可以用**扩展运算符**作为参数。
以前,我们在定义方法时,参数要确定个数,如下:(程序会报错)
```javascript
function fn(a, b, c) {
console.log(a);
console.log(b);
console.log(c);
console.log(d);
}
fn(1, 2, 3);
```
上方代码中,因为方法的参数是三个,但使用时是用到了四个参数,所以会报错:
![](http://img.smyhvae.com/20180304_1638.png)
现在,我们有了扩展运算符,就不用担心报错的问题了。代码可以这样写:
```javascript
function fn(...arg) { //当不确定方法的参数时,可以使用扩展运算符
console.log(arg[0]);
console.log(arg[1]);
console.log(arg[2]);
console.log(arg[3]);
}
fn(1, 2, 3); //方法中定义了四个参数但只引用了三个参数ES6 中并不会报错。
```
![](http://img.smyhvae.com/20180304_1650.png)
上方代码中注意arg参数之后不能再加别的参数否则编译报错。
**举例:**数组赋值的问题
我们来分析一段代码:(将数组 arr1 赋值给 arr2
```javascript
let arr1 = ['www', 'smyhvae', 'com'];
let arr2 = arr1; // 将 arr1 赋值给 arr2其实是让 arr2 指向 arr1 的内存地址
console.log('arr1:' + arr1);
console.log('arr2:' + arr2);
console.log('---------------------');
arr2.push('你懂得'); //往arr2 里添加一部分内容
console.log('arr1:' + arr1);
console.log('arr2:' + arr2);
```
运行结果:
![](http://img.smyhvae.com/20180304_1950.png)
上方代码中,我们往往 arr2 里添加了`你懂的`却发现arr1 里也有这个内容。原因是:`let arr2 = arr1;`其实是让 arr2 指向 arr1 的地址。也就是说,二者指向的是同一个内存地址。
如果不想让 arr1 和 arr2 指向同一个内存地址,我们可以借助扩展运算符来做:
```javascript
let arr1 = ['www', 'smyhvae', 'com'];
let arr2 = [...arr1]; //arr2 会重新开辟内存地址
console.log('arr1:' + arr1);
console.log('arr2:' + arr2);
console.log('---------------------');
arr2.push('你懂得'); //往arr2 里添加一部分内容
console.log('arr1:' + arr1);
console.log('arr2:' + arr2);
```
运行结果:
![](http://img.smyhvae.com/20180304_1951.png)
我们明白了这个例子,就可以避免开发中的很多业务逻辑上的 bug。
## `rest` 运算符
`rest` 在英文中指的是**剩余部分**(不是指休息)。我们来举个例子,理解剩余部分的含义:
```javascript
function fn(first, second, ...arg) {
console.log(arg.length);
}
fn(0, 1, 2, 3, 4, 5, 6); //调用函数后,输出结果为 5
```
上方代码的输出结果为 5。 调用`fn()`时,里面有七个参数,而`arg`指的是剩下的部分(因为除去了`first`和`second`)。
从上方例子中可以看出,`rest`运算符适用于:知道前面的一部分参数的数量,但对于后面剩余的参数数量未知的情况。
## 模块化
**模块化的意义**
比如说当我需要用到jQuery库时我会把jQuery.js文件引入到我自己代码的最前面当我需要用到vue框架时我会把vue.js文件引入到我自己代码的最前面。
可是如果有20个这样的文件就会产生20次http请求。这种做法的性能肯定是不能接受的。
如果把20个文件直接写在一个文件里肯定是不方便**维护**的。可如果写成20个文件这20个文件又不好排序。这就是一个很矛盾的事情于是模块化就诞生了。
**模块化历程**commonJS、AMD规范RequireJS、CMD规范SeaJSimport & export
**export**
静态化:必须在顶部,不能使用条件语句,自动采用严格模式。(静态化有利于性能以及代码的稳定性)