1.this指向绑定与函数调用的关系
在正式理解this指向之前,我们需要明确一点,this是在函数被调用执行时才确定绑定的。当函数执行时,会创建执行环境,此时确定this的指向。
this的绑定和函数调用息息相关,函数的四种调用方式(函数独立调用模式、方法调用模式、构造调用模式、间接调用模式)也对应了this的四种绑定方式(默认绑定、隐式绑定、new绑定、显示绑定)。
2.this的默认绑定
this默认绑定指向window,与之对应的是函数独立调用模式。可以分为五种情况:
1)全局环境下,this默认指向window
console.log(this);//window
2)函数独立调用时,函数内部this指向window
function fn() {
console.log(this);
}
fn();//window
3)被嵌套的函数独立调用时,this也默认指向window
var a='全局下的a';
var obj={
a:'obj的a',
foo:function () {
//foo函数作为对象的方法被调用, this指向该对象
var that=this;//此this指向obj
function test() {
console.log(this.a);//全局下的a
console.log(that.a);//obj的a
}
test();
}
}
obj.foo();//调用obj.foo时,test函数自动执行 其中的this指向window
在调用obj.foo()时,foo()是由obj调用的,他的this指向obj,但是test()是自动执行的,不是作为obj的方法执行的,他的this仍然是指向window。
4)自执行函数(IIFE)内部的this默认指向window
(function () {
console.log('自执行函数',this);
})();
5)闭包函数中的this默认指向window
var obj={
a:'obj的a',
foo:function () {
var c=this.a;//obj的a
return function test() {
console.log(this.a);//全局下的a
return c;//obj的a
}
}
}
var fn=obj.foo();
console.log(fn());
fn=null;
上例中,函数test是一个闭包函数,他的this指向window。
3.this的显示绑定
this的显示绑定对应的是函数的间接调用,主要有两种情况:
1)call,bind,apply方法绑定
var a="全局下的a";
function foo() {
console.log(this.a);
}
var obj={
a:"obj的a",
}
foo();//全局
foo.call(obj);//obj
foo.apply(obj);//obj
foo.bind(obj)();//obj
通过call,apply以及bind方法间接调用函数时,可以传入一个对象,将调用的函数foo中的this绑定到这个对象obj上。关于call、apply、bind方法此处不赘述。
2)在使用数组的forEach/map/filter/some/every等方法时,也可以传入对象,显示绑定this
//数组的forEach(fn,对象)
var arr=[1,2,3];
arr.forEach(foo);//全局下的a *3
arr.forEach(foo,obj);//obj的a *3
4.this的new绑定
this的new绑定对应函数的构造函数模式。当使用new关键字执行函数时,函数中的this指向当前的实例化对象。
function foo() {
console.log(this);//foo{}
}
var fn=new foo();
console.log(fn);//foo{}
因为此处函数foo没有返回值,所以创建的fn就是foo函数,this指向的是fn。
5.this的隐式绑定
this的隐式绑定对应的是函数的方法调用模式。当函数作为一个对象的方法被调用时,函数内部this指向该对象。
function foo() {
console.log(this.a);
}
var a='全局的a';
var obj={
a:'obj的a',
foo:foo,
obj2:{
a:'obj2的a',
foo:foo,
}
}
foo();//window
//foo()函数的直接对象是obj this的指向为直接对象
obj.foo();//obj
//foo()函数的直接对象是obj2 this的指向为直接对象
obj.obj2.foo();//obj2
在this的隐式绑定中要特别注意隐式丢失的几种情况。隐式丢失是指,被隐式绑定的函数丢失了绑定对象,因此又默认绑定到了window上。如下:
var a="全局下的a";
function foo() {
console.log(this.a);
}
var obj={
a:"obj的a",
foo:foo,
}
1)别名赋值
var bar=obj.foo;
bar();//全局下的a
很多人可能会觉得,bar调用后应该是输出‘obj的a’,其实不然。bar本身是和对象obj没有任何关系的。
因为在声明函数foo时,js是在堆中开辟了一块内存空间,用于存放foo函数内部的代码(以字符串的形式),然后将这个内存空间的地址给了foo,在对象objfoo:foo,
,以及var bar=obj.foo;
时,实际上赋值的都是内存地址,在执行时,bar根据内存地址,找到堆中函数体代码进行执行,这个过程和对象obj没有产生任何联系。为了便于理解,可以将obj看为:
var obj={
a:"obj的a",
foo:function(){
console.log(this.a);
},
}
将var bar=obj.foo;
看为:
var bar=function(){
console.log(this.a);
};
2)参数传递
function bar(fn) {
fn();
}
bar(obj.foo);//全局下的a
把obj.foo当做参数出入bar,相当于隐式的参数赋值 fn=obj.foo
,即
fn=function(){
console.log(this.a);
};
本质和别名赋值相同。
3)内置函数
setTimeout/setInterval的回调函数中的this默认指向window,也可以看做是参数传递
setTimeout(obj.foo,1000);//全局中的a
4)特殊情况
(obj.foo = obj.foo)();//全局下的a
(false||obj.foo)();//全局下的a
(1,obj.foo)();//全局下的a
这三种情况都可以看做是参数传递赋值,将obj.foo赋值给了自执行函数。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。