JavaScript函數的各種調用模式

函數是JavaScript世界裏的第一公民,換句話來說,就是我們如果可以精通JavaScript函數的使用,那麼對JavaScript的運用可以更遊刃有餘了。熟悉JavaScript的人應該都知道,同樣的函數,以不同的方式調用的話,受影響最大的應該是  this 。下面我們來說說JavaScript函數的各種調用模式。

一、普通函數的調用模式

  所謂普通函數的調用模式,也是JavaScript函數的最簡單的一種調用模式,直接就是函數名後接一個  ()  實現調用,看下面代碼:

function func(){
    console.log(this === window);  //true
}
func();

  上面代碼,我們用function關鍵字聲明瞭一個 func 函數,並且在函數體內打印 this===window,然後我們直接調用函數func,我們可以看到控制檯是直接打印出 true ,也就是說,函數的這種普通調用模式,函數體內的  this  是指向全局環境 window 的。不清楚這點的同學,可以能會遇到這樣的一個bug:

複製代碼

var color = 'gg';var obj = {
    color : 'red',
    show : function(){        function func1(){
            console.log(this.color);  //gg
        }
        func1();
    }
}
obj.show();

複製代碼

  我們在全局環境下聲明瞭一個變量 color 和一個對象 obj ,在對象 obj 裏面我們還聲明瞭一個 color 屬性 爲 'red',一個 show 方法。而且在 show 方法裏面呢,我們還聲明瞭一個函數 func1 並且調用了 func1,func1 的作用是打印 this.color。最後我們運行代碼  obj.show();   調用obj裏面的show方法。不清楚函數的普通調用模式的特點的同學可能會認爲此時在控制檯答應出來的會是  'red' 。實際上此時在控制檯答應出來的應該是  gg  。因爲函數  func1  的調用模式是 普通函數調用模式(即使它是在  obj  的  show  方法裏面調用的),所以此時函數體內的  this  是指向 全局環境window 的,所以就打印了全局環境下的變量  color 。

  可能有些同學會問:如果我們希望  func1  函數打印出來的是 'red' 呢,應該怎麼改?其實很簡單,因爲  obj.color  纔是 'red' ,所以我們只需要把  指向  obj  的  this 引入到函數 func1  裏面就行了: 

複製代碼

var color = 'gg';var obj = {
    color : 'red',
    show : function(){        var that = this;        function func1(){
            console.log(that.color);  //red
        }
        func1();
    }
}
obj.show();

複製代碼

  在上面的代碼中,因爲  show  裏面的   this  指向   obj   的,所以我們在  show  裏面聲明一個變量  that = this;用來把指向  obj  的  this  引入到  func1 中,然後再把 func1 函數體內的  this.color  改爲  that.color ,此時在控制檯打印出來的就是我們想要的 'red' 了。

  可能現在又有同學會問:爲什麼   show   裏面的  this  是指向  obj 的呢?這就是我們要說的JavaScript函數的第二種調用模式:方法調用模式

二、方法調用模式

  方法調用模式,簡單來說就是把一個 JavaScript函數作爲一個對象的方法來調用,當一個函數被保存爲一個對象的屬性是,我們就把它稱爲方法,例如上文的  obj  對象裏的  show  ,當一個方法被調用時,函數體裏面的   this  就會綁定到這個對象,例如上文的 show 裏面的  this  。方法調用模式也很容易辨別:obj.show(),對象名 . 屬性名 () ;代碼的話可以參考上文的  obj  代碼 ,博主就不多寫了。記住:方法的調用是可以在函數體內通過  this  訪問自己所屬的那個對象的。

三、構造器調用模式

  博主認爲構造器調用模式是相對於其他模式來說較爲複雜點的調用模式了。通過關鍵字  new  可以把一個函數作爲構造器來調用。關鍵字  new  可以改變函數的返回值:

複製代碼

.name = foo = func2('afei'name;    bar =  func2('lizefei'bar.name;

複製代碼

  在上示代碼中我們聲明瞭一個函數 func2 ,分別用兩種不同的調用模式去調用它。因爲函數  func2  並沒有顯式返回值,所以作爲普通函數去調用時,它什麼也沒有返回,所以  foo  的值是  undefined  。因爲普通調用模式的   this   是指向 全局環境   window  的,所以  func2('afei');  後,全局環境下就多了一個  name 變量且等於 'afei'。

  func2  作爲構造器調用時,我們可以看到,它返回的是一個對象,因爲關鍵字  new  使得函數在調用是發生瞭如下的特殊變化:

  1.   創建了一個新對象,而且這個新對象是鏈接到 func2  的  prototype  屬性的

  2.   把函數裏的  this  指向了這個新對象

  3.   如果沒有顯式的返回值,新對象作爲構造器func2的返回值進行返回(所以bar 是 {name:'lizefei'})

  這樣子我們就可以看出構造器的作用:通過函數的調用來初始化新創建出來的對象。在JavaScript的面向對象編程裏面,這個可是相當重要的。

  因爲在函數的聲明上,在未來作爲構造器調用的函數和普通函數的聲明沒什麼區別,所以導致後來的開發者很容易因爲調用模式的錯誤導致程序出問題。所以開發者們都默契地約定,用來做構造器調用的函數的函數名的第一個字符應該大寫,例如:Person,People。這樣子後來的開發者一看到函數名就知道要用構造器調用模式調用此函數了。

四、使用apply()和call()方法調用

  這種調用的模式是爲了更靈活控制函數運行的上下文環境而誕生的。簡單的說就是爲了靈活控制函數體內  this  的值。

  apply 和 call這兩個方法的第一個參數都是要傳遞被函數上下文的對象(簡單點說就是要綁定給函數  this  的對象)。其他參數就有所不同了:

  apply方法的第二個參數是一個數組,數組裏面的值將作爲函數調用的參數;

  call方法,從第二個參數起(包括第二個參數),剩下的參數都是作爲函數調用的參數;

  讓我們看看栗子:

複製代碼

var obj = {
    name :'afei'}function say(ag1,ag2){
    console.log(ag1+':'+ag2+" "+ this.name);
}
say.apply(obj,['apply方法','hello']); //apply方法:hello afeisay.call(obj,'call方法','hi'); //call方法:hi afei

複製代碼

  正如栗子所示,我們把對象 obj  作爲函數  say  的上下文來調用函數  say  ,所以函數裏的  this  是指向 對象  obj  的。在apply方法裏,我們通過數組  ['apply方法','hello']  給  say  方法傳遞了兩個參數('apply方法' 和 'hello'),所以打印出來是:  apply方法:hello afei。

  同理  call 也是一樣,而且函數傳遞的方式通過上面的代碼也一目瞭然我,博主就不多做解釋了。

  另外,博主還聽說apply和call這兩個方法除了傳遞參數的方式不一樣,執行的速度還是apply 比 call 要快呢。不過博主就沒有實驗過。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章