Javascript 的 this 用法/指向

this是JavaScript语言的一个关键字。

它是函数运行时,在函数体内部自动生成的一个对象,只能在函数体内部使用。

那么this到底怎么用?this的值是什么?this的指向是什么?

函数的不同使用场合,this有不同的值。总的来说,this就是函数运行时所在的环境对象。

大致能够分为四种情况

  • 纯粹的函数调用
  • 作为对象方法的调用
  • 作为构造函数调用
  • apply调用

纯粹的函数调用

作为函数调用时也就是全局调用,此时的this代表全局对象。

1
2
3
4
5
var x = 1;
function test() {
   console.log(this.x);
}
test();  // 1

作为对象方法的调用

当函数作为某个对象的时,this就指向这个上级对象

1
2
3
4
5
6
7
8
9
function test() {
  console.log(this.x);
}

var obj = {};
obj.x = 1;
obj.m = test;

obj.m(); // 1

作为构造函数调用

所谓构造函数,就是通过这个函数,可以生成一个新对象。此时的this指向这个新对象。而不是全局对象。

1
2
3
4
5
6
function test() {
 this.x = 1;
}

var obj = new test();
obj.x // 1

apply调用

apply()是函数的一个方法,作用是改变函数的调用对象。它的第一个参数就表示改变后的调用这个函数的对象。因此,这时this指的就是这第一个参数。

1
2
3
4
5
6
7
8
9
var x = 0;
function test() {
 console.log(this.x);
}

var obj = {};
obj.x = 1;
obj.m = test;
obj.m.apply() // 0

apply()的参数为空时,默认调用全局对象。因此,这时的运行结果为0,证明this指的是全局对象。

如果把最后一行代码修改为

obj.m.apply(obj); //1

运行结果就变成了1,证明了这时this代表的是对象obj

this的“前世今生”

隐藏参数this和普通参数arguments

除了箭头函数,每个函数都有argumentsthis。正如JS之父所说,JS的原创部分并不优秀,优秀之处并不原创。这两样东西就有很多难用之处。

arguments是一个伪数组,即没有数组共有属性的数组。如何变成真的数组?Array.from能使所有不是数字的东西变成数组。

如果不给任何条件,this默认指向window

在使用call()时,如果传的this不是对象,JS会自动把它封装成一个对象。在声明的时候加上 ’use strict’可避免JS的封装。

1
2
3
4
5
function fn(){
'use strict'
console.log(this)
console.log(arguments)
}

上述代码中如何传arguments

调用fn即可传arguments

fn(1,2,3)那么argumens就是[1,2,3]伪数组

如何传this

目前可以用fn.call(xxx,1,2,3)thisarguments

this就是xxx,而且xxx在不加’use strict’会被自动转换成对象

后面的1,2,3就是arguments

this就好比隐藏参数,而arguments是普通参数,this的本质是参数

在一个对象的名字未知的情况下,如何拿到它的引用?

首先是一种土办法,用参数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
//对象
let person = {
	name:'feank',
	sayHi(P){
		console.log(`你好,我叫`+p.name)
	}
}
person.sayHi(person)
//类
class Persom{
	constructor(name){this.name = name}
	sayHi(P){
		consoloe.log(`你好,我叫`+p.name)
	}
}

谁会用这种土办法?

python就用了

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
//python代码
class Person:
	def__init__(self,name): #构造函数
		self.name = name

	def sayHi(self):
		print('hi,I am '+self.name)

person = Person('frank')
person.sayHi()

这种方法的特点是

  • 每个函数都接受一个额外的self
  • 这个self就是传进来的对象
  • 只不过python会偷偷帮你传对象
  • person.sayHi()等价于person.sayHi(person)
  • person就被传给self

JS没有模仿Python的思路,而是走了另一条更难理解的路

JS在每个函数里加了this,并用this获取那个对象

1
2
3
4
5
6
let person = {
	name:frank,
	sayHi~~this~~ {
		console.log(`你好,我叫`+this.name)
	}
}
  • person.sayHi()相当于person.sayHi(person)
  • 然后person被传给this了(person是个地址)
  • 这样,每个函数都能用this获取一个未知对象的引用了

person.sayHi()会隐式地把person作为this传给sayHi,方便sayHi获取person对应的对象。

总结一下目前的知识

  • 我们想让函数获取对象的引用
  • 但是并不想通过变量名做到
  • Python通过一个额外的self参数做到
  • JS通过额外的JS做到

小白调用与大师调用

person.sayHi()person.sayHi(person)中,前一种是对的,虽然后一种看起来更加完整易于理解,但从语法角度上是错的。

JS为了解决这种不和谐,提供了两种调用方法。

  1. 小白调用法person.sayHi()会自动把person传到函数里作为this
  2. 大师调用法person.sayHi.call(person)需要自己手动把person传到函数里作为this

让我们看一个例子

1
2
3
function add(x,y){
	return x+y
}

大师调用法add.call(undefined,1,2)中为什么要多写一个undefined

因为第一个参数要作为this,而代码里没有this,所以只能用undefined占位,null也能起到同样效果。

小白调用发中,this隐式传递的。

而大师调用发中,this显式传递的。

add.call(undefined,1,2)等价于 add.apply(undefined,[1,2])