一、了解this

       在面向对象的语言中 this 就是当前对象的一个引用,即当前环境的上下文对象,面向对象语言中 this 表示当前对象的一个引用。在JS中this会随着环境的改变而改变,此所谓世界上唯一不变的就是改变。


二、this指向总结

1、单独使用 this

        这种情况应该是最好理解的,this既不在方法中也不再对象中,而是出现在文档的第一层,默认情况下this当然指向当前文档这一对象。(严格模式下依然是指向全局对象)

2、this 在定义的函数中

        严格模式下("use strict"),函数是没有绑定到 this 上的,所以此时使用是 undefined。非严格模式下,函数的所属者默认绑定到 this 上,此时 this 就指向函数的所有者,如果你在全局定义的函数那么 this 就指向全局对象。

// 非严格模式
<script>
    var name = "xxx";

    function test() {
        let name = "yyy";

        console.log(this.name); // xxx
        console.log(name);      // yyy    //注意:如果函数内部未定义name则会向外寻找, 一直到最上一层,找到则打印,未找到打印 undefined
    }
    test1();
</script>


// 严格模式
<script>
    // 开始严格模式
    "use strict";

    var name = "xxx";

    function test() {
        var name = "yyy";

        console.log(this.name); // 报错不打印,提示name未定义
    }
    test1();
</script>

3、this 在定义的对象方法中

        如果 this 出现在对象的方法中, 那么当前函数归属于当前的对象,所以函数内的 this 的指向就是当前对象。

<script>
// 开启严格模式
"use strict";

var name = "ypf";

var person = {
    name: "John",
    log: function() {
        console.log(this.name); // John

        console.log(name);      // ypf  // 区别于在函数中的打印
    }
};

this.person.log();  // person.log(); 此时两种写法等效
</script>

        这个东西要灵活理解,就比如下面这个例子,对象中的属性的返回值是一个匿名函数,而匿名函数的归属于Windows对象,所以这个例子的匿名函数中的 this 就不指向 person 对象。

<script>
    // 开启严格模式
    // "use strict";

    var name = "ypf";

    var person = {
        name: "John",
        name2: this.name, 
        log: function () {
            console.log(this.name); // John

            return function () {
                console.log(this.name); // ypf  // 函数返回的是一个匿名函数,而匿名函数归属于Windows对象

                console.log(name);      // ypf
            }
        }
    };

    this.person.log()();

    console.log(person.name2);  // ypf  // 此时 this 就和person的归属对象保持一致
</script>

        下面是构造函数的例子,所谓构造函数,就是通过这个函数生成一个新对象,这时,this就指向这个对象,区别于对象中的函数。

<script>
    var testStr = "ypf";

    function Demo() {
        this.testStr = 'gj';
        this.fun = function() {
            console.log(this.testStr);
        }
    }

    let a = new Demo();

    console.log(a.testStr);   // gj
    
    a.fun()                   // gj

</script>

4、显式函数绑定 (apply、call、bind 

        显示绑定就不用过多解释了就是指那打那,apply、call、bind 都可以改变 this 的指向,区别在于返回值和第二个参数,大家可以自行百度。

<script>
    // 开启严格模式
    // "use strict";

    var person1 = {
        fullName: function () {
            return this.firstName + " " + this.lastName;
        }
    }
    var person2 = {
        firstName: "李",
        lastName: "婉莹",
    }

    console.log(person1.fullName.call(person2));  // 李婉莹
</script>

三、牛刀小试

实例1 :腾讯面试题

<script>
    var x = 20;
    var a = {
        x: 15,
        fn: function () {
            var x = 30;
            return function () {
                return this.x
            }
        }
    }
    console.log(a.fn());
    console.log((a.fn())());
    console.log(a.fn()());
    console.log(a.fn()() == (a.fn())());
    console.log(a.fn().call(this));
    console.log(a.fn().call(a));

// 答案

// 1.console.log(a.fn());
// 对象调用方法,返回了一个方法。
// # function() {return this.x}

// 2.console.log((a.fn())());
// a.fn()返回的是一个函数,()()这是自执行表达式。this -> window
// # 20

// 3.console.log(a.fn()());
// a.fn()相当于在全局定义了一个函数,然后再自己调用执行。this -> window
// # 20

// 4.console.log(a.fn()() == (a.fn())());
// # true

// 5.console.log(a.fn().call(this));
// 这段代码在全局环境中执行,this -> window
// # 20

// 6.console.log(a.fn().call(a));
// this -> a
// # 15
</script>

实例2:this指向Foo 

<script>
    function Foo() {
        getName = function() {
            alert(1);
        }
        return this;
    }
    Foo.getName = function() {
        alert(2);
    }
    Foo.prototype.getName = function() {
        alert(3);
    }
    var getName = function() {
        alert(4);
    }

    function getName() {
        alert(5);
    }

    Foo.getName();       // 2
    getName();           // 4
    Foo().getName();     // 1 
    getName();           // 1
    new Foo().getName(); // 3

</script>

实例3:this指向window

</script>
    function Foo() {
        var getName = function() {
            alert(1);
        }
        return this;
    }
    Foo.getName = function() {
        alert(2);
    }
    Foo.prototype.getName = function() {
        alert(3);
    }
    var getName = function() {
        alert(4);
    }

    function getName() {
        alert(5);
    }

    Foo.getName();          // 2
    getName();              // 4 
    Foo().getName();        // 4
    getName();              // 4
    new Foo().getName();    // 3
</script>