调用位置
理解调用位置:调用位置就是函数在代码中被调用的位置(而不是声明的位置);
只有仔细分析调用位置才能回答这个问题:这个this到底引用的是什么?
寻找调用位置最重要是要分析调用栈。
绑定规则
我们需要根据调用位置,来确定所采用的规则!
默认绑定:
最常用的函数调用类型,默认调用,这条规则是无法应用其它规则时的默认规则。
看一段最简单的代码:
var a=1;
function foo(){
console.log(this.a);
}
foo();//1
我们看一下运行情况:
声明在全局作用域中的变量,比如代码开始的声明 var a=1;就是全局对象的一个同名属性。
当调用foo时,this.a被解析成了全局变量,在本例子中,函数调用了this的默认绑定,因此指向全局对象。
在代码中,foo是直接不使用带任何修饰符的函数引用进行调用的,因此只能采用默认绑定。
如果使用严格模式,则不能将全局对象用于默认绑定,因此this会绑定到undefined:
var a=1;
function foo(){
"use strict"
console.log(this.a);
}
foo();//TypeError:this is undefined
虽然this的绑定规则完全取决于调用位置,但是只有foo运行在非严格模式下时,默认绑定才能绑定到全局对象
虽然foo在严格模式下,但是他不是this的调用位置,所以,下面的例子还是输出1;
var a=1;
function bar(){
console.log(this.a);//1
}
function foo(){
"use strict"
bar();//1
}
隐式绑定:
看一下下面的代码:
function foo(){
console.log(this.a);
}
var obj={
a:2,
foo:foo
};
obj.foo();//2
foo();//undefined
需要注意的是foo的声明方式,以之后是如何被当做引用属性添加到obj中的。
但是无论是直接在obj中定义还是先定义再添加为引用的属性,这个函数严格来说都不属于obj对象。
函数定义在全局作用域中,个人感觉属于全局作用域。
然而,调用位置会使用obj上下文来引用函数,因此可以说函数被调用时,obj对象“拥有”或“包含”它。
当函数引用有上下文对象时,隐式绑定规则会把函数调用中的this绑定到这个上下文对象。
个人感觉:
1.默认绑定也是隐式绑定的一种特殊情况,第一种情况的调用,可以被认为是
window.foo();
window对象也是一个对象呀!
2.取对象的属性的操作也是一种对上下文的体现,例如obj.foo();
隐式丢失:
在一些情况下,隐式绑定的函数会丢失绑定的对象而应用默认绑定,我们看一段代码:
function foo(){
console.log(this.a);
}
var obj={
a:2,
foo:foo
};
var bar =obj.foo;
var a=12;
bar();//12
虽然bar是obj.foo的一个引用,但是this的绑定取决于调用位置:bar函数调用时,上下文是window对象,因此使用了默认绑定。
在传入回调函数时,参数传递其实就是一种隐式赋值,因此我们传入函数时也会被隐式赋值。
function foo(){
console.log(this.a);//12
}
function doFoo(fn){
fn();//调用位置
}
var obj={
a:2,
foo:foo
};
var a=12;
doFoo(obj.foo);
如果把最后的调用改成 setTimeout(obj.foo,100);结果也是一样的,因为这个函数的效果跟下面的伪代码类似:
在doFoo里调用fn和在全局对象里调用fn是等效的!!!
function setTimeout(fn,delay){
//等待dely毫秒
fn();
}