JavaScript——this指向问题详解
一、非箭头函数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
,后面同理
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!