结论:
一 :在一个函数上下文中,this由调用者提供,由调用函数的方式来决定。如果调用者函数,被某一个对象所拥有,那么该函数在调用时,内部的this指向该对象。如果函数独立调用,那么该函数内部的this,则指向undefined。但是在非严格模式中,当this指向undefined时,它会被自动指向全局对象。

从结论中我们可以看出,想要准确确定this指向,找到函数的调用者以及区分他是否是独立调用就变得十分关键。

// demo01
var a = 20;
function fn() {
    console.log(this.a);
}
fn();  //fn()是调用者,为独立调用,非严格模式下指向全部对象window,因此结果为20
// demo02
var a = 20;
function fn() {
    function foo() {
        console.log(this.a);
    }
    foo();
}
fn(); //fn()是调用者,为独立调用,非严格模式下指向全部对象window,因此结果为20
// demo03
var a = 20;
var obj = {
    a: 10,
    c: this.a + 20,
    fn: function () {
        return this.a;
    }
}

console.log(obj.c);  // 对象obj中的c属性使用`this.a + 20`来计算。这里我们需要明确的一点是,单独的`{}`是不会形成新的作用域的,因此这里的`this.a`,由于并没有作用域的限制,所以它仍然处于全局作用域之中。所以这里的this其实是指向的window对象,因此结果为40。
console.log(obj.fn());  //fn()是调用者,他不是独立调用,被对象obj所调用,因此它的this指向指向obj因此结果为10

再来看一些容易理解错误的例子,加深一下对调用者与是否独立运行的理解。

var a = 20;
var foo = {
    a: 10,
    getA: function () {
        return this.a;
    }
}
console.log(foo.getA()); // 10

var test = foo.getA;
console.log(test());  // 20

foo.getA()中,getA是调用者,他不是独立调用,被对象foo所拥有,因此它的this指向了foo。而test()作为调用者,尽管他与foo.getA的引用相同,但是它是独立调用的,因此this指向undefined,在非严格模式,自动转向全局window。

稍微修改一下代码,大家自行理解。

var a = 20;
function getA() {
    return this.a;
}
var foo = {
    a: 10,
    getA: getA
}
console.log(foo.getA());  // 10

灵机一动,再来一个。如下例子。

function foo() {
    console.log(this.a)
}

function active(fn) {
    fn(); // 真实调用者,为独立调用
}

var a = 20;
var obj = {
    a: 10,~~~~
    getA: foo
}

active(obj.getA); // 20

二:使用call,apply显示指定this

JavaScript内部提供了一种机制,让我们可以自行手动设置this的指向。它们就是call与apply。所有的函数都具有着两个方法。它们除了参数略有不同,其功能完全一样。它们的第一个参数都为this将要指向的对象。

如下例子所示。fn并非属于对象obj的方法,但是通过call,我们将fn内部的this绑定为obj,因此就可以使用this.a访问obj的a属性了。这就是call/apply的用法。

function fn() {
    console.log(this.a);
}
var obj = {
    a: 20
}
fn.call(obj); //结果为20

三:箭头函数 this指向

箭头函数中,没有this。如果你在箭头函数中使用了this,那么该this一定就是外层的this。也正是因为箭头函数中没有this,因此我们也就无从谈起用call/apply/bind来改变this指向。
在ES6中,会默认采用严格模式,因此this也不会自动指向window对象了,而箭头函数本身并没有this,因此this就只能是undefined,这一点,在使用的时候,一定要慎重慎重再慎重,不然踩了坑你都不知道自己错在哪!这种情况,如果你还想用this,就不要用使用箭头函数的写法。

箭头函数 this指向是在定义的时候处在的对象就是它的this。箭头函数的this看外层的是否有函数,如果有,外层函数的this就是内部箭头函数的this,如果没有,则this是window。

var a = 10;
var obj = {
    a:99,
    fn1: () => {
    console.log(this.a)},
    fn2:function () {
    console.log(this.a)}
    }
    obj.fn1(); //10
    obj.fn2(); //99
 let btn1 = document.getElementById('btn1');
        let obj = {
            name: 'kobe',
            age: 39,
            getName: function () {
                btn1.onclick = () => {
                    console.log(this);//obj
                };
            }
        };
        obj.getName();

上例中,由于箭头函数不会创建自己的this,它只会从自己的作用域链的上一层继承this。

那假如上一层并不存在函数,this指向又是谁?

 let btn2 = document.getElementById('btn2');
        let obj = {
            name: 'kobe',
            age: 39,
            getName: () => {
                btn2.onclick = () => {
                    console.log(this);//window
                };
            }
        };
        obj.getName();

上例中,虽然存在两个箭头函数,其实this取决于最外层的箭头函数,由于obj是个对象而非函数,所以this指向为Window对象
参考文章:
全方位解读this
你还没搞懂this

典型面试例子及答案:

// 修改$对象里面的代码,使得以下代码运行正常
var $ = {
fn:function () {
console.log(1);
},
fn2:function () {
console.log(2);
}
}
$.fn().fn2();
// 答案:
var $ = {
fn:function () {
console.log(1);
return this; // 返回this,可以继续调用函数
        },
fn2:function () {
console.log(2);
        }
    }

$.fn().fn2();

guona
54 声望11 粉丝