本文将详细介绍 “ JavaScript 中的 this 到底指向谁?”
this 指向问题核心就是把握如下两点:
1)this 指向的是对象
2)与函数或者方法如何声明的无关,而是要看这个函数或者方法最终被谁调用,谁最终调用这个函数或方法,那么这个函数或方法中的 this 就是谁
下面从简单到复杂的开始进行介绍。
普通函数中的 this
<script type="text/javascript">
//声明式函数
function showTime1(X) {
console.log("showTime1+" + X, this);
}
//声明式函数
function showTime2() {
this.showTime1(130);
}
showTime1(110);
window.showTime1(120);
showTime2();
</script>
showTime1(X)、showTime2() 作为声明式函数在编译的时候就已经绑定到了 Window 对象上,全局函数等同于给 Window 对象添加方法。
所以第 13 行、第 14 行调用 showTime1(X) 函数本质一样,因为 showTime1(X) 被 Window 对象调用,所以 showTime1(X) 函数中的 this 就表示 Window 对象。
第 15 行实在也同样是 Window 对象在调用 showTime2()函数,所以 showTime2() 中的 this 也就是 Windows 对象,而 Window 对象自然可以调用 showTime1(X)。运行结果如下:
对象方法中的 this
<script type="text/javascript">
let jsonObj = {
id: 9527,
name: "华安",
showAge: function () {
console.log("年方18", this);
console.log(this.id, this.name);
}
};
jsonObj.showAge();
</script>
jsonObj 是 Json 对象,第 10 行的 showAge() 方法由 jsonObj 对象调用,所以 showAge 方法中的 this 就表示 jsonObj 对象,运行结果如下:
复合案例
案例一:
<script type="text/javascript">
//声明式函数
function showTime() {
console.log(new Date().getTime(), this);
}
/**全局变量,也相当于为 window 对象添加的属性*/
var id = 1001;
var name = "宁王";
//Json 对象
let jsonObj = {
id: 9527,
name: "华安",
showAge: function () {
console.log("年方18", this);
console.log(this.id, this.name);
}
};
showTime = jsonObj.showAge;
showTime();
</script>
关键在第 20 行,将 jsonObj 对象中的 showAge 函数的函数体赋值给了 showTime,所以第 21 行调用的肯定是第 15-18 行,已经知道第 21 行 showTime()本质就是 window.showTime(),也就是说方法其实是被 window 对象调用了,于是第 16-17 行中的 this 就表示 window 对象。运行结果如下:
案例二:
<script type="text/javascript">
//声明式函数
function showTime() {
console.log(new Date().getTime(), this);
}
/**全局变量,也相当于为 window 对象添加的属性*/
var id = 1001;
var name = "宁王";
//Json 对象
let jsonObj = {
id: 9527,
name: "华安",
showAge: function () {
console.log("年方18", this);
console.log(this.id, this.name);
}
};
jsonObj.showAge = showTime;
jsonObj.showAge();
</script>
关键点同样在第 21 行,将 showTime 函数的函数体赋值给了 jsonObj 对象的 showAge 函数,所以第 22 行调用之后运行的肯定是第 3-5 行,而调用它的对象是 jsonObj 对象,所以第 4 行中的 this 就表示 jsonObj 对象。运行结果如下:
案例三:
<script type="text/javascript">
/**全局变量,也相当于为 window 对象添加的属性*/
var id = 1001;
var name = "宁王";
//Json 对象
let jsonObj = {
id: 9527,
name: "华安",
tiger: {
id: 8866,
name: "江小白",
showAge: function () {
console.log(this);
console.log(this.id, this.name);
}
}
};
jsonObj.tiger.showAge();
</script>
关键点在第 19 行,jsonObj 是 JSON 对象,它的属性 tiger 也是 JSON 对象,而真正调用 showAge() 函数的是 tiger 对象,所以第 15 行中的 this 表示 tiger 对象。运行结果如下:
构造函数中的 this
<script type="text/javascript">
/**ES5 中的类和普通函数写法类似,Tiger 就相当于一个构造器*/
function Tiger() {
this.id = 9527;
this.name = "华安";
console.log("构造器执行", this.id);
this.showAge = function () {
console.log(this.name + " 今年 28 岁.");
}
}
let tiger = new Tiger();
console.log(tiger);
console.log(tiger.id, tiger.name);
tiger.showAge();
</script>
new 关键字创建对象,构造器会执行,其中的 this 指向这个对象,同时这个对象会返回。
修改 this 默认指向
除了指定函数中 this 的指向之外,JavaScript 还提供了一套机制来修改 this 的默认指向,可以让函数中的 this 指向想让它指向的对象。
ES5 中通常采用函数上下文调用模式,如 call、apply、bind 等,
<script type="text/javascript">
/**全局变量,相当于给 window 对象添加属性*/
var id = 120;
var name = "华太师";
// json 对象
let json = {
id: 110,
name: "华安"
};
//全局函数,相当于给 window 添加方法
function showTime() {
console.log(this);
console.log(this.id, this.name);
}
//默认情况下调用时,其中的this表示window对象
showTime();
//使用 call 将 json 对象替换默认原来的 window 对象
showTime.call(json);
</script>
运行结果如下:
ES6 开始可以使用箭头函数 ()=> ,它会将 this 表示的对象往外抛一级,如下所示:
<!-- 箭头函数属于 ES6 ,所以 type 类型要改成 module-->
<script type="module">
/**
* Tiger 相当于一个类
* @constructor
*/
let Tiger = function () {
this.id = 110;
this.name = "宁王";
/**Dog 是一个 Json对象*/
this.Dog = {
id: 120,
name: "华太师",
/**非箭头函数写法*/
showInfo1: function () {
console.log(this.id, this.name);
},
/**箭头函数写法*/
showInfo2: ()=> {
console.log(this.id, this.name);
}
}
};
/**这种写法已经说过了,调用 showInfo1 函数的是 Dog 对象,里面的 this 就表示 Dog 对象*/
new Tiger().Dog.showInfo1();
/**虽然调用 showInfo2 函数的是 Dog 对象,但是箭头函数会往外抛一层,里面的 this 表示的是 Tiger*/
new Tiger().Dog.showInfo2();
</script>
运行结果如下: