JS學習筆記之this指向

xuerensusu發表於2018-05-10

初學JS的朋友可能對this指向問題有些困惑。最近在看《Javascript設計模式與開發實踐》一書,裡面總結得很到位。梳理下大致可以分為4情況,有興趣的可以自行去翻書閱讀。

作為物件的方法呼叫
作為普通函式呼叫
構造器呼叫
Function.prototype.call和Function.prototype.apply呼叫

1、作為物件的方法呼叫

當函式作為物件的方法被呼叫時,此時this指向該物件。

var obj = {
     name: lq,
     getName: function () {
        console.log (this === obj); // true
        console.log (this.name); // lq
     }
 }
複製程式碼

2、作為普通函式呼叫

當函式不作為物件屬性被呼叫,而是通過普通的函式呼叫,此時的this總是指向全域性物件。在瀏覽器環境的JavaScript裡,就是window物件。

 window.name = 'lq';
 let getName = function () {
     return this.name;
 }
 console.log ( getName() ); // 輸出:lq 此時this指向window物件
複製程式碼

又比如:

 window.name = 'lq';
 let myObj = {
     name: 'xiaoming',
     getName: function () {
         return this.name; 
     }
 };
 
 let getName = myObj.getName;
 console.log ( getName() ); // 輸出:lq 
複製程式碼

有時候在我們點選div節點的事件函式內部,如果有一個callback回撥函式,當callback被當作普通函式呼叫時,callback內部的this指向window,但實際上我們想讓它指向被點選的div節點本身。程式碼如下:

 <html>
    <body>
        <div id='div1'>my is div node</div>
    </body>
    <script>
        window.id = 'window';
        document.querySelector('#div1').onclick = function () {
            console.log (this.id); // 輸出:div1 
            let callback = function () {
                console.log (this.id); // 輸出:window
            }
            callback();
        }
    </script>
 </html>    
    
複製程式碼

通常的解決方法是用一個變數來儲存div節點的引用:

 document.querySelector('#div1').onclick = function () {
     let _this = this;
     let callback = function () {
         console.log (_this.id); //輸出:div1
     }
     callback();
 }
複製程式碼

另外,在ESMAScript5下的strict嚴格模式下,這種普通函式呼叫已經被規定為不會指向全域性物件,而是undefined。

 function fun () {
     'use strict'
     console.log (this); //輸出了:undefined
 }
 fun();
複製程式碼

3、構造器呼叫

Javascript種沒有類的概念,但提供了new運算子,這樣我們可以從構造器中建立物件。除了宿主提供的一些內建函式,大部分的Javascript函式可以當作構造器使用。構造器跟普通函式沒什麼太大區別,唯一的區別是被呼叫的方式。

當用new運算子呼叫函式時,該函式會返回一個物件,一般情況下,構造器裡的this都指向返回的這個物件。如下:

 let myObj = function () {
     this.name = 'lq';
 };
 
 let obj = new myObj();
 console.log (obj.name); //輸出:lq
複製程式碼

如果new構造器顯式地返回一個object型別的物件,那麼運算的結果就會最終返回這個物件,this就不是我們之前所期望的this了。如下:

 let myObj = function () {
     this.name = 'lq';
     return {
        name: 'xiaoming';   
     }
 }
 let obj = new myObj();
 console.log(obj.name); // 輸出:xiaoming 
複製程式碼

如果構造器返回的不是object型別的物件,而是一個非物件型別的資料,就沒有上面的問題。

let myObj = function () {
    this.name = 'lq';
    return 'xiaoming';
};
let obj = new myObj();
console.log (obj.name); //輸出:lq
複製程式碼

4、Function.prototype.call和Function.prototype.apply呼叫

這兩種方式呼叫可以動態地改變傳入函式的this:

 let obj1 = {
     name: 'lq',
     getName: function () {
         return this.name;
     }
 };
 let obj2 = {
     name: 'xiaoming'
 };
 console.log (obj1.getName()); //輸出:lq
 console.log (obj1.getName.call(obj2)); // 輸出:xiaoming
 console.log (obj1.getName.apply(obj2)); //輸出:xiaoming
 
複製程式碼

相關文章