谈谈JavaScript中的this

何为this

一般而言,在JavaScript中,this指向函数执行时的当前对象。换句话说,这个关键字与函数的执行环境有关,与声明环境无关。所以this的指向要看如何去调用这个函数而不是声明。

不同调用方式中的this

作为对象的方法调用

把函数赋值给对象的一个属性,然后通过该对象调用该方法,此时函数的执行环境就是这个对象,所以this指向该对象

1
2
3
4
5
6
7
8
9
var name = 'lala';
var obj = {
name: 'hehe',
show: function(){
console.log(this.name);
}
}

obj.show(); // hehe

换个更清晰的写法,我们把声明和调用放在两个对象里面

1
2
3
4
5
6
7
8
9
10
11
12
13
var obj = {
name: 'hehe',
show: function(){
console.log(this.name);
}
}

var t_obj = {
name: 'lala',
show: obj.show
}

t_obj.show(); // lala

可以看到show虽然是在obj中声明的,但是通过t_obj调用了这个方法,所以此时this指向t_obj。

作为函数调用

我们将上面的代码改一下,将obj.show赋值给全局变量show再调用他,此时this绑定到全局对象

1
2
3
4
5
6
7
8
9
var name = 'lala';
var obj = {
name: 'hehe',
show: function(){
console.log(this.name);
}
}
var show = obj.show;
show();// lala

在函数内部的函数调用

在函数内部调用一个函数,比如在一个对象的方法里面调用一个函数时,this会指向全局对象(讲道理的话应该指向对象)。这是JavaScript设计比较坑的一个地方,平时经常使用命名一个新变量ctx(context,即上下文环境)替代this。

1
2
3
4
5
6
7
8
9
10
11
12
var name = 'lala';
var obj = {
name: 'hehe',
show: function(){
var test = function(){
console.log(this.name);
}

test();
}
}
obj.show(); // lala

修正版:

1
2
3
4
5
6
7
8
9
10
11
12
13
var name = 'lala';
var obj = {
name: 'hehe',
show: function(){
var ctx = this;
var test = function(){
console.log(ctx.name);
}

test();
}
}
obj.show(); // hehe

作为构造函数调用

我们常使用new 构造函数名()来创建一个对象,此时函数中的this指向新创建的对象。如果不使用new,则和普通函数一样绑定到全局对象

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

setTimeoutsetInterval和匿名函数中

setTimeout,setInterval和匿名函数执行时的对象为全局对象,所以this也指向全局对象。

1
2
3
4
5
6
7
8
9
10
var name = 'lala';
var obj = {
name: 'hehe',
show: function(){
setTimeout(function(){
console.log(this.name);
},500);
}
}
obj.show(); // lala

函数调用callapply方法时

两者的本质的就是改变函数当前的上下文环境即this,两者的区别是call接受一个个参数,而apply接受一个参数数组。这两种方法会调用函数。

PS:使用callapply函数的时候要注意,如果传递的 this 值不是一个对象,JavaScript 将会尝试使用内部ToObject操作将其转换为对象。因此,如果传递的值是一个原始值比如 7 或 ‘foo’ ,那么就会使用相关构造函数将它转换为对象,所以原始值 7 通过new Number(7)被转换为对象,而字符串’foo’使用new String('foo')转化为对象

函数调用bind方法时

函数调用bind方法时会创建一个有相同函数体和作用域的函数,新函数的this指向bind的第一个参数。该方法不会调用函数,而是返回新函数。

参考