首頁  >  文章  >  web前端  >  JavaScriptcall和apply

JavaScriptcall和apply

零到壹度
零到壹度原創
2018-03-24 17:23:472038瀏覽

要深入的去學習JavaScript語言,有一個很重要的知識點,就是對「call()」和「 apply()」的理解,有時候我們在看別人、或是一些開源框架的原始碼的時候,也是大量出現這兩個方法,那這兩個方法是乾嘛的,到底有什麼作用?本文主要內容就是對這兩種方法的深入探討。在講這兩個方法之前,我們還是有必要去回顧一下JavaScript中一個必須要掌握的知識點,那就是“this”,因為“call”和“apply 」與「this」有著密切的聯繫。

一、this是什麼

在物件導向中,比如說Java,this代表的是目前物件的參考。而在JavaScript中,this不是固定不變的,而是隨著它的執行環境的改變而改變。總結一句:this總是指向呼叫它所在方法的物件。 this的用法一般有以下幾種,我分別列出來:

1、this 在函數裡面

##在這種方式中,我也稱之為「全局性的函數呼叫」。程式碼如下:

JavaScriptcall和apply

圖1:this之全域性函數呼叫

#

透過結果可以看出來,函數test()內部的this指向的是全域對象,在本例中,全域物件是window。為了更能充分的證明本例中this就是window,把程式碼稍作調整如下:

JavaScriptcall和apply

圖2:this之全局性函數呼叫改進

透過結果可以更證明了全域的name在函數內部被修改了,因為這個函數內部的this指的就是window。

總結:對於全域性函數調用,函​​數內部的this就是指的全域物件window,也就是:this是調用函數所在的物件。實際上這個test()函數是由全域物件window來去呼叫的,那麼函數內部的this當然就指的是window。

2、this 在建構子裡

下面透過程式碼分析:

JavaScriptcall和apply

圖3:this之建構子呼叫

分析:我們透過new關鍵字建立一個物件的實例,可以發現new關鍵字改變了this的指向,將這個this指向了對象person。在建構子內部,我們將this.name=「HelloWorld」重新賦值,並沒有改變全域變數name的值。

總結:宣告一個建構函數的實例物件時,建構函式內部的this都會指向新的實例對象,或者說,建構函式內部的this指向的是新建立的物件本身。

3、在物件的方法中呼叫

下面透過程式碼分析:

JavaScriptcall和apply

圖4:this之物件方法的呼叫

總結:當person物件呼叫info()函數時,info函數內部的this指向的就是person物件。即,當this出現在物件的方法中時,那麼該方法內部的this指向的就是這個物件本身,也就是說this指向的呼叫函數的物件。

以上就是this出現位置的三種情況,另外還有一種位置就是今天要講的出現在call()和apply()方法中。

二、使用call和apply有什麼用?

1、問題引入

##結合上面的例子,再來回顧思考下在JavaScript中函數有幾種呼叫形式,看程式碼:

JavaScriptcall和apply

圖5:問題引入程式碼

解答:

第一種:直接呼叫。


Person(); 此種呼叫方式中,函數內部的this所指向的window


#

第二種:建構函數形式的呼叫

var person = new Person(); 此種方式呼叫中,函數內部的this指向的是person

總結:以上兩種方式,實際上可以這麼說,函數內部的this都是代表當前對象,只不過是JavaScript中函數內部的this會隨著程式而指向不同的對象。

那我的問題是:我們能不能手動修改this的指向呢?

答案:可以的,使用call或apply。這也就是call和apply的作用-->改變函數內部this的指向。

三、由程式碼引發的思考

#首先來看兩個程序,透過程式分析

程式一:

JavaScriptcall和apply

圖6:引發問題思考程式一

問題:我現在想藉用這個info方法來去實現對p1物件的列印,怎麼做?

方案一:直接呼叫info函數,即:info();透過上面的講解,仔細想想,肯定不行,因為這樣呼叫的話,info函數內部的this其實是指向的window 。

方案二:透過物件調用,即p1.info();其實也不行,因為p1物件壓根就沒有info這個方法,p1物件只有name和age屬性。

那麼程式一的問題該如何解決呢?往下看,程序二。

程式二:

JavaScriptcall和apply

圖7:引發問題思考程序二

透過圖示,可以發現我們透過向p1對象加入了一個show屬性,而這個show屬性的值其實是一個函數的位址,是一個函數對象,然後透過p1.show()就可以實現列印了。此種方法確實可以實現功能,但是這種方法是透過為p1物件添加屬性完成的,如果仍然有類似的需求,是否要為p1物件添加屬性來完成需求呢,這樣就會導致p1物件的佔用空間越來越大,所以方式並不優雅。

針對上面的問題,本質上就是想透過修改info函數內部的this指標的問題來完成目前物件的一個列印,那麼我們可以在不增加屬性的方式上來完成功能,這個就需要使用到了call和apply

#四、call和apply的使用

1、 功能:

使用指定的物件呼叫目前函數。

2、文法:

call([thisObj[,arg1[, arg2[, [,.argN]]] ]])

apply(thisObj[,argArray])

3、說明:

兩個方法的功能完全一樣,唯一差別就是參數。

對於第一個參數來說thisObj,作用是一樣的,用作代表當前對象的對象,說白了就表示的是函數執行時,this指向誰。

對於第二個參數,apply要求傳入的是一個參數數組,也就是說將一系列參數組成一個數組傳入,而對於call來說,散列的的參數值的方式傳入。例如,func(thisObj,arg1,arg2,arg3...)對應的apply用法就是func(thisObj,[arg1,arg2,arg3...])。本文以call方法為例。

這兩個方法都是Function物件中的方法,因為我們定義的每個物件都擁有該方法。

call 方法可以用來取代另一個物件呼叫一個方法。 call 方法可將一個函數的物件上下文從初始的上下文變更為由 thisObj 指定的新對象,如果沒有提供 thisObj 參數,那麼 Global 物件被用作 thisObj。

4、使用call和apply解決上面程式碼的問題

#程式碼如下:

JavaScriptcall和apply

##圖8:使用call方式解決問題

上面的程式碼就解決了問題,分析:就是說,

當在函數中呼叫call方法時,函數內部的this會自動指向call方法中的第一個參數。 上面的例子中,當執行info.call(p1)時,info函數內部的this則會自動指向p1對象,所以當然就可以call這種方式來完成對p1對象的列印。

5、再來看一個複雜一點的例子

程式碼如下:

JavaScriptcall和apply

圖9:call方式深入探討程式

分析上面程式碼,為什麼會印出來“ HelloWorld小碼農”,cat物件明明壓根沒有info方法呀,這個答案最關鍵是Person.call(cat)這一行程式碼,仔細去想想究竟發生了什麼事情,當呼叫call方法時,函數Person內部的this其實已經自動的指向了cat對象,相當於就是給cat對象執行了下面的兩行程式碼:

JavaScriptcall和apply

##然後重寫了原來cat物件中的name屬性,把name由“小花貓”改成了“HelloWorld小碼農”,而且並獲得了一個新的info方法(可以這麼理解,相當於為cat物件添加了一個info屬性),所以cat物件當然可以呼叫info方法了,所以結果就是「HelloWorld小碼農」。 apply的使用和call的功能相同,使用方式也很類似,這裡就不舉例子了

以上是JavaScriptcall和apply的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn