当一个函数被调用时,会创建一个活动记录(有时候也称为执行上下文)。这个记录会包含函数在哪里被调用(调用栈)、函数的调用方式,传入的参数等信息。this 就是这个记录的一个属性,会在函数执行的过程中用到。
每个函数的 this 是在调用时被绑定的,完全取决于函数的调用位置(也就是函数的调用方法)。
1、调用位置
首先,我们需要理解调用位置: 就是函数在代码中被调用的位置(注意,不是申明的位置)。只有自信分析到调用的位置,才能知道这个 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 的调用位置
2、this 的绑定规则
默认绑定,隐式绑定,显式绑定, new绑定。首先找到调用位置,然后判断需要应用这四条规则中的哪一条。
首先,我们需要了解这四条规则,以及多条规则都适用时,他们的优先级该如何排列。
今天我们先来学习一下 默认绑定
最常用的函数调用规则:独立函数调用。
可以把这条规则看着是无法应用其他规则时的默认规则。思考一下下面代码:
function foo(){
console.log(this.a);
}
var a = 3;
foo(); //3
生命在全局作用域中的变量(比如 var a = 3 ),就是全局对象的一个同盟属性。他们本质上就是同一个东西,并不是复制得到的。
该例中,函数调用时应用了默认绑定,因此,this 指向的是全局对象。
那么我们怎么知道这里应用的是*默认绑定 *呢?
在代码中,foo() 是直接使用不带任何修饰的函数引用进行调用的,因此只能使用 默认绑定 。
需要注意的是:如果使用严格模式,则不能将全局对象用于默认绑定,因此,this 会绑定undefined。
function foo(){
"use strict";
console.log(this.a);
}
var a = 3;
foo(); // TypeError this is undefined
这里有一个微妙但是非常重要的细节:虽然 this 的绑定规则完全取决于调用位置,但是只有 foo() 运行在非 strict mode 下时,默认绑定才能绑定到全局对象;在严格模式下调用 foo() 则不影响默认绑定。
function foo(){
console.log(this.a);
}
var a = 3;
(fnction (){
"use strict";
foo(); //3
})();
\