es6学习笔记之三 箭头函数

这篇学习笔记继续上一篇,为大家介绍es6给大家带来的一些实用功能,今天我们来简单说说箭头函数(=>)。我个人
非常喜欢这个功能,因为它可以使代码变得更简单,更容易阅读,而且某些时候还可以为我们解决javascript的一个
很头疼的问题:this问题。

1. 一些题外话

我们都知道es6是近两年才开始普及的javascript版本,虽然目前的几大浏览器都开始支持es6,但是旧版本的浏览器
和一些其他平台上却没有办法支持es6,那是不是这意味着我们如果要从兼容性方面考虑的话,就不应该使用es6了呢?
并不是!因为有这么一款神器的工具叫babel,它可以将es6甚至是es7的一部分新语法转换成es5,让我们简单来看一下
babel的网站:英文版:http://babeljs.io/,中文版:http://babeljs.cn/
这里需要讲一下,我建议如果你决定使用es6的话,就一定要会用babel,而幸运的是,网上babel的教程非常多,而且
babel本身也不是很难上手。这里我们先进入babel中文网站,然后点击最上面的“在线实验”,你会看到有两个在线编辑器
babel1.png

我们可以在左边的编辑器里打入我们想测试的es6代码,右边就会输出babel转换以后的es5代码,我们可以用这种方法
了解babel是如何把我们的es6代码进行转换的,举例来说,我在左边输入let a = 'hello world',右边就会显
示出var a = 'hello world',是不是很方便。但是要注意的是,这个在线实验只是个“山寨版”,真正的babel比
它要强大的多,所以千万不要把写好的es6代码简单的复制,粘贴到这里,然后把转换出来的es5贴回去。如果想使用
babel,最好还是使用它的命令行工具,官网里面有详细的介绍。

2. 用箭头函数简化代码

2.1 初识箭头函数

我们先来写一个最最简单的加法function,

1
2
3
4
5
const double = function(a){
return a*2;
}

console.log(double(3)); //这里会输出6

大家来看一下这个function,就算是这么简单的逻辑,我们还是一样要写function,return,各种大括号和小括号。是
不是感觉特别麻烦。这就是箭头函数存在的一个意义。我们来看一下箭头函数的版本应该怎么写:

1
2
3
4
5
const addition = (a)=>{
return a*2;
}

console.log(double(3)); //这里会输出6

是不是感觉清爽了一点?其实这里只是刚开始,其实还没怎么开始简化。我们去掉function这个词,但后在参数后面加上一个
粗箭头(=>),其实就是一个等于号加上一个大于号,就这么简单。其实它不仅仅省去了function这个单词,请仔细看箭头这
段代码,是不是感觉有点像函数式?箭头左边是输入,右边是输出,在这个例子里面,就是输入一个数,然后输出它的两倍。而
function本身不就是函数的意思么?这样是不是比function(a){...}更直观了呢?其实javascript里面有一个流派,
叫做函数式编程,这个流派会大量的使用这种函数式,让代码尽量符合输入=>输出这种函数的理念,同时尽可能的少使用面向对
象的概念,代码变得更纯净并且更好预测(给你一个函数,相同的输入一定会产出相同的输出)。

2.2 进一步简化

如果你并不关心所谓函数式编程,只是想简化代码,而且觉得仅仅这样还不过瘾的话,别着急,我们的代码其实还能继续简化,这
里说一下箭头函数的第二个简化规则:如果箭头右边的函数体只有一行代码并且需要return一个东西的话,那么我们可以去掉大括
号和return关键字,代码就变成了这样:

1
2
3
const addition = (a)=> a*2;

console.log(double(3)); //这里会输出6

对,就一行,更简洁了吧?这里再强调一下,你的代码体只有一行的时候,才能去掉大括号,如果多于一行,那么大括号和return
都必须保留。第二,如果你像上面一样,只写一行的话,那就意味着=>右边是你要return的内容,并且一定会return。还没完
呢,箭头函数还有第三个简化规则,就是如果你函数的参数(arguments)只有一个的话,那么你可以去掉小括号,代码就变成
了这样:

1
2
3
const addition = a=> a*2;

console.log(double(3)); //这里会输出6

怎么样?这样够简洁了吧!原来的function{…}直接变成了一个算术式。

3. 用箭头函数改变this指针

