this關鍵字在JAVA和JS中的異同
this在JS中的用法
由於js中this 是在執行期進行繫結的,所以js中的 this 可以是全域性物件、當前物件或者任意物件,這完全取決於函式的呼叫方式。JavaScript中函式的呼叫有以下幾種方式怎麼區別:
1.this關鍵字在何處出現?
this他只能出現在函式中。當然在全域性作用域中是個例外,意思是this只可能在兩種情境下出現,一個是在函式體內,另一個是在全域性作用域。
2.this是什麼?
this是關鍵字,語言規範裡規定他指向函式執行時的當前物件。它代表函式執行時,自動生成的一個內部物件,只能在函式內部使用。
3.this到底指向哪?
首先明確this在JavaScript中和函式的執行環境而不是宣告環境相關。
第一條中已經說過,this只能出現在函式中和全域性作用域中,全域性作用域中this指向全域性物件(全域性物件在瀏覽器這個環境中指window)。
如果this出現在函式中,那就要分情況來看this到底指向哪了。指向的依據就是函式的執行環境而不是宣告環境。其實可以以一句話來概括就是this永遠指向所在函式的所有者,當沒有顯示的所有者的時候,那麼this指向全域性物件。
4.各種情況下的this的具體指向?
(1).全域性的作用域
1console.log(this)
window呼叫所有,this指向window
注:(js變數作用域可分為:"全域性變數"和"區域性變數"。"全域性變數":申明在函式之外的變數,"區域性變數":申明在函式體中的變數,並且只能在當前函式體內訪問,如:function(){var a = 0;}在申明變數是凡是沒有var關鍵字,而直接賦值的變數均為全域性變數)
(2).函式作為某個物件的成員方法呼叫
+ View Code在全域性作用域中宣告全域性變數name,在obj物件中宣告同時也宣告一個name。當用物件obj呼叫他的成員方法fn的時候,this指向這個obj,因此打印出來的this.name為obj的name。
所以函式作為某個物件的成員方法呼叫時this指向該物件。
(3).函式作為函式直接使用
View Code同樣的還是上邊的一段程式碼,只不過這次我們把fn這個函式直接執行,這時在函式執行的時候他沒有明確的當前物件,所以預設這時this就指向了全域性物件。
所以函式作為函式直接使用時this指向全域性物件。
(4).函式作為建構函式呼叫
var name = 'a'; function Obj () { this.name = 'b'; } Obj.prototype.getName = function () { console.log(this.name); } var myObj = new Obj(); myObj.getName();//b
函式作為建構函式呼叫時this指向用該建構函式構造出來的新物件。
(5).setTimeout(延遲)和setInterval(持續)以及匿名函式(函式呼叫模式就是指向window)
1 var name = "a"; 2 var obj = { 3 name: "b", 4 getName: function () { 5 setTimeout(function () { 6 console.log(this.name); 7 }, 1000); 8 }, 9 }; 10 11 obj.getName();//a
這兩個函式執行的時候第一個引數可以為一個匿名函式,也可以是一個宣告好的函式引用,所以this指向也就是指這個匿名函式或者函式引用的this的指向。通過第一條已經知道匿名函式或者在全域性作用域中宣告的函式直接執行的時候,其中的this指向全域性對 象,所以在這裡也一樣,setTimeout和setInterval兩者執行時,this指向全域性物件。
這個時候如果還想輸出的是b,也就是this指向obj的話就需要做點手腳了。
1 var name = "a"; 2 var obj = { 3 name: "b", 4 getName: function () { 5 var that = this; 6 setTimeout(function () { 7 console.log(that.name); 8 }, 1000); 9 }, 10 }; 11 12 obj.getName();//b
當然這樣是改變不了this指向的,但是可以通過把this賦值給that,就實現了儲存this指向的作用。
緊接著是匿名函式。
1(function() {2console.log(this);3})()//window
匿名函式中的this指向全域性物件,因為他沒有顯示的所有者。
(6)apply、call、bind
這三個函式是函式物件的一個方法,他們的作用就是為了改變函式執行時候的this指向,具體用法如下:
call:call(obj[,arg1][,arg2]);第一個引數為強制改變需要指向的物件,後邊可選的是該函式的引數,如果不傳obj的話預設為window。
apply:apply(obj[,arr]);第一個引數為強制改變需要指向的物件,後邊可選的是該引數集合的陣列形式,如果不傳obj的話預設為window。
apply和call的作用和呼叫形式基本一致,不同的是call後面的引數與方法中是一一對應的,而apply的第二個引數是一個數組,陣列中的元素是和方法中一一對應的,這就是兩者最大的區別。兩者都可以不傳引數,此時預設改變指向的物件為全域性物件。
bind:bind的呼叫形式和call相同,但是他返回的是改變呼叫物件後的函式引用,所以還要再執行一次,也就是obj.fun().bind()()。
總結:1.作為函式呼叫的時候,this指向全域性物件(window)。
2.作為物件的方法呼叫,this指向呼叫的物件(誰呼叫指向誰)。
3.作為建構函式被呼叫的this指向新的物件(指向new出來的物件)。
4.apply,call方法呼叫指向這些方法的第一個引數。
this在Java中的用法(結合例項)
this.屬性名
大部分時候,普通方法訪問其他方法、成員變數時無須使用 this 字首,但如果方法裡有個區域性變數和成員變數同名,但程式又需要在該方法裡訪問這個被覆蓋的成員變數,則必須使用 this 字首。
例 1
假設有一個教師類 Teacher 的定義如下:
- public class Teacher {
- private String name; // 教師名稱
- private double salary; // 工資
- private int age; // 年齡
- }
在上述程式碼中 name、salary 和 age 的作用域是 private,因此在類外部無法對它們的值進行設定。為了解決這個問題,可以為 Teacher 類新增一個構造方法,然後在構造方法中傳遞引數進行修改。程式碼如下:
- // 建立構造方法,為上面的3個屬性賦初始值
- public Teacher(String name,double salary,int age) {
- this.name = name; // 設定教師名稱
- this.salary = salary; // 設定教師工資
- this.age = age; // 設定教師年齡
- }
在 Teacher 類的構造方法中使用了 this 關鍵字對屬性 name、salary 和 age 賦值,this 表示當前物件。this.name=name
語句表示一個賦值語句,等號左邊的 this.name 是指當前物件具有的變數 name,等號右邊的 name 表示引數傳遞過來的數值。
建立一個 main() 方法對 Teacher 類進行測試,程式碼如下:
- public static void main(String[] args) {
- Teacher teacher = new Teacher("王剛",5000.0,45);
- System.out.println("教師資訊如下:");
- System.out.println("教師名稱:"+teacher.name+"\n教師工資:"+teacher.salary+"\n教師年齡:"+teacher.age);
- }
執行該程式,輸出的結果如下所示。
教師資訊如下: 教師名稱:王剛 教師工資:5000.0 教師年齡:45
提示:當一個類的屬性(成員變數)名與訪問該屬性的方法引數名相同時,則需要使用 this 關鍵字來訪問類中的屬性,以區分類的屬性和方法中的引數。
this.方法名
this 關鍵字最大的作用就是讓類中一個方法,訪問該類裡的另一個方法或例項變數。
例 2
假設定義了一個 Dog 類,這個 Dog 物件的 run( ) 方法需要呼叫它的 jump( ) 方法,Dog 類的程式碼如下所示:
- /**
- * 第一種定義Dog類方法
- **/
- public class Dog {
- // 定義一個jump()方法
- public void jump() {
- System.out.println("正在執行jump方法");
- }
- // 定義一個run()方法,run()方法需要藉助jump()方法
- public void run() {
- Dog d = new Dog();
- d.jump();
- System.out.println("正在執行 run 方法");
- }
- }
使用這種方式來定義這個 Dog 類,確實可以實現在 run( ) 方法中呼叫 jump( ) 方法。下面再提供一個程式來建立 Dog 物件,並呼叫該物件的 run( ) 方法。
- public class DogTest {
- public static void main(String[] args) {
- // 建立Dog物件
- Dog dog = new Dog();
- // 呼叫Dog物件的run()方法
- dog.run();
- }
- }
在上面的程式中,一共產生了兩個 Dog 物件,在 Dog 類的 run( ) 方法中,程式建立了一個 Dog 物件,並使用名為 d 的引用變數來指向該 Dog 物件。在 DogTest 的 main() 方法中,程式再次建立了一個 Dog 物件,並使用名為 dog 的引用變數來指向該 Dog 物件。
下面我們思考兩個問題。
1)在 run( ) 方法中呼叫 jump( ) 方法時是否一定需要一個 Dog 物件?
答案是肯定的,因為沒有使用 static 修飾的成員變數和方法都必須使用物件來呼叫。
2)是否一定需要重新建立一個 Dog 物件?
不一定,因為當程式呼叫 run( ) 方法時,一定會提供一個 Dog 物件,這樣就可以直接使用這個已經存在的 Dog 物件,而無須重新建立新的 Dog 物件了。因此需要在 run() 方法中獲得呼叫該方法的物件,通過 this 關鍵字就可以滿足這個要求。
this 可以代表任何物件,當 this 出現在某個方法體中時,它所代表的物件是不確定的,但它的型別是確定的,它所代表的只能是當前類的例項。只有當這個方法被呼叫時,它所代表的物件才被確定下來,誰在呼叫這個方法,this 就代表誰。
將前面的 Dog 類的 run( ) 方法改為如下形式會更加合適,run( ) 方法程式碼修改如下,其它程式碼不變。
- /**
- * 第二種定義Dog類方法
- **/
- // 定義一個run()方法,run()方法需要藉助jump()方法
- public void run() {
- // 使用this引用呼叫run()方法的物件
- this.jump();
- System.out.println("正在執行run方法");
- }
從第一種 Dog 類定義來看,在 Dog 物件的 run( ) 方法內重新建立了一個新的 Dog 物件,並呼叫它的 jump( ) 方法,這意味著一個 Dog 物件的 run( ) 方法需要依賴於另一個 Dog 物件的 jump( ) 方法,這不符合邏輯。
第二種 Dog 類定義是當一個 Dog 物件呼叫 run( ) 方法時,run( ) 方法需要依賴它自己的 jump( ) 方法,與第一種定義類的方法相比,更符合實際情形。
在現實世界裡,物件的一個方法依賴於另一個方法的情形很常見,例如,吃飯方法依賴於拿筷子方法,寫程式方法依賴於敲鍵盤方法。這種依賴都是同一個物件兩個方法之間的依賴。因此,Java 允許物件的一個成員直接呼叫另一個成員,可以省略 this 字首。也就是說,將上面的 run( ) 方法改為如下形式也完全正確。
- public void run() {
- jump();
- System.out.println("正在執行run方法");
- }
大部分時候,一個方法訪問該類中定義的其他方法、成員變數時加不加 this 字首的效果是完全一樣的。
注意:對於 static 修飾的方法而言,可以使用類來直接呼叫該方法,如果在 static 修飾的方法中使用 this 關鍵字,則這個關鍵字就無法指向合適的物件。所以,static 修飾的方法中不能使用 this 引用。並且 Java 語法規定,靜態成員不能直接訪問非靜態成員。
省略 this 字首只是一種假象,雖然程式設計師省略了呼叫 jump() 方法之前的 this,但實際上這個 this 依然是存在的。
this( )訪問構造方法
this( ) 用來訪問本類的構造方法(構造方法是類的一種特殊方法,方法名稱和類名相同,沒有返回值。詳細瞭解可參考《Java構造方法》一節),括號中可以有引數,如果有引數就是呼叫指定的有參構造方法。
例 3
下面定義一個 Student 類,使用 this( ) 呼叫構造方法給 name 賦值,Student 類的程式碼如下所示:
- public class Student {
- String name;
- // 無參構造方法(沒有引數的構造方法)
- public Student() {
- this("張三");
- }
- // 有參構造方法
- public Student(String name) {
- this.name = name;
- }
- // 輸出name和age
- public void print() {
- System.out.println("姓名:" + name);
- }
- public static void main(String[] args) {
- Student stu = new Student();
- stu.print();
- }
- }
輸出結果為:
姓名:張三
注意:
- this( ) 不能在普通方法中使用,只能寫在構造方法中。
- this關鍵字必須放在非靜態方法裡(原因:static方法就是沒有this的方法。在static方法內部不能呼叫非靜態方法,反過來是可以的。而且可以在沒有建立任何物件的前提下,僅僅通過類本身來呼叫static方法。這實際上正是static方法的主要用途。)
-
在構造方法中使用時,必須是第一條語句。