最近看了一些javascript相关的知识,打算整理一下

一、全局执行上下文中的this

console.log(this)

上面这个打印结果是:

print window

全局执行上下文中的this是指向window对象的

二、函数执行上下文中的this

function foo() {
	console.log(this)
}
foo()

上方这段代码的打印结果是:

print window

再看下下面这段代码,它的打印结果也是window对象

function bar() {
	console.log(this)
}

function foo() {
	bar()
}
foo()

print window

以上的示例说明,在默认情况下调用一个函数,其执行上下文中的this也是指向window对象的

2.1 如何指定函数执行上下文中的this

看以下示例代码,同一个函数,由于调用的方式不同,它内部的this指向了不同的对象。

在一个函数的执行上下文中,this由该函数的调用者提供,由调用函数的方式来决定其指向。

var a = 10;
var obj = {
    a : 20
}
function foo() {
	console.log(this)
    console.log(this.a)
}

foo();
foo.call(obj);

打印结果是:

javascript-this-1-example-1-print

如果想要指定函数执行上下文中的this,可以使用call/apply/bind这三种函数

2.1.1 call/apply指定this

当使用call/apply时,表示会执行该函数,并且函数内部的this指向call/apply的第一个参数。这两个方法传递参数的形式不同,call方法第一个参数是为函数内部指定this指向,后续的参数则是函数执行时候需要的参数,一个一个传递。apply方法的第一个参数含义与call方法相同,而函数执行需要的参数,以数组形式传递。

var a = 10;
var obj = {
    a : 20
}
function foo(b, c) {
    console.log(this.a + b + c) 
}

foo(10, 10);
foo.call(obj, 10, 10);
foo.apply(obj, [10, 10]);

打印结果是:

30
40
40

在javascript里面,数组里的每一项可以是任何类型的数据,所以当上方的foo函数需要的参数是不同类型的时候,apply方法也可以使用数组来传递参数。如下方示例代码。

var a = 10;
var obj = {
    a : 20
}
function foo(b, c, d) {
    console.log(this.a + b + c) 
    console.log(d)
}

foo(10, 10, 't');
foo.call(obj, 10, 10, 'test call');
foo.apply(obj, [10, 10, 'test apply']);

打印结果是:

30
t
40
test call
40
test apply

2.1.2 bind指定this

bind方法也可以指定函数内部的this指向,但是函数并不会立即执行,而是返回一个新的函数,这个新的函数与原函数有共同的函数体,且新函数的参数与this指向都已经被绑定

看下面的示例代码:

function foo(b, c) {
    console.log(this.a + b + c);
}

var a = 10;
var obj = {
    a : 20
}

var _foo = foo.bind(obj, 4, 5);
console.log(_foo);
console.log(_foo === foo);
_foo();
_foo(1, 2);

打印结果是:

javascript-this-2-example-2-print

可以看到最后一行代码_foo(1, 2)的打印结果是29,这是因为参数被绑定,所以重新传入参数是无效的

2.1.3 通过对象调用方法设置

var a = 10
var obj = {
	a : 20,
	showA : function() {
		console.log(this.a)
	}
}
obj.showA()

以上代码打印结果为:

20

这可以说明,使用对象来调用其内部的一个方法,该方法的this是指向对象本身的。但是如果对象的内部方法被赋值给了一个全局变量,使用该全局变量执行函数时,this又指向了window对象。看下面的示例代码:

var a = 10
var obj = {
	a : 20,
	showA : function() {
		console.log(this.a)
	}
}
var fn = obj.showA
fn()

以上代码打印结果为:

10

2.1.5 通过构造函数设置

在javascript里面创建对象有很多种方式,以下代码是通过构造函数创建对象

var a = 10
function Test() {
	this.a = 20
}
var obj = new Test()

当执行new Test()的时候,javascript引擎做的事情相当于下面这些:

var tempObj = {} // 创建一个空对象
Test.call(tempObj) // 指定Test执行上下文的this并执行Test函数
return tempObj // 返回tempObj对象

2.2 this的缺陷

缺陷一、2.2.1 嵌套函数中的this不会从外层函数中继承

var a = 10
var obj = {
	a : 20,
	showA : function() {
		console.log(this.a);
		function bar() {
			console.log(this.a)
		}
		bar()
	}
}
obj.showA()

打印结果是:

20
10

本来以为bar函数里面的this是继承外层的showA函数的,但实际上不是。实际表明,bar函数里的this是全局window对象。

为了解决这个问题,有两种方法,第一种方法是:可以用一个self变量来存储this。第二种方法是:使用箭头函数来实现嵌套函数

方法一:可以用一个self变量来存储this

var a = 10
var obj = {
	a : 20,
	showA : function() {
		console.log(this.a);
		var self = this
		function bar() {
			console.log(self.a)
		}
		bar()
	}
}
obj.showA()

以上代码打印结果是:

20
20

方法二:使用箭头函数来实现嵌套函数

var a = 10
var obj = {
	a : 20,
	showA : function() {
		console.log(this.a);
		var bar = () => {
			console.log(this.a)
		}
		bar()
	}
}
obj.showA()

以上代码打印结果是:

20
20

关于箭头函数可以看廖雪峰的这篇。

箭头函数与this:

  • 箭头函数默认绑定外层this

  • 箭头函数不能用call方法修改里面的this