还是照例先上一段代码:

1
2
3
4
5
6
7
8
9
10
11
const person = {
name: 'John',
skills: ['javascript', 'html', 'css'],
sayHello: function(){
return this.skills.forEach(function(skill){
console.log(this.name+' knew '+ skill);
})
}

};
person.sayHello();

这段代码很简单,就是我们定义了一个object,然后有name和skills两个属性,其中skills是一个数组,里面有三个字符串,
然后我们在这个object上面定义一个sayHello的方法,对于skills数组里的每一个skill输出一段话,这里可以看到,我们
用了之前说过的forEach方法对数组进行迭代。但是,在我们执行这段代码的时候,会发现输出结果是这样的:

1
2
3
knew javascript
knew html
knew css

this.name并没有输出,我们在console.log(this.name+' knew '+ skill);之前加上console.log(this);
发现,this输出的是window或者global。这其实是因为forEach里面的那个function是回调函数,回调函数的this指向的是全
局上下文,在node环境里运行就是global,在浏览器运行就是window。

如果想让它正常的输出,我们通常会用这两种办法:

  1. bind改变this指针:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    const person = {
    name: 'John',
    skills: ['javascript', 'html', 'css'],
    sayHello: function(){
    return this.skills.forEach((function(skill){
    console.log(this);
    console.log(this.name+' knew '+ skill);
    }).bind(this))
    }
    };
  2. 对this进行缓存:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    const person = {
    name: 'John',
    skills: ['javascript', 'html', 'css'],
    sayHello: function(){
    var self = this;
    return this.skills.forEach(function(skill){
    console.log(self);
    console.log(self.name+' knew '+ skill);
    })
    }
    };

这两种方法我们应该都很熟悉吧。好,我们来改写一下sayHello这个方法,用箭头函数来代替function:

1
2
3
4
5
6
7
8
const person = {
name: 'John',
sayHello: function(){
return this.skills.forEach(skill=>{
console.log(`${this.name} knew ${skill}`);
})
}
};

当你用之前的person.sayHello()方法调用时,你会发现这个方法输出的是正确的,而且如果你试着在箭头函数里面输出
this时,发现它输出的是peron这个对象。这就是箭头函数的另一大特性,改变this指针,把this改变成这个function外面
一层的执行上下文。也可以这么理解,箭头函数不会创建独立的上下文,而是引用的外层的上下文。

我们再来看一个例子,这个例子证明了改变this指针未必总是好的:

1
2
3
4
5
6
7
8
9
10
const person = {
name: 'john',
getName: function(){
return this.name;
},
getNameArrow: ()=>this.name
};

console.log(person.getName());//这里正常输出john
console.log(person.getNameArrow());//这里无法输出

getName和getNameArrow唯一的区别就是一个用了箭头函数,一个用了普通的function。而用了箭头函数的getNameArrow中
this指针指向的是外层上下文,也就是全局上下文,而全局上下文里面没有name这个属性,所以无法正常输出。

4. 适合与不适合用箭头函数的情况

从上面的例子,我们能看出来,不是所有时候都要用箭头函数的,这里我大概向大家介绍一下我一般会在什么时候用箭头函数,什么
时候舍弃箭头函数而用普通的function函数

1)回调函数中适合使用箭头函数。这样可以使代码变得更简洁,更容易理解,比如forEach,map这些方法都需要传入一个回调函数
这就是一个使用箭头函数的好机会。

2) 高阶函数适合使用箭头函数,我们来看个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
//这是不用箭头函数的写法
const add = function(a){
return function(b){
return a+b;
}
};

console.log(add(2)(3));

//这是使用箭头函数的写法
const add = a=>b=>a+b;
console.log(add(2)(3));


代码看着是不是简单了很多。

3)在涉及到this时,除非需要使用apply,call或者bind去改变this指针,否则尽量不要用箭头函数。

总结

今天和大家简单的介绍了一下es6的又一个新功能:箭头函数,为什么要使用箭头函数,以及使用箭头函数简化代码的几个规则,并且给
大家看了箭头函数的另外一个功能:改变this指针。并且简单聊了一下我个人什么时候会用箭头函数,什么时候不会用,希望大家在以后
的工作和学习中可以尽量去尝试使用箭头函数,这样既可以简化代码,又可以使代码变得更清晰。