JavaScript 你真的了解this指向嗎


前言

  終於開始寫this指向了,相信這對很多JavaScript的學習者來說是一個非常恐怖的環節,個人認為也算是JavaScript中最難理解的一個知識點,this非常的方便但是在你不熟悉它的情況下可能會出現很多坑。

  本篇文章將帶你充分了解this指向,用最精煉簡短的語句闡述不同情況下的this指向。

詳解this指向

window對象

  window是一個全局的對象,里面存了很多方法。

  當我們使用var進行變量命名時,變量名會存入到window對象中,以及當我們使用標准函數定義方法時函數名也會存入window對象中。

<script>var username = "雲崖";

        function show(){
                console.log("show...");
        };

        console.log(window.username);  // 雲崖
        window.show();  // show...
</script>

全局環境

  在全局環境中,this的指向就是window對象。

  但是我們一般不這么用。

<script>var username = "雲崖";

        function show(){
                console.log("show...");
        };

        console.log(this.username);  // 雲崖
        this.show();  // show...
// 依舊可以執行,代表全局環境下this就是window對象
        // 如果你不相信,可以打印它看看。

        console.log(this);  //  Window {parent: Window, opener: null, top: Window, length: 0, frames: Window, …}
</script>

普通函數

  非嚴格模式下,普通函數中this的指向為window對象。

<script>// "use strict"; // 在非嚴格模式下,普通函數中this的指向是window對象
function show() {

                console.log("show...");
                console.log(this);  // window

        };

        show()

</script>

  但是在嚴格模式下,普通函數中this指向為undefined

<script>"use strict"; // 在嚴格模式下,普通函數中this指向為undefined
function show() {

                console.log("show...");
                console.log(this);  // undefined

        };

        show()

</script>

構造函數

  當一個函數能被new時,該函數被稱之為構造函數。

  一般構造函數中包含屬性與方法,函數中的上下文指向到實例對象。

  在構造函數中的this一般指向為當前對象。對於其方法而言this指向同樣為當前對象。

  你可以這么認為,在用Function定義類時,類中的方法指向當前類。

  這樣是不是好理解多了?

<script>"use strict"; 

        function User(username) {  // 在沒有class語法出現之前,這種構造函數我們通常會將它當做類來看待。
this.username = username;  // 可以稱之為類屬性

                console.log(this);  // User {username: "雲崖"}  代表指向當前對象
this.show = function () {  // 可以稱之為類方法

                        console.log(this.username);  // 雲崖
                        console.log(this);  // {username: "雲崖", show: ƒ}  代表指向當前對象
                }

        }

        let user = new User("雲崖");
        user.show();

</script>

對象字面量

  在對象中的this指向即為當前對象,同樣的在對象中的函數(方法)this指向也是當前對象本身。

  這與構造函數如出一轍。

<script>"use strict"; 

        let obj = {
                username:"雲崖",  // 最終的key都會轉為String類型,但是Symbol類型不會轉換。

                show:function(){ // 這里也可以將show稱作為方法,而username即為屬性

                        console.log(this.username); // 雲崖
                        console.log(this);  // {username: "雲崖", show: ƒ}

                },
        }

        obj.show();

</script>

方法中的普通函數

  首先聊方法中的普通函數之前,要先知道什么情況下的函數常被稱之為方法。

  結合本章前面介紹的內容,以下環境中的函數將被稱之為方法:

  在構造函數中的函數可以將其稱之為方法

  在對象中的字面量函數也可以將其稱之為方法

  那么,在方法中的普通函數即是這樣的:

  在構造函數中的函數中的函數可以稱其為方法中的普通函數

  在對象中的字面量函數中的函數也可以將其稱為方法中的普通函數

  有點繞哈,看代碼你就懂了。

  在方法中的普通函數的this指向非嚴格模式下為window對象,嚴格模式下為undefined

  值得一提的是,對於大多數開發者而言,這么嵌套的情況很少使用。

<script>"use strict";

        function User(username) { 

                this.username = username; 

                this.show = function () {  // 方法

                        console.log(this.username);  // 雲崖
                        console.log(this);  // {username: "雲崖", show: ƒ}  代表指向當前對象
function inner() {  // 普通函數
                                console.log(this);  // 嚴格模式:undefined,非嚴格模式:window
                        };

                        inner();  // 在方法中定義一個函數並調用

                }

        }

        let user = new User("雲崖");
        user.show();

</script>
<script>"use strict"; 

        let obj = {
                username:"雲崖",  // 最終的key都會轉為String類型,但是Symbol類型不會轉換。

                show:function(){  // 方法

                        console.log(this.username); // 雲崖
                        console.log(this);  // {username: "雲崖", show: ƒ}
function inner(){  // 普通函數
                                console.log(this);  // 嚴格模式:undefined,非嚴格模式:window
                        };

                        inner();  // 在方法中定義一個函數並調用

                },
        }

        obj.show();

</script>

方法中的普通函數改變this指向

  那么,怎么改變方法中普通函數的this指向呢?

  非常簡單。使用一個常量將方法的this賦值並傳遞給其中的普通即可。

