关于js中this的指向问题,我们首先要了解一下几个概念:
- 全局变量默认挂载在window对象下
- 一般情况下this指向它的调用者
- es6的剪头函数中,this指向创建者,并非调用者
- 通过call,apply,bind可以改变this的指向
在函数调用时
非严格模式
const fn1 = function () {
console.log(this);
const fn2 = function() {
console.log(this);
}
fn2();//window
}
fn1();//window
严格模式下
'use strict'
const fn1 = function () {
console.log(this);
const fn2 = function() {
console.log(this);
}
fn2();//undefined
}
fn1();//undefined
结合第一和第四条规则:fn1这个函数是全局的,默认挂载在window对象下,this指向它的调用者即window,所以输出window对象,但是在严格模式下,this不允许指向全局变量window,所以输出为undefined(fn2在函数直接调用时默认指向了全局window,其实这属于JavaScript设计上的缺陷,正确的设计方式是内部函数的this应该绑定到外层函数对应的对象上,为了规避这一设计缺陷,程序员就想出了变量替代的方法,约定俗成,该变量一般被命名为that。)
作为对象方法
const obj = {
name:'roy',
age:18,
infoFun:function(){
console.log('我的名字叫:'+this.name+',年龄:'+this.age);
const fn = function(){
console.log('我的名字叫:'+this.name+',年龄:'+this.age);
}
fn();//我的名字叫:undefined,年龄:undefined
}
}
obj.infoFun();//我的名字叫:roy,年龄:18
按照第二条规则,this指向它的调用者,infoFun()方法的调用者是obj,所以在infoFun()方法内部this指向了它的父对象即obj,而fn方法输出的为undefined的原因就是因为我们前面说的缺陷问题,这种情况下,我们通常选择在infoFun()方法里将this缓存下来。
const obj = {
name:'roy',
age:18,
infoFun:function(){
console.log('我的名字叫:'+this.name+',年龄:'+this.age);
const that = this;
const fn = function(){
console.log('我的名字叫:'+that.name+',年龄:'+that.age);
}
fn();//我的名字叫:roy,年龄:18
}
}
obj.infoFun();//我的名字叫:roy,年龄:18
此时fn的指向就理想了。
const obj = {
name:'roy',
age:18,
infoFun:function(){
console.log('我的名字叫:'+this.name+',年龄:'+this.age);
}
}
const other = obj.infoFun;
other();//我的名字叫:undefined,年龄:undefined
const data = {
name:'Tom',
age:20
}
data.infoFun = obj.infoFun;
data.infoFun();//我的名字叫:Tom,年龄:20
在看这段代码,将infoFun()赋值给了全局变量other,调用other()方法,other挂载在全局函数window对象下,window对象下没有name和age这两个属性,所以输出为undefined。第二段代码,申明了data对象,包含了name和age属性,在第二条规则一般情况下this指向它的调用者,大家就明白了,data是infoFun()的函数的调用者,所以输出了data的name和age。
在html里作为事件触发
<body>
<div id="btn">点击我</div>
</body>
<script>
const btn=document.getElementById('btn');
btn.addEventListener('click',function () {
console.log(this); //<div id="btn">点击我</div>
})
</script>
在这种情况下其实也遵循了第二条规则一般情况下this指向它的调用者,this指向了事件的事件源即event。
new关键字(构造函数)
const fn=function(name){
this.name=name;
}
const obj=new fn('roy');
console.log(obj.name); //roy
new关键字构造了一个对象实例,赋值给了obj,所以name就成为了obj对象的属性。
es6(剪头函数)
const fn1=()=>{
console.log(this);
};
fn1(); //Window
const data={
name:'roy',
infoFun:function(){
console.log(this); //Object {name: "roy", infoFun: function}
const fn2=()=>{
console.log(this); //Object {name: "roy", infoFun: function}
}
fn2();
}
}
data.infoFun();
根据第三条规则:es6的剪头函数中,this指向创建者,并非调用者,fn1在全局函数下创建,所以this指向全局window,而fn2在对象data下创建,this指向data对象,所以在fn2函数内部this指向data对象。
改变this的指向
call,apply,bind这三个函数是可以人为的改变函数的this的指向。
const fn=function(){
console.log(this);
};
fn(); //window
fn.apply({name:"roy"}); //Object {name: "roy"}