深入理解this
你可能听过很多this,就像C#中的this就是指向一个他的实例对象,
但是在javascript中,我可能要告诉你,他是在运行时动态绑定的
他指向什么,完全取决于函数在哪里调用
在这里还要说明一个东西,调用栈----------->通俗的来讲,就是为了到达一个方法所调用的所有方法
我们可以用浏览器自带的调试工具来查看
function foo(){
console.log(this.a);
}
var b={
a:10,
foo:foo
};
b.foo();//10
从运行的结果,我们就可以看出来,当调用b.foo()时this被绑定到了b
了解了调用栈以后,我们在来了解一个词,调用位置
调用位置,顾名思意,也就是我调用这个函数的上一个位置,也叫做调用位置0-0
function foo(){
console.log(this.a);
}
var a=20;
foo();//20
仔细的人就会发现,上面的调用位置和下面的调用位置都是全局,那么是什么让他们产生了差异?
如果你仔细的看的话,就会发现他们的差别就在于调用foo()的方法不同,上面的是b.foo(),而下面的只是foo();
也就是这一点的差异,让绑定发生的变化
为了说明这点我们需要指明绑定时的4个规则
1.默认绑定
function foo(){
console.log(this.a);
}
var a=20;
foo();//20
独立函数调用。可以把这条规则看作是无法应用其他规则时的默认规则。
在这里需要说明的是,只有在非严模式下,默认绑定才会绑定到全局,否则会报ReferencesError错误
2.隐式绑定
另一条需要考虑的规则是调用位置是否有上下文对象,或者说是否被某个对象拥有或者包含,不过这种说法可能会造成一些误导。
我们就拿上面的那个例子来说
function foo(){
console.log(this.a);
}
var b={
a:10,
foo:foo
};
a=20;
b.foo();//10
foo();//20
在这个例子中,我们通过b.foo();来调用foo()函数,在这里需要注意的是b.foo只是对foo()函数的一个引用
那么你可能就会说既然只是一个引用,那么我调用它和直接调用它没有什么区别吧,但是,我们还记得第一个标题?
this是在被调用时被绑定的!!! 而我们调用的地方正是b,所有this就指向了b!!!!!!!!!!!!
当然使用这种隐式绑定也有一定的缺点 !!! 请看下面的例子
function foo(){
console.log(this.a);
}
var b={
a:10,
foo:foo
};
a=20;
var bar=b.foo;
bar();//20 这里是20你想通了吗?
一种更微妙、更常见并且更出乎意料的情况发生在传入回调函数时
function foo(){
console.log(this.a);
}
var b={
a:10,
foo:foo
};
a=20;
function bind(fn){
fn();
}
bind(b.foo);//20 在这里输出的是20 是因为b.foo 仅仅就是一个函数的引用,所以他调用了默认绑定,指向this
上面的例子和下面的结果一样
function foo(){
console.log(this.a);
}
var b={
a:10,
foo:foo
};
a=20;
setTimeout(b.foo,1000);//20 可以看到结果和上面一样!!!!!!!!
3.显示绑定
Call(this,参数)
var b1={
a:10,
foo:function foo(){
console.log(this.a);
}
};
var b2={a:20};
b1.foo.call(b2);//20 在这里我们显示的让this指向了b2这个对象
Apply(this,参数数组)
var b1={
a:10,
foo:function foo(t,e){
console.log(this.a+t+e);
}
};
var b2={a:20};
b1.foo.apply(b2,[1,2]);//23 我们可以看到,他们除了在参数的传递上不同外,其他的都一样
bind
function foo(){
console.log(this.a);
}
var a=200;
var b={a:100};
setTimeout(foo.bind(b)//100
,1000);
当然在这里有必要说一下bind的语法 function.bind(Object),也就是将一个对象绑定到一个函数上
4.New绑定
使用 new 来调用函数,或者说发生构造函数调用时,会自动执行下面的操作。
1. 创建(或者说构造)一个全新的对象。
2. 这个新对象会被执行 [[ 原型 ]] 连接。
3. 这个新对象会绑定到函数调用的 this 。
4. 如果函数没有返回其他对象,那么 new 表达式中的函数调用会自动返回这个新对象
function foo(a) {
this.a = a;
}
var bar = new foo(2);
console.log( bar.a ); // 2
那么有这4个绑定规则,哪个的优先权更高呢?---------------------->请看下章