ES6 新增了箭头函数,使用起来非常简洁干练。
请看下面代码:
// 普通函数
const fn = function (arg1, arg2) {
// ...code
};
// 箭头函数,括号内的是参数
const fn = (arg1, arg2) => {
// ...code
};
// 箭头函数 无参数
setTimeOut(() => {
// ...code
});看了上面的例子,是不是觉得箭头函数很简便,少了很多代码,看起来也很舒适呢。特别是使用匿名函数时,非常清爽,是不是觉得以后要尽量使用箭头函数了。
但是,如果你是新手的话,在使用一段时候后你可能会发现,有时候箭头函数总是会出现一些莫名奇妙的错误,也不知道是什么原因。如果这时候把箭头函数换成普通的函数,错误又突然解决掉了。这到底是怎么回事呢?
这就要更深入的去了解下箭头函数了!箭头函数虽然简洁方便,但是不能滥用,特别是在与this相关的场景。
与 this 相关场景#
定义对象方法#
const hale = {
name: "Hale",
sayName: () => {
console.log(this.name);
},
};
hale.sayName(); // undefined定义构造函数#
const Person = (name) => {
this.name = name;
};
const hale = new Person("hale");
console.log(hale); // TypeError: Person is not a constructor定义函数原型方法#
function Person(name) {
this.name = name;
}
Person.prototype.sayName = () => {
console.log(this.name);
};
const jack = new Person("jack");
jack.sayName(); // undefined定义动态上下文的回调函数#
const p = document.querySelector("p");
p.addEventListener("click", () => {
this.innerText = "hello world";
}); // 无反应说明#
上面的几种场景都是与this相关的。
我们都知道函数(普通函数)里面的this的值是根据执行上下文来判断的, 是不固定的。但是,箭头函数内的this的值不一样,因为箭头函数没有自己的this,它里面的this指的就是外层代码块的this,也就是外层代码块的作用域,它是固定的。所以,不能使用箭头函数创建构造函数。
因为箭头函数没有this,也不能使用call(),apply(),bind()等改变this的指向。如果需要用到this指向本身函数或者对象的时候,就不能用箭头函数了。
当然,有时候也可以利用箭头函数的this指向外层代码块的情况来编写代码。比如 vue 组件的实例方法,使用定时器或者内层函数时,需要调用本身的属性和方法,就可以使用箭头函数和this了,这时候正好利用箭头函数的this指向外层代码块的情况。
export default {
data() {
return {
name: 'hale',
list: [],
target: {}
}
},
create() {
this.$axios.get('api/data')
.then(res => { // 箭头函数
this.list = res.data
// ...code
})
},
methods: {
method1() {
setTimeOut(() => { // 箭头函数
const name = this.name
// ...code
this.method2(list)
}, 1000)
},
method2(list) {
list.forEach(item => { // 箭头函数
// ...code
this.target = item.xxx
})
}
}
}稍微了解过 Vue 的源码的同学都知道,Vue 其实就是一个构造函数。整个 Vue 项目的入口就是 Vue 构造函数new出来的实例对象。
这个实例对象里面有自带很多属性和方法。Vue 实例里面的this指的就是 vue 实例本身。
例如,setTimeOut方法调用了箭头函数,而且还使用了this,这里的this指向的是setTimeOut外层的作用域,即 method1 方法的作用域,也就是 method1 的this的执行一样,而 method1 是一个普通函数,它里面的this指向的是调用这个方法的实例对象,也就是 Vue 实例,所以可以拿到 Vue 实例中 data 的数据,以及调用实例本身的方法。
如果把setTimeOut里面的箭头函数改成普通函数function(){}, 再使用this,this指向的就是setTimeOut本身,因为setTimeOut本身根本就没有data数据或者其他的方法,所以得到的只有undefined了。
所以,methods选项里面的方法,最好也不要使用箭头函数。如果使用了箭头函数,里面再调用this是无法访问 Vue 实例的属性和方法的,因为这时的this已经不是指向 Vue 实例了,而是指向 Vue 实例外层的作用域。
箭头函数用在什么场景中,主要还是需要理解箭头函数里面this的指向问题。
追求过短代码#
箭头函数虽然简洁方便,但是有时候过于简洁也不行,特别是在函数嵌套的情况下,会影响代码的可读性。
下面的例子中是两个参数相加的函数,如果只有一个参数就加上常量 5,使用箭头函数只有一行代码,可能比较难以理解,转换成普通函数就比较好理解了。
// 箭头函数
const add = (a, b) => (b === undefined ? (b) => b + 5 : a + b);
const addNum = add();
console.log(addNum(1)); // 6
console.log(add()(1)); // 6
console.log(add(4, 10)); // 14
// 普通函数
const add = function (a, b) {
if (b === undefined) {
return function (b) {
return b + 5;
};
}
return a + b;
};滥用箭头函数使得代码过于简短,语义化不明,不利于阅读。毕竟代码主要还是给人看的,而不是给机器看的。
其他情况#
还有一些其他的特殊场景也不适合使用箭头函数。
- 需要用到
arguments对象时,因为箭头函数里面没有arguments这个对象。 - Generator 函数, 因为箭头函数不能使用
yield命令。
总结#
- 箭头函数适合于无复杂逻辑或者无副作用的纯函数的场景。比如定时器
setTimeOut,setInterval; 数组对象的方法forEach,filter,findIndex,map,reduce等。 - 尽量不要在最外层的函数中使用箭头函数。因为箭头函数的
this指向的是外层作用域,这个作用域可能是全局作用域。函数里面如果需要用到this的话,会污染全局作用域。这时候应该先使用普通函数,然后再根据实际情况使用箭头函数,记住箭头函数的this指向的就是外面普通函数的作用域。 - 使用箭头函数不要追求过于简短的代码,还需要保证代码的可读性。