函數(shù)是JavaScript世界里的第一公民,換句話來說,就是我們?nèi)绻梢跃↗avaScript函數(shù)的使用,那么對JavaScript的運用可以更游刃有余了。熟悉JavaScript的人應該都知道,同樣的函數(shù),以不同的方式調(diào)用的話,受影響最大的應該是  this 。下面我們來說說JavaScript函數(shù)的各種調(diào)用模式。

一、普通函數(shù)的調(diào)用模式

  所謂普通函數(shù)的調(diào)用模式,也是JavaScript函數(shù)的最簡單的一種調(diào)用模式,直接就是函數(shù)名后接一個  ()  實現(xiàn)調(diào)用,看下面代碼:

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

  上面代碼,我們用function關(guān)鍵字聲明了一個 func 函數(shù),并且在函數(shù)體內(nèi)打印 this===window,然后我們直接調(diào)用函數(shù)func,我們可以看到控制臺是直接打印出 true ,也就是說,函數(shù)的這種普通調(diào)用模式,函數(shù)體內(nèi)的  this  是指向全局環(huán)境 window 的。不清楚這點的同學,可以能會遇到這樣的一個bug:

iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓

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

iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓

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

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

iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓

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

iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓

  在上面的代碼中,因為  show  里面的   this  指向   obj   的,所以我們在  show  里面聲明一個變量  that = this;用來把指向  obj  的  this  引入到  func1 中,然后再把 func1 函數(shù)體內(nèi)的  this.color  改為  that.color ,此時在控制臺打印出來的就是我們想要的 'red' 了。

  可能現(xiàn)在又有同學會問:為什么   show   里面的  this  是指向  obj 的呢?這就是我們要說的JavaScript函數(shù)的第二種調(diào)用模式:方法調(diào)用模式

二、方法調(diào)用模式

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

三、構(gòu)造器調(diào)用模式

  博主認為構(gòu)造器調(diào)用模式是相對于其他模式來說較為復雜點的調(diào)用模式了。通過關(guān)鍵字  new  可以把一個函數(shù)作為構(gòu)造器來調(diào)用。關(guān)鍵字  new  可以改變函數(shù)的返回值:

iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓

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

iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓

  在上示代碼中我們聲明了一個函數(shù) func2 ,分別用兩種不同的調(diào)用模式去調(diào)用它。因為函數(shù)  func2  并沒有顯式返回值,所以作為普通函數(shù)去調(diào)用時,它什么也沒有返回,所以  foo  的值是  undefined  。因為普通調(diào)用模式的   this   是指向 全局環(huán)境   window  的,所以  func2('afei');  后,全局環(huán)境下就多了一個  name 變量且等于 'afei'。

  func2  作為構(gòu)造器調(diào)用時,我們可以看到,它返回的是一個對象,因為關(guān)鍵字  new  使得函數(shù)在調(diào)用是發(fā)生了如下的特殊變化:

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

  2.   把函數(shù)里的  this  指向了這個新對象

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

  這樣子我們就可以看出構(gòu)造器的作用:通過函數(shù)的調(diào)用來初始化新創(chuàng)建出來的對象。在JavaScript的面向?qū)ο缶幊汤锩?,這個可是相當重要的。

  因為在函數(shù)的聲明上,在未來作為構(gòu)造器調(diào)用的函數(shù)和普通函數(shù)的聲明沒什么區(qū)別,所以導致后來的開發(fā)者很容易因為調(diào)用模式的錯誤導致程序出問題。所以開發(fā)者們都默契地約定,用來做構(gòu)造器調(diào)用的函數(shù)的函數(shù)名的第一個字符應該大寫,例如:Person,People。這樣子后來的開發(fā)者一看到函數(shù)名就知道要用構(gòu)造器調(diào)用模式調(diào)用此函數(shù)了。

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

  這種調(diào)用的模式是為了更靈活控制函數(shù)運行的上下文環(huán)境而誕生的。簡單的說就是為了靈活控制函數(shù)體內(nèi)  this  的值。

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

  apply方法的第二個參數(shù)是一個數(shù)組,數(shù)組里面的值將作為函數(shù)調(diào)用的參數(shù);

  call方法,從第二個參數(shù)起(包括第二個參數(shù)),剩下的參數(shù)都是作為函數(shù)調(diào)用的參數(shù);

  讓我們看看栗子:

iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓

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

iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓

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

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

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

五、總結(jié)

  在JavaScript里面,函數(shù)只要的調(diào)用模式就是這幾種了(在ES6里面還有一種很奇怪很特殊的函數(shù)調(diào)用模式,叫做’標簽模板‘,在這里博主也不多說了,有空另更),只要掌握了這幾種主要的調(diào)用模式,那么日后再也不用擔心  this 的值變來變?nèi)チ恕?/p>

  上文如果有漏的、有錯誤的地方,望各位小伙伴指出,小弟虛心向?qū)W。

ps:轉(zhuǎn)載請標明出處  http://www.cnblogs.com/afeihome/

http://www.cnblogs.com/afeihome/p/7111830.html