为什么要学习this?因为复杂的前端应用现在都是面向对象编程,如果不掌握好this,开发过程就会出现各式各样的问题,所以面向对象的前端er,第一件事就是搞懂this.
什么是普通函数? => function () {}
什么是箭头函数? => () => {}
当然这两者肯定不是长相不同这么肤浅的区别,本篇文章主要集中分析一下两者不同的this指向。
普通函数看执行处
箭头函数看声明处
先从普通函数着手
看一个最简单的例子:
function testThis1() {
const a = 1; // testThis1的私有变量,不能被外界访问到
console.log(this.a);
}
// 普通函数的上下文(this)由执行处决定,因此这里的this为window || undefined , 取决于use strict
testThis1(); // => undefined || TypeError
既然普通函数的上下文(this)由执行处决定,因此我们可以通过apply/call/bind等方式更换testThis1的上下文。
const obj1 = {
a: 2
}
// 这里testThis1的上下文环境就是obj,其中的this.a就是obj.a
testThis1.call(obj1); // => 2
// 如果function是一个对象的方法呢?(其实在非严格模式下,testThis1就是window的一个方法)
const obj2 = {
a: 3,
fn: function() {
const a = 4;
console.log(this.a);
}
}
// 分析: 同样的obj2.fn是普通函数,其上下文由执行处决定,其this就是obj2
obj2.fn(); // => 3
如果,我们中间多个赋值过程呢?
const testThis2 = obj2.fn;
testThis2(); // => undefined
所谓不畏浮云遮望眼,对于普通函数我们只要找到其执行处就可以确定其上下文(this),这里fn的执行处并不在第一句,第一句仅仅是个赋值语句,真正的执行处是在第二句,而第二句的上下文就是window了
有点感觉了吗,看一个复杂的。
const obj3 = {
a: 5,
fn: function() {
const a = 6;
return function() {
console.log(this.a);
}
}
}
obj3.fn()(); // => undefined
// 如果我们想要obj3.fn()()输出5呢?现在应该很清楚了,我们将obj3.fn()()的上下文指向obj3就可以了
obj3.fn().apply(obj3); // => 5
不要怕函数嵌套,只要找到问题核心,一切都简单了。由于函数是从左向右执行的,我们可以看到obj3.fn()的上下文(this)毫无疑问是obj3,但要注意了obj3.fn()返回的又是一个函数,这里并没有指明其执行的对象,因此obj3.fn()()是window。
了解了普通函数的this之后,我们再来看es6中的箭头函数。还是从头开始一步步来。
const testThis3 = () => {
const a = 1;
console.log(this.a);
}
testThis3(); // => undefined || TypeError
箭头函数本身是没有上下文(this)的,如果在内部使用则需要向上回溯到最近那个拥有上下文的作用域,因此这里的this为window || undefined , 取决于use strict。
貌似结果与普通函数没差,我们再接着看
const obj4 = {
a: 2,
fn: () => {
const a = 3;
console.log(this.a);
}
}
// 这里就与普通函数有区别了,明明是obj4调用的fn,为什么结果不是3呢?
obj4.fn(); // => undefined || TypeError
需要明确一点,箭头函数本身是没有this的,也就是说,它的上下文在其声明处就已经确定好了,与调用它的对象无关,并且也无法通过apply/bind/call来变更上下文。
const obj5 = {
a: 4,
fn: function() {
const a = 5;
return () => {
console.log(this.a);
}
}
}
obj5.fn()(); // => 4
为什么这里的结果是4呢?我们一步步看。
obj5.fn()这一步里fn是普通函数,因此它的上下文是obj5,但它返回的是一个箭头函数,故不能在执行处判断它的上下文,需要到其声明处寻找离它最近的上下文,我们可以看到它的上下文由obj5.fn这个函数确定,因此obj5.fn()()与obj5.fn()的this相同,均为obj5。
虽然不同直接改变箭头函数的上下文,但我们可以通过改变obj5.fn()的this来间接改变obj5.fn()()的this.
const obj6 = {
a: 6,
}
obj5.fn.apply(obj6)(); // => 6
总结
普通函数this看执行处,如果我们要改变一个普通函数的上下文,直接通过apply/call/bind来改变
箭头函数this看声明处,如果我们要改变一个箭头函数的上下文,则需要通过改变其上层上下文来间接改变