javascript中关于this的理解

首先看一下这几个定义

  • this对象是在运行时基于函数的执行环境绑定的:在全局函数中,this等于window,而当函数被视为某个对象的方法调用时,this等于那个对象。

    不过,匿名函数的执行环境具有全局性,因此其this对象通常指向window.

  • 每个函数在被调用的过程中都会自动取得两个特殊变量:this和arguemtns。内部函数在搜索这两个变量的时候,只会搜索到其活动对象为止,因此永远不可能访问外部函数中的这两个变量。

  • this实际上是在函数被调用时发生的绑定,它指向什么完全取决于函数在哪里被调用。

首先,先排除几个错误的认知:

1、this对象是指向自身的。

function foo(num){

console.log(num);

this.count++;

}

foo.count=0;

for(var i=0;i<5;i++){

foo(i)

};

console.log(foo.count); //0

通过这个实例可以看出,count虽然被设置为了foo的属性。但是这里的this并不是指向foo自身的,因此当输出foo.count值的时候,其依然是0.

2、this是指向自身的词法作用域的,也可以简单理解成对于变量对象的引用吧。

function foo(){

var a=2;

console.log(this.a);

}

foo(); //undefined;

我们在foo函数的作用域内定义了a的值为2,然而当我们输出a的值的时候却可以的到undefined,因此这样的理解也是不对的。

实际上,this是在函数被调用时发生的绑定,它指向什么完全取决于在哪里调用。

所以说,我们的第一步就是判断函数的调用位置。

 function baz(){
       //当前调用栈是:baz
       //因此,当前的调用位置是全局作用域
       console.log('baz');
       bar();//bar的调用位置
    }
function bar(){
   //当前调用栈是baz->bar
   //因此调用位置是在baz中
   console.log('bar');
   foo();//<-foo的调用位置

}
function foo(){
   //目前调用栈是baz->bar->foo
   //因此,当前调用位置是在bar中
   console.log('foo')

}
baz();//<--baz的调用位置

分析出了他们的调用位置之后,接下来就是要判断它是应用于哪种绑定规则的。

1、默认绑定 独立函数调用-指向全局

var a=2;

function foo(){

console.log(this.a);

}

foo(); //2

这个例子中,函数的调用位置是全局环境,所以其指向全局即等于window(注意这是在非严格模式下)

var a=2;

function foo(){

'use strict';

console.log(this.a);

}

foo(); //undefined

这时,由于严格模式的原因将会输出undefined;

var a=100;

function foo(){

var a='foo中的a';

bar();

}

function bar(){

console.log(this.a);

}

foo();//100

由调用位置决定的另外一个示例。

2、隐式绑定 调用位置是否有上下文对象,或者说是否被某个对象拥有或者包含。

function foo(){

console.log(this.a);

}

var obj={

a:2,

foo:foo

}

obj.foo(); //2

当函数引用有上下文对象时,隐式绑定规则会把函数调用中的this绑定到这个上下文对象中。当然,也有一些隐式绑定的函数丢失绑定对象的问题,例如:

var a='全局中的a';

function foo(){

console.log(this.a);

}

var obj={

a:2,

foo:foo

}

var s=obj.foo;

s(); //全局中的a;

s只是obj.foo的一个引用,而在当s调用的时候已经没有了上下文对象,因此将会默认绑定,从而输出‘全局中的a’

还有一种情况:

var a='全局中的a';

function s(fn){

fn();<--调用位置

}

function foo(){

console.log(this.a);

}

var obj={

a:2,

foo:foo

}

s(obj.foo);

fn只是引用了obj.foo,而在obj.foo的调用位置没有任何特殊绑定,所以是默认绑定指向全局。

3、显式绑定 用call/apply方法。

function foo(){

console.log(this.a);

}

var obj={

a:'董志强',

foo:foo

}

foo.call(obj);

//通过foo.call(..),我们可以在调用foo时强制把他的this绑定到obj上。

4、new绑定

1创建一个新对象

2这个对象会被执行[proto]连接

3这个新对象会绑定到函数调用的this.

4如果函数没有返回其他对象,那么new表达式中的函数会自动返回这个新对象。

function foo(a){

this.a=a;

}

var b=new foo(2);

console.log(bar.a);//2

大概就是这样子的吧,判断出具体的位置之后,再判断绑定方式,这样就可以判断出this的指向了。也有一些例外情况,例如foo.call(null)或者foo.apply(undefined)的情况,在没有其他绑定的情况下,这时foo函数里面的this是指向全局的。

这时你会发现是不是好多问题都理解更深刻了,像下面,.

function Person(name,age){

this.name='dong';

this.age=29

}

Person.prototype.z=2;

var person=new Person();

alert(person.z);//2

alert(person.name);//'dong'

都知道,person.z是person对象沿着原型链向上查找得到的,因为Person.prototype属性里面有z这个属性。

而对于name和age来说他们,则是new绑定,this会指向当前new的对象,所以相当于自身创建了name和age这两个属性,而不是向上查找得到的。