与其它语言相比,JS的this关键字的指向稍微有点差别。
一、全局环境
首选我们要明确一点,在全局环境中(在任意函数体外部),无论是严格模式还是非严格模式,this都指向window。下面,我们来通过几个例子理解一下。
//示例1
console.log(this)//这里的this在全局域下,指向window
//示例二
var a = 10;
var obj = {
a : 12,
b : this.a //这里的this在全局域下,指向window
}
console.log(obj.b) //返回10
二、函数环境
函数在未执行时,函数内部this指向undefined,函数被调用后,this指向取决于被调用的方式。同样的我们来通过几个例子来理解一下。
// 示例一
var a = 10;
function fun1(){
var a = 12;
console.log(this.a);//打印10
function fun2(){
console.log(this.a);//打印10
}
fun2();//这相当于 window.fun2() 在window 中调用fun1,this 指向window
}
fun1();//这相当于 window.fun1() 在window 中调用fun1,this 指向window
//示例二
var b = 10;
var obj = {
b : 12,
fun1 : function(){
console.log(this.b)//打印 12 打印10 打印12
return function fun(){
console.log(this.b) // 打印10
}
},
c : {
b : 14,
fun2 : function(){
console.log(this.b) //打印14
}
}
}
obj.fun1()//在obj 中调用fun1,this 指向obj 所以会打印 12
obj.c.fun2()//在c 中调用fun2,this 指向c 所以会打印 14
var res1 = obj.fun1 //将fun1赋给res1
res1() //这相当于 window.fun1() 在window 中调用fun1,this 指向window,所以打印10
var res2 = obj.fun1() //执行fun1中打印12,然后将fun1函数执行的返回值赋给res2,也就是将fun的函数体赋给res2
res2()//这相当于 window.fun() 在window 中调用fun,this 指向window,所以打印10
以上是我们常见的函数执行时的this 问题,当然除了以上这些基本问题之外,还会遇到立即执行函数,箭头函数,事件处理函数,构造函数的this指向问题,下面来介绍一下这些函数中的this指向问题。
1.立即执行函数中的this
立即执行函数中的this指向全局,也不难理解,立即执行函数在定义时即被执行,相当于在window下执行该函数。`
var num = 3;
var obj = {
num: 5,
fun: (function () {
var num = 4;
console.log(this.num) //this 执行window 打印3
})()
}
2.箭头函数
箭头函数的this指向和其上一层作用域的this指向一致。
var name = 'sun';
var obj = {
name: 'cat',
say: () => {
console.log(this.name);//打印sun
},
fun: function () {
console.log(this.name);//打印cat
}
};
obj.say();//say是箭头处理函数,this和上一层环境一致,上一层是全局环境,this指向window,所以打印sun
obj.fun();//fun是普通函数,谁调用指向谁,所以this指向obj,打印cat
3.在事件处理函数中,哪个元素触发事件,this就指向哪个元素
//获取 Id = Btn 的标签
var btn = document.getElementById('Btn');
//在事件处理函数中,哪个元素触发事件,this指向哪个元素
btn.onclick = function(){
console.log('点击了')
console.log(this) //this指向btn
this.style.color = 'red'
}
4.构造函数
在正常的构造函数中,构造函数中的this指向实例化对象。
function Person(){
this.name = 'cat',
this.age = 21,
this.friend = 'sun'
}
var person1 = new Person()
console.log(person1) // Person {name: "cat", age: 21, friend: "sun"}
function Person(){
this.name = 'cat',
this.age = 21,
this.friend = 'sun'
}
var person1 = new Person() //这里的构造函数中的this指向person1
console.log(person1) // Person {name: "cat", age: 21, friend: "sun"}
person1.hobby = 'game'//给person1添加属性,不影响原构造函数
console.log(person1) // Person {name: "cat", age: 21, friend: "sun", hobby: "game"}
var person2 = new Person() //这里的构造函数中的this指向person1
console.log(person2) // Person {name: "cat", age: 21, friend: "sun"}
当然,我们也可以手动破坏构造函数,当在构造函数中返回数组,对象,函数时,构造函数的this指向将会指向返回的数组,函数,函数。
//正常环境
function Person(){
this.name = 'cat'
}
var person = new Person();
console.log(person.name) //打印cat
//返回数组
function Person(){
this.name = 'cat'
return []
}
var person = new Person();
console.log(person)//打印 []
console.log(person.name) //打印undefined
//返回对象
function Person(){
this.name = 'cat'
return {}
}
var person = new Person();
console.log(person)//打印 {}
console.log(person.name) //打印undefined
//返回函数
function Person(){
this.name = 'cat'
return function fun(){}
}
var person = new Person();
console.log(person)//打印 ƒ fun(){}
console.log(person.name) //打印undefined
讲到这里,我们会发现this指向在使用时我们很难把控,那有没有方法将this指向我们自己所希望指向的对象呢?答案是肯定的。
三、this的显式函数绑定
我们可以通过call,apply,bind进行显式函数绑定。
var obj1 = {
name : 'cat',
say : function fun(){
console.log(this.name)
}
}
var obj2 = {
name : 'sun',
say : function fun(){
console.log(this.name)
}
}
obj1.say() // 函数的普通调用,谁调用this 指向谁,所以打印cat
obj1.say.call(obj2)//call强制改变obj1中this指向,并且规定指向obj2,所以打印sun
obj1.say.apply(obj2)//apply强制改变obj1中this指向,并且规定指向obj2,所以打印sun
var res = obj1.say.bind(obj2)//bind强制改变obj1中this指向,并且规定指向obj2,同时返回say函数体
res()//即使res是在全局环境调用,但是this依旧指向obj2,打印sun