在javascript中每执行一个函数时,会为函数创建一个执行上下文,并把他压入调用栈中,这个执行上下文包含了函数执行所需要的信息,如该函数是由谁调用的等等,而this也是这些信息中的一员。
this的值是在执行时去确认的,所以我们判断this的值分为两个部分,第一个部分是确认出函数被调用的位置,级、第二个部分是结合第一步分析出的结果,根据四个规则来确认this的值
一. 我们判断this的值首先第一步应该是判断出函数的调用栈信息,我举个例子
function frist() { // 调用栈是 全局
second();
}
function second() { // 调用栈是 全局 => first
third();
}
function third() { // 调用栈是 全局 => first => second
}
first();
(调用栈信息可以用调试器打断点来查看)
二. 结合第一步的调用栈信息,结合四个规则来判断this的值
1. 隐式绑定,可能有些地方是这样描述这个规则的,“调用一个对象的方法时,该方法的this指向该对象”,但是这样不是很准确,应该是,当一个函数拥有上下文对象时,该函数的this指向离函数最近的上下文对象(其实就是刚刚的调用栈信息),代码示例
var obj1 = {
foo: function() {
console.log(this);
}
}
obj1.foo() //obj1 foo函数被obj调用,他的上下文对象就是obj1
//下面解释什么是离函数最近的上下文对象
var obj2 = {
foo: function() {
console.log(this);
}
}
var obj3 = {
obj2: obj2
}
obj3.obj2.foo() //obj2 这时foo函数的上下文对象是 obj3 -> obj2 但是this指向最近 所以是obj2
2. 显式绑定,即用bind,call,apply来调用函数,可以为函数来绑定一个值
function foo() {
console.log(this);
}
var obj = {
}
foo.call(obj); //obj
foo.apply(obj); //obj
var bar = foo.bind(obj);
bar() //obj
3.使用new时 new FunctionName() 时,先会创建一个新的对象,然后如果没有return对象的话,就return这个新对象
function Person() {
}
new Person();
/**
相当于 function Person() {
var obj = {};
return obj;
}
Person();
**/
这时的this是执行这个新创建的对象的,就是obj
4. 默认绑定 默认情况就是说如果一个函数的调用方式匹配不了以上的三种方式,就会采用的方式
function foo() {
console.log(this);
}
foo() //global / undefined 非严格模式下指向global,严格模式下指向undefined
三. 优先级问题
在有些情况下,可能会同时匹配到多个规则,那么在这种情况下应该使用哪种呢?我先说明一下结论,new > 显示绑定 > 隐式绑定 > 默认绑定,具体的分析过程再写一篇blog
四. 特殊例子
上面的四个例子可能很简单,完全无法匹配我们日常中遇到的一下情况,下面我来举一些特殊例子来分析
var obj = {
foo: function() {
console.log(this);
}
}
var bar = obj.foo;
bar() //global或undefined 这是因为 把obj.foo赋值给bar后,bar的值就是函数foo的一个引用
//bar的调用是没有上下文对象 匹配默认绑定
function bar() {
console.log(this);
}
bar.call(null) //这时null会被忽视,函数保持原来的this值