排除箭头函数,其他情况下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
指向新创建的实例对象。
需要注意的是:
在事件处理函数中,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新构造的实例对象
箭头函数是JavaScript
中的一个特殊情况。它们不会创建自己的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
,后面同理