箭头函数极大地简化了this的取值规则。
普通函数与箭头函数
普通函数指的是用function定义的函数:
var hello = function () { console.log("Hello, Fundebug!"); }
箭头函数指的是用=>定义的函数:
var hello = () => { console.log("Hello, Fundebug!"); }
JavaScript箭头函数与普通函数不只是写法上的区别,它们还有一些微妙的不同点,其中一个不同点就是this。
箭头函数没有自己的this值,箭头函数中所使用的this来自于函数作用域链。
这句话很简单,不过听着稍微有点莫名其妙,得从头说起。
this到底是什么?
关于this的文章也够多了,有时候越描越黑,我就不再添乱了,我只负责搬运一下MDN文档:this,感兴趣的可以仔细阅读一下,我摘录一些最重要的话就好了。
A function's this keyword behaves a little differently in JavaScript compared to other languages. It also has some differences between strict mode and non-strict mode.
JavaScript是一门比较奇特的语言,它的this与其他语言不一样,并且它的取值还取决于代码是否为严格模式("use strict")。
this的值是什么?
The JavaScript context object in which the current code is executing.
this就是代码执行时当前的context object。
Global context
In the global execution context (outside of any function), this refers to the global object whether in strict mode or not.
代码没有在任何函数中执行,而是在全局作用域中执行时,this的值就是global对象,对于浏览器来说,this就是window。
这一条规则还是比较容易接受的。
Function context
Inside a function, the value of this depends on how the function is called.
函数中的this值取决于这个函数是怎样被调用的,这一条规则就有点变态了,也是很容易出BUG的地方。
另外,this的值还与函数是否为严格模式("use strict")有关,这就非常的丧心病狂了...
大家如果好奇的话,出门左转看MDN文档,我多说无益,只说明一种简单的情况。
As an object method
When a function is called as a method of an object, its this is set to the object the method is called on.
当函数作为对象的方法被调用时,它的this值就是该对象。
var circle = { radius: 10, getRadius() { console.log(this.radius); } }; circle.getRadius(); // 打印 10
self = this?
当我们需要在对象方法中嵌套一个内层函数时,this就会给我们带来实际的困扰了,大家应该写过这样的代码:
// 使用临时变量self var circle = { radius: 10, outerDiameter() { var self = this; var innerDiameter = function() { console.log(2 * self.radius); }; innerDiameter(); } }; circle.outerDiameter(); // 打印20
outerDiameter函数是circle对象的方法,因此其this值就是circle对象。
那我们直接写this.radius多好啊,可惜不能这么写,因为内层函数innerDiameter并不会继承外层函数outerDiameter的this值。outerDiameter函数的this值就是circle对象,this.radius等于10。
但是,innerDiameter函数的this值不是circle对象,那它到底是啥?它是innerDiameter函数执行时当前的context object,这个context object又是啥?其实我也晕了,所以不妨测试一下:
// innerDiameter函数中的this是window var circle = { radius: 10, outerDiameter() { var innerDiameter = function() { console.log(this === window); }; innerDiameter(); } }; circle.outerDiameter(); // 打印true
innerDiameter函数中的this是window,为啥是window这个不去管它,反正不是circle对象。
因此,如果我们直接在innerDiameter函数中使用this的话,就出问题了:
// 使用普通函数 var circle = { radius: 10, outerDiameter() { var innerDiameter = function() { console.log(2 * this.radius); }; innerDiameter(); } }; circle.outerDiameter(); // 打印NaN
于是,我们不得不使用一个临时变量self将外层函数outerDiameter的this值搬运到内层函数innerDiameter。
.bind(this)
我们也可以使用.bind(this)来规避this变来变去的问题:
// 使用.bind(this) var circle = { radius: 10, outerDiameter() { var innerDiameter = function() { console.log(2 * this.radius); }; innerDiameter = innerDiameter.bind(this); innerDiameter(); } }; circle.outerDiameter(); // 打印20
但是,无论是使用临时变量self,还是使用.bind(this),都不是什么很简洁的方式。