update: 箭头函数

This commit is contained in:
qianguyihao 2020-07-15 21:05:45 +08:00
parent ea62903be5
commit ea8c465b43

View File

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