標籤:
函數是JavaScript 世界裡的第一公民,換句話來說,就是我們如果可以精通 JavaScript 函數的使用,那麼對JavaScript 的運用可以更遊刃有餘了。熟悉 JavaScript 的人應該都知道,同樣的函數,以不同的方式調用的話,受影響最大的應該是 this 。下面我們來說說 JavaScript 函數的各種調用模式,希望對大家 學習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 可以改變函數的傳回值: function func2( name){this.name = name;} name; //undefined// 普通函數調用模式 var foo = func2(’afei’);foo; //undefined name; //afei// 構造器調用模式 var bar = new func2(’lizefei’);bar.__proto__ === func2.prototype; //truebar; //{name:’lizefei’}bar. name; //’lizefei’在上示代碼中我們聲明了一個函數 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 要快呢。不過博主就沒有實驗過。 五、總結在JavaScript 裡面,函數只要的調用模式就是這幾種了(在 ES6 裡面還有一種很奇怪很特殊的函數調用模式,叫做 ’ 標籤模板 ‘ ,在這裡博主也不多說了,有空另更),只要掌握了這幾種主要的調用模式,那麼日後再也不用擔心 this 的值變來變去了。來源: 部落格園
JavaScript函數的調用模式有哪些?