一、非箭头函数this指向
排除箭头函数,其他情况下this指向遵循下面规则:
-
在全局上下文中,
this指向全局对象(通常是window对象)。需要注意的是:
- 非严格环境下,全局下的
this指向window, 而在严格环境下是undefined。 setTimeout等都是挂载在window下的,因此setTimeout中的this,不管怎么写都指向window
- 非严格环境下,全局下的
-
在对象方法中,
this指向调用该方法的对象。 -
函数独立调用时,
this指向window,独立调用可以理解为,它没有上级。可以思考一下如下代码:
let obj = { a: function () { function b() { console.log(this); } b(); }, };输出的结果是
window,因为b函数是没有上级的,不像a函数作为方法调用,它的上级是obj。 -
立即执行函数【也就是定义后立即执行的匿名函数】的this指向
window。可以思考一下如下代码:
let obj = { a: (function () { console.log(this); })(), b: function c() { (function () { console.log(this); })(); }, }; obj.a; obj.b();上面两个都输出
window,因为都是执行函数,只不过a成了对象的属性,b成了对象的方法。 -
在构造函数中,
this指向新创建的实例对象。需要注意的是:
- 如果是function的构造函数,不加new的话会当成方法使用而不是构造函数,因此加new指向新构造的实例,不加new指向调用该方法的对象。
-
在事件处理函数中,
this通常指向触发事件的DOM元素。
function f1() {
console.log(this);
}
function f2() {
"use strict"; // 严格模式
console.log(this);
}
function f3() {
this.name = "zhangsan";
console.log(this);
}
f1(); // window
f2(); // undefined
f3(); // window 当成方法调用了而不是构造函数
new f3(); // f3新构造的实例对象
二、箭头函数this指向
箭头函数是JavaScript中的一个特殊情况。它们不会创建自己的this上下文,而是继承外部函数的this。这使得箭头函数的this是静态的,不会随着调用方式而改变。
三、隐式绑定导致的this丢失
在某些特殊情况下会存在this丢失的问题,常见的就是将调用函数作为参数传递或者变量赋值给另外一个变量,此时this指向window。
下面看代码:
let obj = {
a: function () {
console.log(this);
},
};
let b = obj.a;
obj.a();
b();
这里obj.a()运行的this指向obj对象,而b()运行的this指向window,因此b变量导致this丢失。上面的代码就相当于:
let obj = {
a: function () {
console.log(this);
},
};
let b = function () {
console.log(this);
};
obj.a();
b();
四、经典面试题
题1
var a = 'a'
let b = 'b'
function foo() {
console.log(this.a)
console.log(this.b)
}
foo()
此题先输出a再输出b,因此var将变量a提升至顶级,并挂载到了window上,而let``不存在变量提升,不会被挂载到window`上。
题2
const obj1 = {
fn: function () {
return this;
},
};
const obj2 = {
fn: function () {
return obj1.fn();
},
};
const obj3 = {
fn: function () {
var fn1 = obj1.fn;
return fn1();
},
};
console.log(obj1.fn()); // obj1
console.log(obj2.fn()); // obj1
console.log(obj3.fn()); // window
此题第一个输出很简单,指向调用fn()的对象obj1,
第二个输出就等同于在obj2对象内调用obj1对象的fn,因此fn方法指向调用它的obj1,
第三个输出出现隐式绑定导致的this丢失,因此就相当于在obj3的fn方法的作用域内新建了一个fn1函数,再在此作用域内独立执行函数,因为是独立执行,所以输出window。
题3
const obj = {
foo: () => {
console.log(this);
},
};
obj.foo();
此题输出window,因此foo为箭头函数,因此this指向上一级的上下文this,即obj所在的上下文window。
题4
let obj = {
say: function () {
var f1 = () => {
console.log(this);
};
f1();
},
pro: {
getPro: () => {
console.log(this);
},
getPro2: function () {
console.log(this);
},
},
};
let o = obj.say;
o(); // window
obj.say(); // obj
obj.pro.getPro(); // window
obj.pro.getPro2(); // pro
第一个输出发生隐式绑定导致this丢失,又因为f1是箭头函数,所以它的this继承o()的this,因此输出window。
第二个输出由于f1是箭头函数,所以它的this继承say()的this,因此输出obj。
第三个暂时没理解
第四个对象。
题5
var a = 1;
a = 2;
window.a = 3;
function foo() {
let a = 4;
this.a = 5;
setTimeout(function () {
console.log(a);
}, 10);
setTimeout(function () {
console.log(this);
console.log(this.a);
}, 20);
setTimeout(function () {
console.log(a);
}, 30);
setTimeout(function () {
console.log(this.a);
}, 40);
}
foo()
输出顺序是 4 5 4 5。
分步解析:
- 首先
var a = 1在全局对window上挂载了a: 1 a = 2重新赋值了window上的a属性window.a = 3同理- 执行
foo()这段代码,,这里没加new,所以把它当成函数执行,foo内的this指向window,所以执行this.a = 5后,window上的a被修改为5。
这样就很容易理解了,第一个输出没加this,因此输出局部变量a ,等于4,第二个输出加了this,因此输出window上的a,等于5,后面同理










