1. 程式人生 > >js中this指向學習總結

js中this指向學習總結

在面向物件的語言中(例如Java,C#等),this 含義是明確且具體的,即指向當前物件。一般在編譯期繫結。
然而js中this 是在執行期進行繫結的,這是js中this 關鍵字具備多重含義的本質原因。下面就讓我們一起來分析一下具體情況。
由於js中this 是在執行期進行繫結的,所以js中的 this 可以是全域性物件、當前物件或者任意物件,這完全取決於函式的呼叫方式。JavaScript 中函式的呼叫有以下幾種方式:

  • 作為物件方法呼叫
  • 作為函式呼叫
  • 作為建構函式呼叫
  • 使用 apply 或 call 呼叫

JavaScript this決策樹

JavaScript this決策樹
根據這個決策樹我們需要進行兩步判斷:


1.函式呼叫是用new進行呼叫的嗎?如果是,則this指向新建立的物件,否則,進入”否”分支;
2.判斷該函式是否是用dot(.)進行呼叫的,如果是,則即進入”是”分支,即this指向dot(.)之前的物件;否則this指向全域性物件window.
demo1:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>this指向</title
>
</head> <body> <script type="text/javascript"> function test(a){ this.a = a;//相當於window.a } test(3);//這裡相當於window.test(3) //所以test函式中的this指向了window console.log(a);//3 這裡相當於與訪問window.a </script> </body> </html>

demo2:


對於say方法中的sayA和sayB中的this來說:不是通過new操作符來呼叫的,也沒有通過dot(.)來呼叫,因此this指向window

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>this指向</title>
</head>
<body>
<script type="text/javascript">
    var obj = {
        a:0,
        b:0,
        say:function(a,b){
            var sayA = function(a){
                console.log(this);//Window
                this.a = a;
            };
            var sayB = function(b){
                console.log(this);//Window
                this.b = b;
            };
            sayA(a);
            sayB(b);
        }
    }
    obj.say(1,1);
    console.log(obj.a+"--"+obj.b);//0--0
    console.log(window.a+"-----"+window.b);//1-----1
</script>
</body>
</html>

demo3:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>面試題</title>
</head>
<body>
<script type="text/javascript">
    function Person(name,age){
        this.name = name;
        this.age = age;
    }
    var person1 = new Person("lisi",18);
    console.log(person1.name);//lisi
    var person2 = Person("wangwu",12);
    //person2是undefined
    // console.log(person2.name);//Uncaught TypeError: Cannot read property 'name' of undefined
    console.log(window.name);//wangwu
    //Person("wangwu",12)相當於window.Person("wangwu",12)
</script>
</body>
</html>

demo4:
apply 和 call 這兩個方法切換函式執行的上下文環境(context),即可以改變this指向。obj1.say.call(obj2,3,3)實際上是obj2.say(3,3),所以say中的this就指向了obj2

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>this指向</title>
</head>
<body>
<script type="text/javascript">
    function Test(a,b){
        this.a = a;
        this.b = b;
        this.say = function(a,b){
            this.a = a;
            this.b = b;
        }
    }
    var obj1 = new Test(1,1);
    var obj2 = {a:2,b:2};
    obj1.say.call(obj2,3,3);
    console.log(obj2.a+"--"+obj2.b);//3--3
</script>
</body>
</html>

demo5:
this的指向在函式定義的時候是確定不了的,只有函式執行的時候才能確定this到底指向誰,實際上this的最終指向的是那個呼叫它的物件.當然這句話不完全準確,因為在不同環境下情況就會有不同。下面我們就來分析一下各種情況。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>this指向</title>
</head>
<body>
<script type="text/javascript">
    // 這裡的函式test實際是被Window物件呼叫的
    function test(){
        var name = "lisi";
        console.log(this.name);//undefined
        console.log(this);//Window
    }
    test();//這裡相當於window.test();
</script>
</body>
</html>

demo6:
如果一個函式中有this,這個函式有被上一級的物件所呼叫,那麼this指向的就是上一級的物件。
這個例子就是指向上一級物件obj

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>this指向</title>
</head>
<body>
<script type="text/javascript">
     var obj = {
        name:"lisi",
        sayName:function(){
            console.log(this.name);//lisi
        }
     }
     obj.sayName();
</script>
</body>
</html>

demo7:
物件字面量obj={}
變數obj其實也是window物件的屬性
所以可以這樣來呼叫window.obj.sayName();
如果一個函式中有this,這個函式中包含多個物件,儘管這個函式是被最外層的物件所呼叫,this指向的也只是它上一級的物件,這裡this指向obj,而不是window

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>this指向</title>
</head>
<body>
<script type="text/javascript">
    var name = "wangwu";
     var obj = {
        name:"lisi",
        sayName:function(){
            console.log(this.name);//lisi
        }
     }
     window.obj.sayName();
</script>
</body>
</html>

demo8:
如果一個函式中有this,這個函式中包含多個物件,儘管這個函式是被最外層的物件所呼叫,this指向的也只是它上一級的物件

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>this指向</title>
</head>
<body>
<script type="text/javascript">
     var obj = {
        name:"wangwu",
        objInner:{
            name:"lisi",
            sayName:function(){
            console.log(this.name);//lisi
            }
        }
     }
     obj.objInner.sayName();
</script>
</body>
</html>

demo9:
儘管物件objInner中沒有屬性name,這個this指向的也是物件objInner,因為this只會指向它的上一級物件,不管這個物件中有沒有this要的東西。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>this指向</title>
</head>
<body>
<script type="text/javascript">
     var obj = {
        name:"wangwu",
        objInner:{
            // name:"lisi",
            sayName:function(){
            console.log(this.name);//undefined
            }
        }
     }
     obj.objInner.sayName();
</script>
</body>
</html>

demo10:
從這個例子我們可以看出:this永遠指向的是最後呼叫它的物件,也就是看它執行的時候是誰呼叫的
在這個例子中雖然函式sayName是被物件objInner所引用,但是在將sayName賦值給變數fn的時候並沒有執行,所以this最終指向的是window物件。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>this指向</title>
</head>
<body>
<script type="text/javascript">
     var obj = {
        name:"wangwu",
        objInner:{
            name:"lisi",
            sayName:function(){
            console.log(this.name);//undefined
            console.log(this);//Window
            }
        }
     }
     var fn = obj.objInner.sayName;
     fn();
</script>
</body>
</html>

demo11:
物件person可以點出建構函式中的name是因為new關鍵字可以改變this的指向,將這個this指向物件person.
這裡new Person()建立了一個Person物件的例項,並賦值給了變數person

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>建構函式版this</title>
</head>
<body>
<script type="text/javascript">
    function Person(){
        this.name = "lisi";
    }
    var person = new Person();
    console.log(person.name);//lisi
</script>
</body>
</html>

demo12:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>當this碰到return</title>
</head>
<body>
    <script type="text/javascript">
    /*
    如果返回值是一個物件,那麼this指向的就是那個返回的物件,如果返回值不是一個物件那麼this還是指向函式的例項
     */
        function fn2(){
            this.username = "lisi";
            return {};
        }
        var obj = new fn2;
        console.log(obj);//Object{}
        console.log(obj.username);//undefined
        function fn1(){
            this.username = '王五';
            return function(){};
        }
        var b = new fn1;
        console.log(b);//function(){}
        console.log(b.username);//undefined
        function fn()
        {
            this.username = '王五';
            return 1;
            // return undefined;
        }
        var a = new fn;
        console.log(a);//fn {username: "王五"}
        console.log(a.username); //王五
        //雖然null也是物件,但是在這裡this還是指向那個函式的例項,因為null比較特殊
        function fn3()
        {
            this.username = '王五';
            return null;
        }
        var a = new fn;
        console.log(a);//fn {username: "王五"}
        console.log(a.username); //王五
    </script>
</body>
</html>

待續。。。