lambda 表达式中的 this 与普通情况下的 this 指向
lambda 表達式中的 this 與普通情況下的 this 指向
- Java
- JavaScript
- this 綁定
- 總結與提醒
??很多編程語言都支持 lambda 表達式,不過對于不同編程語言,其 lambda 表達式中 this 指向差異很大,有些甚至相反。下面舉例如下。
Java
??對于 Java 語言,很普通情況下的 this 很好理解,這里不在詳述。值得一提的是 Java 中的內部類。
??Java 的內部類有:成員內部類、局部內部類、匿名內部類、靜態內部類。這里只講述匿名內部類,因為其它的內部類中的 this 指向都不易混淆,所以從略。
-
對于 Java 中的匿名內部類,編譯器會自動生成它的類名,而自動生成的類名為外部類類名$數字。而匿名內部類中的 this,將指向的是這個內部類對象。
-
對于 Java 中的 lambda 表達式中的 this,指向的是 lambda 表達式所在類的對象。也就是說,對一個類來說,lambda 表達式中的 this,與普通表達式中的 this,沒有任何區別。
JavaScript
??關于 JavaSript 中的 this 使用,一般分為普通表達式中的 this、匿名函數中的 this 和箭頭函數中的 this。
??注意:JavaScript 中的匿名函數與箭頭函數不是同一個東西。JavaScript 中的匿名函數又叫 lambda 函數,但從形式上,JavaScript 中的箭頭函數才像 Java 中的 lambda 表達式。例如。
/*** 常規函數*/ function showName() {console.log(this.name); }/*** 匿名函數(lambda 函數)*/ let showSex = function() {console.log(this.sex); }/*** 箭頭函數*/ let showAge = () => console.log(this.age);-
對于普通表達式中的 this 所處的位置,可以分為以下情況:
-
含 this 的表達式沒有位于任何函數中。在這種情況,此 this 指向對象 window(非嚴格模式下)或為 undefined(嚴格模式下)。
-
含 this 的表達式位于一個函數中。在這種情況,此 this 指向調用該函數的對象。
特別地,當 this 直接位于一個構造函數中(即此 this 外最近一層的函數為構造函數),此 this 指向該構造函數所創建的對象(即,視調用構造函數的對象為該構造函數所創建的對象)。
注意:不能武斷地通過判斷此含 this 的表達式所處的位置來斷定 this 的指向。即,不能認為此含 this 的表達式位于某個對象內,就認為該 this 指向那個對象。判斷 this 的指向還需要通過判斷含 this 的函數在哪里被調用。
例如(假設下面的代碼均位于非嚴格模式下):
window.color = 'red'; let obj = {color: 'blue' };function sayColor() {console.log(this.color); }/*** 沒有使用對象來調用 sayColor(),this 默認指向 window。*/ sayColor(); // redobj.sayColor = sayColor; /*** 使用對象來調用 sayColor(),this 指向對象 obj。*/ obj.sayColor(); // blue -
-
對于匿名函數中的 this,它與普通表達式中的 this 的指向規則是一樣的,因此此處不再詳述。
-
對于箭頭函數中的 this,其指向直接包含此箭頭函數定義的函數所屬的對象。
【注意】
-
是包含箭頭函數的函數,不是包含 this 的箭頭函數。
-
“直接”指的是包含箭頭函數的函數必須是箭頭函數外的最近一層的函數。
-
當箭頭函數沒有位于某一個函數中時,此處的“函數”理解為“上下文”。
不過,由于 JavaScript 中的函數的歸屬可以改變,因此此處也不能直接通過判斷此含 this 的表達式所處的位置來斷定 this 的指向,還是要看含該箭頭函數的函數被調用的位置。
例如,對于普通情況下:
window.color = 'red'; let obj = { color: 'blue' };/*** 原箭頭函數沒有位于某一個函數中,而位于全局*/ let sayColorGlobalArrowFun = () => console.log(this.color); /*** 原箭頭函數沒有位于某一個函數中,那就更沒有該函數顯式的所屬對象了,* 因此默認所屬對象為 window。于是 this 恒指向 window*/ sayColorGlobalArrowFun(); // redobj.sayColorGlobalArrowFun = sayColorGlobalArrowFun; /*** 原箭頭函數沒有位于某一個函數中,那就更沒有該函數顯式的所屬對象了,* 因此默認所屬對象為 window。于是 this 恒指向 window*/ obj.sayColorGlobalArrowFun(); // redfunction sayColorNorFun() { // NorFun:Normal Function/*** 原箭頭函數代碼位于函數 sayColorNorFun 中*/let sayColorLocalArrowFun = () => console.log(this.color); sayColorLocalArrowFun(); } /*** 原箭頭函數代碼位于函數 sayColorNorFun 中,* 而此處函數 sayColorNorFun 屬于對象 window。* 于是 this 指向 window*/ sayColorNorFun(); // redobj.sayColorNorFun = sayColorNorFun; /*** 原箭頭函數代碼位于函數 sayColorNorFun 中,* 而此處函數 sayColorNorFun 屬于對象 obj。* 于是 this 指向 obj*/ obj.sayColorNorFun(); // blue對于將包含箭頭函數的函數作為構造函數時:
function King() {/*** this 直接位于一個普通函數中。在本文后面的調用中,其指向 King 對象*/this.royaltyName = 'Henry';/*** this 直接位于一個箭頭函數中。在本文后面的調用中,其指向 King 對象* * 注意:不能看代碼看到這里就斷定此處的 this 恒指向 window,* 要看含函數 King() 被調用處附近的代碼。* 但通常情況下,可以猜測 this 恒指向 window*/setTimeout(() => console.log(this.royaltyName), 1000); }function Queen() {/*** this 直接位于一個普通函數中。在本文后面的調用中,其指向 Queen 對象*/this.royaltyName = 'Elizabeth';/*** this 直接位于一個匿名函數中*/setTimeout(function() { console.log(this.royaltyName); }, 1000); }/*** 此處將函數 King() 作為構造函數,因此 this 指向此處“new”創建的 King 對象,* 同時還調用了函數 King() */ new King(); // Henry/*** 此處將函數 Queen() 作為構造函數,但由于 this 位于一個匿名函數中,* 而此匿名函數將由函數 setTimeout 來調用,* 而函數 setTimeout 將不會使用顯式的對象來調用此匿名函數,因此視調用方為 window* * 注意:此處并不是因為“new Queen()”位于全局,所以認為調用方為 window,* 而是因為直接包含此箭頭函數代碼的是一個匿名函數,而此匿名函數的調用方為 window*/ new Queen(); // undefined -
this 綁定
??既然 JavaScript 中普通函數與匿名函數中的 this 如此危險,有什么辦法可以回避這個風險呢?一種方法是改用箭頭函數,如果箭頭函數所處的上下文是確定的對象,那么它的 this 指向就不會改變。
??但有些時候,需要這個 this 不事先確定,這有點像面向對象語言中基類方法的 this 一樣。在 JavaScript 中,有時為了減少代碼冗余,會將一些共同代碼置入一個模塊中,供其它模塊使用,這個時候,希望 this 指向調用模塊的上下文。不過考慮函數傳遞調用(一個函數調用另一個函數,而后者再調用另一個函數),這個時候判斷 this 的指向會變得很麻煩。為此,JavaScript 提供了函數 bind 來終結這個判斷難題。函數 bind 可以間接強制規定一個函數中恒定的 this 指向。
??在 JavaScript 中,每個函數都是一個對象,每個函數對象都有一個函數 bind,它接收一個對象參數,然后返回一個函數。返回的這個函數與自己幾乎一樣,區別是,這個函數的 this 被強制綁定為傳入的這個對象參數。示例如下:
window.color = 'red';var obj = {color: 'blue' };function sayColor0() {console.log(this.color); }let sayColor = sayColor0().bind(obj);sayColor0(); // red sayColor(); // blue總結與提醒
-
當兩個 this 分別位于一個普通函數和一個箭頭函數中時,它們的區別是,前者的 this 指向該普通函數(在被調用時)所屬的對象(調用該普通函數的對象),而后者的 this 指向包含箭頭函數定義的上下文(在被調用時)所屬的對象。
-
如果把箭頭函數放置在一個函數中,則此箭頭函數中的 this 與普通函數的 this 的效果一樣的。因此,一般來說,不要把含 this 的箭頭函數放置在一個函數中,而應該直接暴露在最外層的上下文中。這樣,此箭頭函數就永遠指向當時的上下文,而不管之后又將此箭頭函數傳遞給了誰。
-
之所以 this 在普通函數與箭頭函數中的行為有所不同,是因為 JavaScript 中的普通函數、匿名函數(lambda 函數)都屬于函數,但箭頭函數不屬于函數,它只是一種特殊的表達式。而在 JavaScript 中,一個函數在被調用時,會為其創建 this 與 arguments,因為箭頭函數不屬于函數,所以它自己沒有 this,它的 this 只能來源于外部的 this——也就是包含箭頭函數定義的上下文。
總結
以上是生活随笔為你收集整理的lambda 表达式中的 this 与普通情况下的 this 指向的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Bean 在 Spring 中代表什么含
- 下一篇: 计算机领域中,增量是什么意思?