对于一个前端初学者来说,this 的指向是一个必须要掌握的知识点,尤其是 ES6 之后的 this 指向更加变得飘忽不定,我们今天就来了解一下在 browser 中各种情况下 this 的指向。

# 什么是 this

首先,this 不是一个函数也不是某个对象,它具体指向什么东西取决于我们在哪里去调用这个 this,只有函数调用的时候才会发生 this 的绑定,主要有有以下几种情况

  • 全局作用域
  • 函数作用域
  • 作为对象方法和对象的属性值
  • 构造函数中
  • 箭头函数中
  • class 类中

# 1. 全局作用域

console.log(this === window) //true

当我们在全局作用域中调用的时候,this 指向的是 window 对象,相当于 window 调用的这个 tihs

Philadelphia's Magic Gardens. This place was so cool!

# 2. 函数作用域

function test(){
  console.log(this)  //this指向window
 }
 test()

此时会发现在函数体内 this 也是指向的 window,究其缘由是因为在全局调用的 test 函数,当我们调用的时候会进行默认绑定,也就是绑定到 window 上

Philadelphia's Magic Gardens. This place was so cool!

# 3. 作为对象方法和对象的属性值

var Person = {
	name: 'vittore',
	that: this,
	age: function () {
		console.log(this) //Person对象
		console.log(this.name) //vittore
	},
}
console.log(Person.that) //window 类似纯函数调用
Person.age() //调用对象的age方法

此时会看到,当我们把 this 作为对象的某个方法去调用的时候,this 指向的是这个对象,相当于这个对象调用这个方法

Philadelphia's Magic Gardens. This place was so cool!

var Person = {
	obj: {
		name: 'vittore_',
		sex: function () {
			console.log(this) //obj对象
			console.log(this.name); //vittore_
		},
	},
}
Person.obj.sex() //调用penson对象中的obj对象的sex方法

另外,在嵌套对象中,this 指向同样是谁调用则指向谁,这时候是 obj 对象调用的。

Philadelphia's Magic Gardens. This place was so cool!

# 4. 构造函数中

var name = 'vittore'
function fun(name, age) {
	this.name = name
	this.age = age
}
var obj = new fun('vittore_', 20)
console.log(obj) //控制台打印如下

Philadelphia's Magic Gardens. This place was so cool!

此时,this 指向的是构造函数所创建出来的实例、即 obj 对象

# 5. 箭头函数中

var obj = {
    name:'vittore',
    say:()=>{
       console.log(this)
  }
}
obj.say()//控制台打印出了window

Philadelphia's Magic Gardens. This place was so cool!

箭头函数没有自己的 this, 会默认继承父级执行上下文的 this,这里的上下文 this 就是 window

# 5.1 函数执行时会创建一个称为执行上下文的内部对象 (可理解为作用域)

:一个执行上下文定义了一个函数执行时的环境,而对象是没有执行上下文的。

var obj = {
		name: 'vittore_',
		// person: function () {
		// 	return {
		// 		name: 'vittore',
		// 		say: () => {
		// 			console.log(this)
		// 		},
		// 	}
		// },
		person: () => {
			return {
				name: 'vittore',
				say: () => {
					console.log(this)
				},
			}
		},
}
obj.person().say()
//调用say方法这里的this指向的是obj对象,因为箭头函数没有this会向上查找,此时上级则是一个普通函数,而普通函数创建时会绑定this,如果person函数也是箭头函数的话this则指向window

Philadelphia's Magic Gardens. This place was so cool!

# 6.class 类中

class Person {
		constructor(name) {
			this.name = name
			this.say = function () {
				console.log('my name is ' + this.name)
			}
		}
		sleep = function () {
			console.log(this)
		}
}
var obj = new Person('vittore')
obj.say() //调用obj对象的say方法 控制台打印 ‘my name is vittore’
obj.sleep() //调用obj对象的sleep方法 打印 obj对象

Philadelphia's Magic Gardens. This place was so cool!

可以看到在 class 中的 this 都是指向这个构造函数生成的对象

# 更改 this 指向(面试的常问点)

可以通过如下方式进行更改

  • bind
  • call
  • apply
var Person = {
	name: 'vittore',
	say: function (age) {
		console.log(`my name is ${this.name},i am ${age} years old`) 
	},
}
var obj = {
	name: 'vittore_',
}

直接调用
Person.say ()

Philadelphia's Magic Gardens. This place was so cool!

通过 bind ()

  • Person.say.bind (obj,'22',' 第二个参数 ',...)()
    Philadelphia's Magic Gardens. This place was so cool!
  • Person.say.bind (obj)('22',' 第二个参数 ',...)
    Philadelphia's Magic Gardens. This place was so cool!

:bind 返回的是一个重新绑定 this 的函数,因此需要调用,两种传参方式,参数依次加在后面

通过 call ()

  • Person.say.call (obj,'23',' 第二个参数 ') //call 返回的是重新绑定 this 并且立即调用之后的结果,参数依次加在后面
    Philadelphia's Magic Gardens. This place was so cool!

通过 apply ()

  • Person.say.apply (obj,['24',' 第二个参数 ']) // 返回的同样是绑定了 this 并且立即调用之后的结果,参数是数组格式
    Philadelphia's Magic Gardens. This place was so cool!