<script>"use strict";

        function User(username) { 

                this.username = username; 

                this.show = function () {  // 方法

                        let self = this;  // 這個this指向的當前對象,即User
function inner(self) {  // 普通函數
                                console.log(self);  // 在方法內的普通函數中使用self即可
                        };

                        inner(self);  // 我們將self傳遞進去

                }

        }

        let user = new User("雲崖");
        user.show();

</script>
<script>"use strict";

        let obj = {
                username: "雲崖",  // 最終的key都會轉為String類型,但是Symbol類型不會轉換。

                show: function () {  // 方法

                        let self = this;  // 這個this指向的當前對象,即obj
function inner(self) {  // 普通函數
                                console.log(self);  //  在方法內的普通函數中使用self即可
                        };

                        inner(self);  // 在方法中定義一個函數並調用

                },
        }

        obj.show();

</script>

方法中的箭頭函數

  箭頭函數這玩意兒沒有this指向,你可以理解為它始終會與外層定義自己的函數共同使用一個this,在大多數情況下是會如此,但是少部分情況會除外,比如在事件的回調函數中,這個在下面會有舉例。

  在方法中的箭頭函數this指向始終會與定義自己的函數共同使用一個this

<script>"use strict";

        function User(username) {

                this.username = username;

                this.show = function () {  // 方法

                        console.log(this);  // 指向 User

                        let inner = () =>   console.log(this);  // 箭頭函數,與定義自己的外層指向同一this,即User

                        inner();  // 在方法中定義一個函數並調用

                }

        }

        let user = new User("雲崖");
        user.show();

</script>

事件普通函數

  事件函數是指某一動作方式后所調用的回調函數,如果是普通函數那么this指向即為事件源本身。

<script>"use strict";

        let div = document.querySelector("div");

        div.onclick = function (event) { 

                console.log(event);  // 事件

                console.log(this);  // div標簽,即為事件源本身,DOM對象
         }

</script>

事件箭頭函數

  事件的回調函數如果箭頭函數,那么this指向即為window,一句話,向上找,看在哪個環境下定義了這個箭頭函數。

  所以我們盡量不要去用箭頭函數作為事件的回調函數。

<script>"use strict";

        let div = document.querySelector("div");

        // 由於是在全局定義的,所以此時的this即為window,如果是在方法中定義的事件箭頭函數則this指向
        // 就不是window了

        div.onclick = event => { 

                console.log(event);  // 事件

                console.log(this);  // Window 
         }

</script>

事件箭頭函數獲取事件源

  如果想在事件箭頭函數中獲取事件源,那就不使用window了。用event參數中的一個叫target的屬性即可找到事件源。

<script>"use strict";

        let div = document.querySelector("div");

        div.onclick = event => { 

                console.log(event.target);  // 事件源DOM對象

                console.log(this);  // Window 
         }

</script>

改變this指向

call方法

  通過call()方法,讓原本函數的this發生改變,如下實例我們可以在user函數中使用this去給對象obj進行添加屬性。

  參數1:新的this指向對象

  其他參數:函數中本來的傳遞值

  特點:立即執行

<script>"use strict";

        function user(name,age) {
                this.name = name; 
                this.age = age;
                console.log(this);
        };

        let obj = {};

        user.call(obj,"雲崖",18);

        console.log(obj.name); // 雲崖
        console.log(obj.age); // 18
</script>

apply方法

  call()方法唯一不同的地方在於參數傳遞,其他都一樣。

  參數1:新的this指向對象

  參數2:函數中本來的傳遞值,請使用數組進行傳遞。

  特點:立即執行

<script>"use strict";

        function user(name,age) {
                this.name = name; 
                this.age = age;
                console.log(this);
        };

        let obj = {};

        user.apply(obj,["雲崖",18]);

        console.log(obj.name); // 雲崖
        console.log(obj.age); // 18
</script>

bind方法

  該方法最大的特點是具有復制特性而非立即執行,對於函數的參數傳遞可以有多種。

  但是我這里只介紹一種。

  參數傳遞:使用bind()方法時傳遞一個this指向即可

  特性:會返回一個新的函數。

<script>"use strict";

        function user(name,age) {
                this.name = name; 
                this.age = age;
                console.log(this);
        };

        let obj = {};

        // 可以這么理解,使用bind()方法的第一步,告訴this指向誰,第二步,復制出新函數,第三步,新函數的this已經改變。其他地方與原函數相同。
        let new_func = user.bind(obj); // obj傳遞進去,返回一個新函數,該新函數與user函數除了this指向不一樣其他均相同。
        new_func("雲崖",18)

        console.log(obj.name); // 雲崖
        console.log(obj.age); // 18
</script>

總結

  window對象 當前對象 上級this指向 事件源DOM對象 undefined
全局環境(不在函數中)        
普通函數 非嚴格模式:√       嚴格模式:√
構造函數        
對象        
構造函數中的方法        
對象中的字面量方法        
構造函數中的方法中的普通函數 非嚴格模式:√       嚴格模式:√
對象中的字面量方法中的普通函數 非嚴格模式:√       嚴格模式:√
構造函數中的方法中的箭頭函數      
對象中的字面量方法中的箭頭函數      
事件普通函數        
事件箭頭函數 大概率是√,主要看上層的this指向      


 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM