javascript 函數的4種調用方式與 this(上下文)的指向


前言:這是筆者學習之后自己的理解與整理。如果有錯誤或者疑問的地方,請大家指正,我會持續更新!

   javascript 中作用域鏈和 this(上下文)的指向是很容易混淆的,簡單的說就是:

  1. 作用域鏈取決於函數聲明的位置,函數聲明之后,從函數內部往外,一直到window,這就是它的作用域鏈,與函數調用位置無關
  2. this 指向函數調用時的對象,如果是獨立調用,那就是指向 window,與函數聲明的位置無關

  函數調用的方式有4種,this 也就有4種指向

  1. 獨立調用:func(),函數獨立調用,this指向window,;
  2. 方法調用:obj.func(),函數作為obj的一個方法(屬性)調用,this指向obj;
  3. 構造函數調用:new Func(),如果在一個函數前面帶上 new 關鍵字來調用, 那么背地里將會創建一個連接到該函數的 prototype 的新對象,this指向這個新的對象;
  4. call、apply、bind調用:func.call(obj,value1,value2);  func.apply(obj,[value1,value2]); func.bind(obj,value1,value2)();  func.bind(obj)(value1,value2); 動態改變 this 的指向 obj;

獨立調用和方法調用

  全局環境中,this 默認指向到 window;

  函數獨立調用(不論這個函數在哪調用),this 默認指向到 window;

  當函數被作為對象的方法(對象的一個屬性)調用時,this 指向該對象;

  函數只有調用了之后才有 this 的概念,不然它只是代碼而已,我們經常把函數傳給一個變量,如果后面沒加括號,就只是傳了個函數,而不是執行結果;

        <script type="text/javascript">
            console.log(this === window);//true  全局環境中,this默認指向到window
            
            function abc(){
                console.log(this === window);
            }
            abc();//true   函數abc獨立調用(不論這個函數在哪調用),this默認指向到window

            function outer(){
                function inner(){
                    console.log(this === window);
                }
                inner();
            }
            outer();//true  函數inner獨立調用(不論這個函數在哪調用),this默認指向到window
            
            var o = {
                m: function(){
                    return this;
                },
                n: function(){
                    function test(){
                        return this;
                    }
                    return test(); //函數test獨立調用(不論這個函數在哪調用),this默認指向到window
                }
            };
            console.log(o.m());//Object o{...}  當函數被作為對象的方法(對象的一個屬性)運行時,this指向該對象
            console.log(o.n());//Window{...}  函數test獨立調用,this默認指向到window
        </script>

 

  當函數被作為對象的方法調用時,可以使用 this 訪問自己所屬的對象,所以它能從對象中取值或對對象進行修改;

  this 到對象的指向發生在調用的時候,如果函數沒有被調用,則不會更改對象的變量;

  通過 this 可取得它們所屬對象的上下文的方法稱為公共方法;

        <script type="text/javascript">
            var o = {
                a: 1,
                m: function(){
                    return this;
                },
                n: function(){
                    this.a = 2; 
                    //當函數被作為對象的方法調用時,可以使用this訪問自己所屬的對象,所以它能從對象中取值或對對象進行修改。
                    //this到對象的綁定發生在調用的時候。所以這里的函數 n 如果沒有調用過,那么外部的 a 是不會被改變的
                }
            };
            console.log(o.m().a);//1
            o.n();
            console.log(o.m().a);//2
        </script>

  

  如果想訪問這個外部函數的 this 值,需要將 this 的值保存在一個變量里,內部函數就可以通過作用域鏈找到這個變量。

        <script type="text/javascript">
            var o = {
                m: function(){
                    console.log(this); //{} 當函數被作為對象的方法(對象的一個屬性)運行時,this指向該對象
                },
                n: function(){
                    console.log(this);//{}
                    
                    var that = this;  
                    //如果想訪問這個外部函數的this值,需要將this的值保存在一個變量里,內部函數就可以通過作用域鏈找到這個變量。
                    
                    function test(){
                        console.log(this);//window
                        console.log(that);//{}
                    }
                    return test(); //函數test獨立調用(不論這個函數在哪調用),this默認指向到window
                }
            };
            o.m();
            o.n();
        </script>

 

構造函數調用

  如果一個函數用 new 關鍵字調用,那么這個函數就是構造函數,並且背地里會創建一個連接到該函數的 prototype 的新對象,this 指向這個新對象;

  如果構造函數沒有形參,實例化的時候是可以不帶()的;如  var  a = Func; 或者  var a = Func(); 兩種都可以;

  同時我們在構造函數的時候有個約定(不是規范),首字母大寫,以避免忘了寫new關鍵字或者在普通函數前面加new;

 

  new 關鍵字的作用就是執行一個構造函數,並返回一個對象實例。使用 new 命令,它后面的函數的函數調用和普通函數調用就不一樣了,步驟如下:

  1. 創建一個空對象,作為將要返回的對象實例;
  2. 將空對象的原型 _proto_ 指向構造函數的 prototype 屬性;
  3. 將構造函數內部的this關鍵字指向空對象;
  4. 執行構造函數內部的代碼;

  就是說 this 指向這個新對象,構造函數內所有針對 this 的操作,都會發生在這個新對象上;

        <script type="text/javascript">
            // 創建一個名為Person 的構造函數,它構造一個帶有user 和age 的對象
            var Person = function (user,age) {
                this.user = user;
                this.age = age; 
            };
            
            // 構造一個Person 實例 ,並測試
            var shane = new Person ('shane',25);
            console.log(shane.user);//shane
            console.log(shane.age);//25
        </script>

 

  javascript 中構造函數是不需要有返回值的,可以認為構造函數和普通函數之間的區別就是:構造函數沒有 return 語句,普通函數可以有 return 語句;

  構造函數使用 this 關鍵字定義變量和方法,當 this 遇到 return 的時候,會發生指向不明(調用結果不明)的問題:

  1. return 返回的不是一個對象,this 還是指向實例(新對象),調用結果也還是新對象;
  2. return 返回的是一個對象,this 就指向這個返回的對象,調用結果就是這個返回的對象;
  3. return 返回的是 null,this 還是指向實例,調用結果也還是新對象;
<script type="text/javascript">
            var Person = function(){
                this.user = 'shane';
                return 
            }
            var shane = new Person;
            console.log(shane.user);//shane   return沒有返回值,this還是指向實例(新對象),調用結果也還是新對象;
            
            var Color = function(){
                this.red = 'red';
                return 'hello world';
            }
            var redColor = new Color;
            console.log(redColor.red);//red   return返回的是一個基本類型的字符串(原始值),this還是指向實例(新對象),調用結果也還是新對象;
            
            var Size = function(){
                this.size = 'big';
                return {};
            }
            var sizeBig = new Size;
            console.log(sizeBig.size);//undefined   return返回的是一個對象,this就指向這個返回的對象,調用結果就是這個返回的對象;
        </script>

 

間接調用

  通過 call()、apply()、bind() 方法把對象綁定到 this 上,叫做顯式綁定。對於被調用的函數來說,叫做間接調用

  1. call、apply、bind三者的第一個參數都是this要指向的對象,
  2. bind 只是返回函數,還未調用,所以如果要執行還得在后面加個();call、apply 是立即執行函數;
  3. 三者后面都可以帶參數,call 后面的參數用逗號隔開,apply 后面的參數以數組的形式傳入;bind則可以在指定對象的時候傳參,和 call 一樣,以逗號隔開,也可以在執行的時候傳參,寫到后面的括號中;func.call(obj,value1,value2);  func.apply(obj,[value1,value2]); func.bind(obj,value1,value2)();  func.bind(obj)(value1,value2);
        <script type="text/javascript">
            var add = function (a,b) {
                return a+b;
            }; 
            
            var arr = [2,3];
            
            var sum0 = add.apply(null, arr);//apply后面的參數以數組的形式傳入
            var sum1 = add.call(null,arr[0],arr[1]);//call后面的參數用逗號隔開
            var sum2 = add.bind(null,arr[0],arr[1]);//bind后面的參數可以和call一樣,用逗號隔開
            var sum3 = add.bind(null); //bind調用的時候也可以在執行的時候傳參
            
            console.log(sum0);//5
            console.log(sum1);//5
            console.log(sum2);//function (a,b) {return a+b;};    bind只是返回函數,還未調用,
            console.log(sum2());//5   所以bind調用的時候,如果要執行還得在后面加個()
            console.log(sum3(arr[0],arr[1]));//5   bind調用的時候也可以在執行的時候傳參
        </script>

 

嚴格模式下

  1. 在嚴格模式下,未指定環境對象而調用函數,則 this 值不會轉型為 window。 除非明確把函數添加到某個對象或者調用 apply() 或 call(),否則 this 值將是 undefined;
  2. 所以我們可以手動添加 window.函數(),將 this 指向 window;
        <script type="text/javascript"> 'use strict' //嚴格模式 function a(){ console.log (this); } a();//undefined //在嚴格模式下,未指定環境對象而調用函數,則 this 值不會轉型為 window , this 值將是 undefined。 //除非明確把函數添加到某個對象或者調用 apply()或 call()使用對象冒充。  window.a();//window //所以我們可以手動添加 window.函數(),來改變this指向window  (function(){ console.log(this);//undefined  })(); setTimeout(function(){ console.log(this);//Window  },0); //setTimeout是window的一個方法(屬性),所以這里可以寫成window.setTimeout
       //所以不管是嚴格模式還是非嚴格模式,setTimeout里的this始終指向window
       //但是有時候我們會發現setTimeout中的this也有其他情況,這只是誤導,當然如果你不確定的話,直接console.log(this)一下,分分鍾解決 </script>

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM