javaScript系列 [06]-javaScript和this

javaScript系列 [01]-javaScript函數(shù)基礎(chǔ)這篇文章中我已經(jīng)簡單介紹了JavaScript語言在函數(shù)使用中this的指向問題昙楚,雖然篇幅不長刽肠,但其實最重要的部分已經(jīng)講清楚了纯命,這篇文章我們來單獨談一談神秘的this,或者叫怎么也搞不清楚的指天指地指空氣的this损俭。

1.1 this簡單說明

this關(guān)鍵字被認為是JavaScript語言中最復(fù)雜的機制之一蛙奖,跟this相關(guān)的知識很多開發(fā)者往往總是一知半解,更有甚者很多人完全搞不懂也不愿意去搞懂跟this相關(guān)的內(nèi)容杆兵,在必須要用到的時候?qū)幵高x擇在代碼中總是使用臨時打印驗證的方式來探知this的指向雁仲。這是現(xiàn)實,也許因為他們覺得跟this有關(guān)的這一切都混亂不堪琐脏,各種文檔晦澀難懂攒砖,this的指向好似沒有固定的套路,總是變來變?nèi)ルy以捉摸日裙。其實吹艇,this原本并沒有那么復(fù)雜,它就是個被自動定義在函數(shù)作用域中的變量昂拂,總是指向某個特定的“對象”受神。接下來,我們將嘗試用這樣一篇文章來講清楚跟this有關(guān)的以下問題:

? this 是什么格侯?
? 為什么要使用this?
? this指向誰鼻听?
? this綁定的幾種情況
? this固定規(guī)則外的注意事項

? ?this是什么?? ?

在聲明函數(shù)的時候,除了聲明時定義的形式參數(shù)外联四,每個函數(shù)還接受兩個附加的參數(shù):thisarguments精算。其中arguments是一個類似于數(shù)組的結(jié)構(gòu),保存了函數(shù)調(diào)用時傳遞的所有實際參數(shù)碎连,arguments這個參數(shù)讓我們有能力編寫能夠接受任意個數(shù)參數(shù)的函數(shù)。參數(shù)this在面向?qū)ο缶幊讨蟹浅V匾月模偸侵赶蛞粋€“特定的對象”鱼辙,至于這個特定的對象是誰通常取決于函數(shù)的調(diào)用模式。

<script>
    console.log(this);  //默認指向window

    function sum() {
        var res = 0;
        for (var i = 0; i < arguments.length; i++) {
           res += arguments[i];
        }
        console.log(this);
        return res;
    }

    //調(diào)用sum函數(shù)的時候玫镐,this默認指向window
    console.log(sum(1, 2, 3, 4)); //計算輸入?yún)?shù)的累加和倒戏,結(jié)果為10
</script>

現(xiàn)在我們知道和this有關(guān)的關(guān)鍵信息是:

① this是JavaScript中所有函數(shù)的隱藏參數(shù)之一,因此每個函數(shù)中都能訪問this恐似。
② 函數(shù)中的this總是指向一個特定對象杜跷,該對象具體取決于函數(shù)的調(diào)用模式。

說明:在script標簽中我們也可以直接訪問this,它通掣鹈疲總是指向widow憋槐,我們討論的this主要特指函數(shù)內(nèi)部(函數(shù)體)的this。

? ?為什么要使用this?? ?

this提供一種更優(yōu)雅的方式來隱士的傳遞一個對象引用淑趾,因為擁有this阳仔,所以我們可以把API設(shè)計得更加的簡潔并且易于復(fù)用。簡單點說扣泊,那就是this可以幫助我們省略參數(shù)近范。

我們可以通過以下兩個代碼片段來加深對this使用的理解。

    /**代碼 [ 01 ]**/
    var personOne = {name:"文頂頂",contentText:"天王蓋地虎 小雞燉蘑菇"};
    var personTwo = {name:"燕赤霞",contentText:"天地?zé)o極 乾坤借法 急急如令令"};

    function speak(obj) {
        console.log(obj.name+"口訣是:" + getContentText(obj));;
    }

    function getContentText(obj) {
        return obj.contentText + "噠噠噠噠~";
    }

    speak(personOne);  //文頂頂口訣是:天王蓋地虎 小雞燉蘑菇噠噠噠噠~
    speak(personTwo);  //燕赤霞口訣是:天地?zé)o極 乾坤借法 急急如令令噠噠噠噠~

    getContentText(personOne);
    getContentText(personTwo);

代碼說明:上面的代碼聲明了兩個函數(shù):speak和getContentText延蟹,這兩個函數(shù)都需要訪問對象中的屬性评矩,上面的代碼中每個函數(shù)都接收一個obj對象作為參數(shù)。

    /**代碼 [ 02 ]**/
    var personOne = {name:"文頂頂",contentText:"天王蓋地虎 小雞燉蘑菇"};
    var personTwo = {name:"燕赤霞",contentText:"天地?zé)o極 乾坤借法 急急如令令"};

    function speak() {
           console.log(this.name+"口訣是:" + getContentText.call(this));;
    }

    function getContentText() {
        return this.contentText + "噠噠噠噠~";
    }

    speak.call(personOne);  //文頂頂口訣是:天王蓋地虎 小雞燉蘑菇噠噠噠噠~
    speak.call(personTwo);  //燕赤霞口訣是:天地?zé)o極 乾坤借法 急急如令令噠噠噠噠~

    getContentText.call(personOne);  //天王蓋地虎 小雞燉蘑菇噠噠噠噠~
    getContentText.call(personTwo);  //天地?zé)o極 乾坤借法 急急如令令噠噠噠噠~

代碼說明:完成相同的功能阱飘,還是兩個同樣的函數(shù)斥杜,區(qū)別在于我們借助this省略掉了函數(shù)必須要傳遞的對象參數(shù),實現(xiàn)更優(yōu)雅俯萌。而且如果你的代碼越來越復(fù)雜果录,那么需要顯式傳遞的上下文對象會讓代碼變得越來越混亂而難以維護,使用this則不會如此咐熙。

1.2 函數(shù)和this

this指向誰綁定給哪個對象并不是在編寫代碼的時候決定的弱恒,而是在運行時進行綁定的,它的上下文取決于函數(shù)調(diào)用時的各種條件 棋恼。this的綁定和函數(shù)聲明的位置沒有任何關(guān)系返弹,只取決于函數(shù)的調(diào)用方式

當函數(shù)被調(diào)用時爪飘,會創(chuàng)建一個執(zhí)行上下文义起。該上下文會包含一些特殊的信息,例如函數(shù)在哪里被調(diào)用师崎,函數(shù)的調(diào)用方式默终,函數(shù)的參數(shù)等,this其實是該上下文中的一個屬性犁罩,它指向誰完全取決于函數(shù)的調(diào)用方式齐蔽。

現(xiàn)在我們已經(jīng)弄明白了this最核心的知識:this的指向取決于函數(shù)的調(diào)用方式。

? ?函數(shù)基礎(chǔ)? ?

在接著講解之前床估,有必要對函數(shù)的情況進行簡單說明含滴,比如函數(shù)的創(chuàng)建、參數(shù)的傳遞丐巫、函數(shù)的調(diào)用以及返回值等等谈况。

函數(shù)的創(chuàng)建
在開發(fā)中我們有多種方式來創(chuàng)建(聲明)函數(shù)勺美,可以使用function關(guān)鍵字直接聲明一個具名函數(shù)或者是匿名函數(shù),也可以使用Function構(gòu)造函數(shù)來創(chuàng)建一個函數(shù)實例對象碑韵。

        //01 function關(guān)鍵字聲明函數(shù)
    function f1() {
        console.log("命名函數(shù)|具名函數(shù)");
    }

    var f2 = function () {
        console.log("匿名函數(shù)");
    }

    //02 Function構(gòu)造函數(shù)創(chuàng)建函數(shù)實例對象
    var f3 = new Function('console.log("函數(shù)實例對象的函數(shù)體")');

函數(shù)的參數(shù)
函數(shù)的參數(shù)有兩種赡茸,一種是形式參數(shù),一種是實際參數(shù)泼诱。

形式參數(shù)
在函數(shù)聲明(創(chuàng)建)的時候坛掠,我們可以通過一定的方式來指定函數(shù)的參數(shù),相當于在函數(shù)體內(nèi)聲明了對應(yīng)的臨時局部變量治筒。

實際參數(shù)
在函數(shù)調(diào)用的時候屉栓,會把實際參數(shù)的值傳遞給形式參數(shù),存在一個隱藏的賦值操作耸袜,實際參數(shù)就是函數(shù)調(diào)用時()中的參數(shù)友多。

隱藏參數(shù)
JavaScript中所有函數(shù)中均可以使用this和arguments這兩個附加的隱藏參數(shù)。

    //[1] 函數(shù)的聲明
    //01 function關(guān)鍵字聲明函數(shù)
    function f1(a,b) {
        //a和b為函數(shù)的形式參數(shù)堤框,相當于在此處寫上代碼 var a,b;
        console.log("命名函數(shù)|具名函數(shù)","a的值:" +a , "b的值:"+b);
        console.log(this);      //此處指向window全局對象
        console.log(arguments); //此處打印的是["f1的a","f1的b"]結(jié)構(gòu)的數(shù)據(jù)
    }

    var f2 = function (a,b) {
        //a和b為函數(shù)的形式參數(shù)域滥,相當于在此處寫上代碼 var a,b;
        console.log("匿名函數(shù)","a的值:" +a , "b的值:"+b);
    }

    //02 Function構(gòu)造函數(shù)創(chuàng)建函數(shù)實例對象
    //a和b為新創(chuàng)建的函數(shù)對象的形式參數(shù)
    var f3 = new Function('a','b','console.log("函數(shù)實例對象的函數(shù)體","a的值:" +a , "b的值:"+b)');


    //[2] 函數(shù)的調(diào)用

    //"f1的a"和"f1的b"這兩個字符串作為f1函數(shù)此處調(diào)用傳遞的實際參數(shù)
    //在調(diào)用函數(shù)的時候,會把"f1的a"這個字符串賦值給形參a,把"f1的b"這個字符串賦值給形參b
    f1("f1的a","f1的b");  //命名函數(shù)|具名函數(shù) a的值:f1的a b的值:f1的b

    f2("f2的a","f3的b");  //匿名函數(shù) a的值:f2的a b的值:f3的b
    f3("f3的a","f3的b");  //函數(shù)實例對象的函數(shù)體 a的值:f3的a b的值:f3的b

函數(shù)調(diào)用
函數(shù)名后面跟上調(diào)用運算符[()]的代碼蜈抓,我們稱為函數(shù)調(diào)用启绰,當函數(shù)被調(diào)用的時候,會把實參賦值給形參并自上而下的執(zhí)行函數(shù)體中的代碼沟使。

? ?函數(shù)調(diào)用和this綁定? ?

因為this的綁定完全取決于函數(shù)的調(diào)用方式委可,所以要搞清楚this綁定問題只需要搞清楚函數(shù)調(diào)用方式即可,函數(shù)的調(diào)用方式通常來說有以下四種:

? 普通函數(shù)調(diào)用(默認綁定)
? 對象方法調(diào)用(隱式綁定)
? 構(gòu)造函數(shù)調(diào)用(new綁定)
? 函數(shù)上下文調(diào)用(顯式綁定)

函數(shù)的調(diào)用方式只有上面的四種情況腊嗡,而要確定其具體的調(diào)用方式着倾,需要先確定函數(shù)調(diào)用的位置。

函數(shù)調(diào)用位置
函數(shù)調(diào)用位置也就是函數(shù)在代碼中被調(diào)用的位置[函數(shù)名+()的形式]燕少,我們可以通過下面的示例代碼來理解函數(shù)的調(diào)用位置卡者。

        function f1() {
        console.log("f1");
        //當前的函數(shù)調(diào)用棧:f1
        f2();   //函數(shù)f2調(diào)用的位置
    }

    function f2() {
        console.log("f2");
        //當前函數(shù)調(diào)用棧:f1 --> f2
        f3();   //函數(shù)f3調(diào)用的位置
    }

    function f3() {
        //當前函數(shù)調(diào)用棧:f1-->f2-->f3
        console.log("f3");
    }
    f1();       //函數(shù)f1調(diào)用的位置

1.3 this綁定淺析

? ?① 普通函數(shù)調(diào)用(默認綁定) ? ?

普通函數(shù)調(diào)用就是函數(shù)名后面直接更上調(diào)用運算符調(diào)用,這種情況下函數(shù)調(diào)用時應(yīng)用了this的默認綁定客们,如果是在非嚴格模式下崇决,該this指向全局對象window,如果是在嚴格模式下底挫,不能將全局對象用于默認綁定嗽桩,該this會綁定到undefined。

    //聲明全局變量 t
    var t = 123;           //所有全局變量自動成為全局對象的屬性
    function foo() {
        console.log("foo"); //foo
        console.log(this);  //this ---> 全局對象window
        console.log(this.t);//123
    }


    foo();  //非嚴格模式下:以普通函數(shù)方式調(diào)用

    function fn() {
        "use strict";      //作用域開啟嚴格模式
        console.log("fn"); //fn
        console.log(this); //this --->undefined
        //Uncaught TypeError: Cannot read property 't' of undefined
        console.log(this.t);
    }

    fn();  //嚴格模式下:以普通函數(shù)方式調(diào)用

? ?② 對象方法調(diào)用(隱式綁定) ? ?

對象方法調(diào)用又稱為隱式綁定凄敢,當函數(shù)引用有上下文對象的時候,隱式綁定規(guī)則會把函數(shù)調(diào)用中的this綁定到這個上下文對象湿痢。需要注意的是涝缝,如果存在引用鏈扑庞,那么只有對象屬性引用鏈中的最后一層在調(diào)用位置中起作用,下面我們通過一個代碼片段來理解這種調(diào)用方式拒逮。

    var name = "wenidngding";
    function showName() {
        console.log(this.name);
    }

    //普通函數(shù)調(diào)用罐氨,函數(shù)中的this默認綁定到全局對象,打印wendingding
    showName();

    var obj = {
        name:"小豬佩奇",
        showName:showName
    }

    //對象方法調(diào)用滩援,函數(shù)中的this綁定到當前的上下文對象obj,打印小豬佩奇
    obj.showName();

上下文對象

上下文對象可以簡單理解為函數(shù)調(diào)用時該函數(shù)的擁有者栅隐,或者引用當前函數(shù)的對象。

this丟失的問題

我們在確定this綁定問題的時候不能一根筋的把該函數(shù)是否是對象的方法作為判斷的準則玩徊,而要抓住問題的本質(zhì)租悄,而且代碼中可能存在this隱式綁定丟失的問題。外在的所有形式其實都不重要恩袱,最根本的就是看函數(shù)調(diào)用的時候泣棋,用的是什么方式?

    //字面量方式創(chuàng)建對象畔塔,該對象擁有name屬性和showName方法
    var obj1 = {
        name:"小豬佩奇",
        showName:function () {
            console.log(this.name);
        }
    }

    //調(diào)用位置(001)
    //對象方法調(diào)用潭辈,函數(shù)中的this綁定到當前的上下文對象obj1,打印小豬佩奇
    obj1.showName();

    //[1] 把obj.showName方法賦值給其他的對象
    var obj2 = {name:"阿文"};
    obj2.show = obj1.showName;

    //調(diào)用位置(002)
    //對象方法調(diào)用,函數(shù)中的this綁定到當前的上下文對象obj2,打印阿文
    obj2.show();

    //[2] 把obj.showName方法賦值給一個變量
    var fn = obj1.showName;

    //調(diào)用位置(003)
    //普通函數(shù)調(diào)用澈吨,函數(shù)中的this指向全局對象把敢,打印空字符串(window.name屬性值是空字符串)
    //注意:函數(shù)調(diào)用方式發(fā)生了改變,this丟失了
    fn();

    //[3] 把obj.showName方法作為其他函數(shù)的參數(shù)(回調(diào)函數(shù))來使用
    //聲明函數(shù)谅辣,該函數(shù)接收一個函數(shù)作為參數(shù)
    function foo(callBack) {
        //調(diào)用位置(004)
        //普通函數(shù)調(diào)用修赞,函數(shù)中的this指向全局對象,打印空字符串(window.name屬性值是空字符串)
        //注意:函數(shù)調(diào)用方式發(fā)生了改變屈藐,this丟失了
        callBack();
    }
    //調(diào)用位置(005) 此處不涉及this
    foo(obj1.showName);

? 思考:能否縮短對DOM操作相關(guān)的方法榔组?

console.log(document.getElementById("demoID")); //正確

//聲明getById函數(shù),該函數(shù)指向document.getElementById方法
var getById = document.getElementById;

console.log(getById("demoID"));//報錯:Uncaught TypeError: Illegal invocation

代碼說明 有的朋友可能嘗試過像上面這樣來寫代碼联逻,發(fā)現(xiàn)通過這樣簡單的處理想要縮短DOM操作相關(guān)方法的方式是不可取的搓扯,為什么會報錯?原因在于document.getElementById方法內(nèi)部的實現(xiàn)依賴于this包归,而上面的代碼偷換了函數(shù)的調(diào)用方式锨推,函數(shù)的調(diào)用方式由對象方法調(diào)用轉(zhuǎn)變成了普通函數(shù)調(diào)用,this綁定的對象由document變成了window公壤。

怎么解決呢换可,可以嘗試使用顯式的綁定指定函數(shù)內(nèi)的this,參考代碼如下:

    var getById = function () {
        //顯式的設(shè)置document.getElementById函數(shù)內(nèi)部的this綁定到document對象
        return document.getElementById.apply(document,arguments)
    };
    console.log(getById("demoID"));  //正確

? ?③ 構(gòu)造函數(shù)調(diào)用(new綁定) ? ?
構(gòu)造函數(shù)方式調(diào)用其實就是在調(diào)用函數(shù)的時候使用new關(guān)鍵字厦幅,這種調(diào)用方式主要用于創(chuàng)建指定構(gòu)造函數(shù)對應(yīng)的實例對象沾鳄。

構(gòu)造函數(shù)
構(gòu)造函數(shù)就是普通的函數(shù),本身和普通的函數(shù)沒有任何區(qū)別确憨,其實構(gòu)造函數(shù)應(yīng)該被稱為以構(gòu)造方式調(diào)用的函數(shù)译荞,這樣也許會更準確一些瓤的。因為在調(diào)用的時候總是以new關(guān)鍵字開頭[例如:new Person() ],所以我們把像Person這樣的函數(shù)叫做構(gòu)造函數(shù)吞歼。雖然構(gòu)造函數(shù)和普通函數(shù)無異圈膏,但因為它們調(diào)用的直接目的完全不同,為了人為的區(qū)分它們篙骡,開發(fā)者總是約定構(gòu)造函數(shù)的首字母大寫稽坤。

當函數(shù)被以普通方式調(diào)用的時候,會完成實參向形參的賦值操作糯俗,繼而自上而下的執(zhí)行函數(shù)體中的代碼尿褪,當構(gòu)造函數(shù)被調(diào)用的時候,目的在于獲得對應(yīng)的實例對象叶骨。

    //聲明一個Person函數(shù)
    function Perosn(name,age) {
        this.name = name;
        this.age = age;
        this.show = function () {
            console.log("姓名:" + this.name + " 年齡:" + this.age);
        }
    }

    //函數(shù)調(diào)用位置(001)
    //構(gòu)造函數(shù)方式調(diào)用(new綁定) Person函數(shù)內(nèi)部的this指向新創(chuàng)建的實例對象
    var p1 = new Perosn("zs",18);

    //函數(shù)調(diào)用位置(002)
    //對象方法的方式調(diào)用(隱式綁定) show方法內(nèi)部的this指向的是引用的對象茫多,也就是p1
    //打印:姓名:zs 年齡:18
    p1.show();

構(gòu)造函數(shù)內(nèi)部細節(jié)

使用new以構(gòu)造函數(shù)的方式來調(diào)用Person的時候忽刽,內(nèi)部主要做以下操作

① 創(chuàng)建空的Object類型的實例對象天揖,假設(shè)為對象o
② 讓函數(shù)內(nèi)部的this指向新創(chuàng)建的實例對象o
③ 設(shè)置實例對象o的原型對象指向構(gòu)造函數(shù)默認關(guān)聯(lián)的原型對象
④ 在函數(shù)內(nèi)通過this來添加屬性和方法
⑤ 在最后默認把新創(chuàng)建的實例對象返回

總結(jié) `如果以構(gòu)造函數(shù)方式調(diào)用,函數(shù)內(nèi)部的this綁定給新創(chuàng)建出來的實例對象跪帝。

? ?④ 函數(shù)上下文調(diào)用(顯式綁定) ? ?
在開發(fā)中我們可以通過call()或者是apply()方法來顯式的給函數(shù)綁定指定的this今膊,使用call或者是apply方法這種調(diào)用方式我們稱為是函數(shù)上下文調(diào)用。

JavaScript語言中提供的絕大多數(shù)函數(shù)以及我們自己創(chuàng)建的所有函數(shù)都可以使用call和apply方法伞剑,這兩個方法的作用幾乎完全相同斑唬,只有傳參的方式有細微的差別。

call方法和apply方法的使用

作用:借用對象的方法并顯式綁定函數(shù)內(nèi)的this黎泣。
語法:對象.方法.call(綁定的對象恕刘,參數(shù)1,參數(shù)2...) | 對象.方法.apply(綁定的對象抒倚,[參數(shù)1褐着,參數(shù)2...])

使用代碼示例

    var obj1 = {
        name:"zs",
        showName:function (a,b) {
            console.log("姓名 " + this.name,a, b);
        }
    };

    var obj2 = {name:"ls"};

    //函數(shù)調(diào)用位置(001)
    //以對象方法的方式調(diào)用函數(shù),函數(shù)內(nèi)部的this指向引用對象托呕,也就是obj1
    //打印結(jié)果為:姓名 zs 1 2
    obj1.showName(1,2);

    //函數(shù)調(diào)用位置(002)
    //obj2對象并不擁有showName方法含蓉,此處報錯:obj2.showName is not a function
    //obj2.showName();

    //函數(shù)調(diào)用位置(003)
    //函數(shù)上下文的方式(call)調(diào)用函數(shù),函數(shù)內(nèi)部的this綁定給第一個參數(shù)obj2
    //打印結(jié)果為:姓名 ls 哈哈 嘿嘿
    //第一個參數(shù):obj2指定函數(shù)內(nèi)this的綁定對象
    //其它的參數(shù):哈哈和嘿嘿這兩個字符串是傳遞給showName函數(shù)的實參项郊,調(diào)用時會賦值給函數(shù)的形參:a和b
    obj1.showName.call(obj2,"哈哈","嘿嘿");

    //函數(shù)調(diào)用位置(004)
    //函數(shù)上下文的方式(apply)調(diào)用函數(shù)馅扣,函數(shù)內(nèi)部的this綁定給第一個參數(shù)obj2
    //打印結(jié)果為:姓名 ls 呵呵 嘎嘎
    //第一個參數(shù):obj2指定函數(shù)內(nèi)this的綁定對象
    //其它的參數(shù):呵呵和嘎嘎這兩個字符串是傳遞給showName函數(shù)的實參,調(diào)用時會賦值給函數(shù)的形參:a和b
    obj1.showName.apply(obj2,["呵呵","嘎嘎"]);

總結(jié) 如果以函數(shù)上下文的方式來調(diào)用着降,函數(shù)內(nèi)部的this綁定call或者是apply方法的第一個參數(shù)差油,如果該參數(shù)不是對象類型那么會自動轉(zhuǎn)換為對應(yīng)的對象形式。

1.4 this的注意事項

我們已經(jīng)介紹了一般情況下this綁定的問題任洞,雖然上面的規(guī)則可以適用絕大多數(shù)的代碼場景厌殉,但也并非總是百分百如此食绿,也有例外。

? ?例外的情況 ① ? ?

在使用call或者apply方法的時候公罕,非嚴格模式下如果我們傳遞的參數(shù)是null或者是undefined,那么這些值在調(diào)用的時候其實會被忽略耀销,this默認綁定的其實是全局對象楼眷。

    /**[代碼 01]**/
    //聲明全局變量用于測試
    var name = "測試的name";
    var obj1 = {
        name:"zs",
        showName:function (a,b) {
            console.log("姓名 " + this.name,a, b);
        }
    };

    //注意:雖然此處以上下文的方式調(diào)用,但是因為傳遞的第一個參數(shù)是null,實際這里應(yīng)用的是默認綁定規(guī)則
    obj1.showName.call(null,1,2);       //姓名 測試的name 1 2
    obj1.showName.call(undefined,1,2);  //姓名 測試的name 1 2

嚴格模式下熊尉,傳遞null或者是undefined作為call和apply方法的第一個參數(shù)罐柳,this的綁定和上下文調(diào)用保持一致。

    /**[代碼 02]**/
    //開啟嚴格模式
    "use strict";

    //聲明全局變量用于測試
    var obj = {
        name:"zs",
        showName:function () {
            console.log(this);
        }
    };

    obj.showName.call(null);        //null
    obj.showName.apply(undefined);  //undefined

    //建議的處理方式
    obj.showName.apply(Object.create(null));

建議 以前我們在以函數(shù)上下文方式來調(diào)用函數(shù)的時候狰住,如果并不關(guān)心函數(shù)內(nèi)部的this綁定张吉,那么一般會傳遞null值或者undefined值。如果這樣的話催植,在非嚴格模式下肮蛹,函數(shù)內(nèi)部的this默認綁定給全局對象并不安全,建議傳遞空對象[可以使用Object.create(null)方式創(chuàng)建]创南,這樣函數(shù)操作會更安全而且代碼可讀性會更好伦忠。

? ?例外的情況 ② ? ?

ES6中推出了一種特殊的函數(shù)類型:箭頭函數(shù)。箭頭函數(shù)使用=>操作符來定義稿辙,需要注意的是箭頭函數(shù)內(nèi)部的this綁定并不適用于既定的四種規(guī)則昆码,this的綁定由外層作用域來決定。

    //聲明函數(shù)
    function fn() {
        console.log("fn",this);
        //fn函數(shù)中返回一個箭頭函數(shù)
        return ()=>{
            console.log(this);
        }
    }

    var o = {name:"zs"};
    //fn以普通函數(shù)方式調(diào)用邻储,fn中的this指向全局對象
    //箭頭函數(shù)中的this綁定由外部的詞法作用域來決定,this指向window
    fn()();

    //fn以函數(shù)上下文方式調(diào)用赋咽,fn中的this指向?qū)ο髈
    //箭頭函數(shù)中的this綁定由外部的詞法作用域來決定,this指向?qū)ο髈
    fn.call(o)(); //this指向{name:"zs"}對象

? ?例外的情況 ③ ? ?

需要特別注意的是:在代碼中我們可能會創(chuàng)建函數(shù)的“間接引用”,這種情況下調(diào)用函數(shù)會使用默認綁定規(guī)則吨娜。

    var objA = {
        name:"zs",
        showName:function(){
            console.log(this.name);
        }
    }

    var objB = {name:"ls"};
    objA.showName();                //對象方法調(diào)用脓匿,this指向objA 打印zs

    (objB.showName = objA.showName)(); //打印 空字符串

代碼說明 我們重點看最后一行代碼,賦值表達式objB.showName = objA.showName的返回值是目標函數(shù)的引用萌壳,這種間接引用調(diào)用方式符合普通函數(shù)調(diào)用的規(guī)則亦镶,this會被綁定給全局對象。最后一行代碼袱瓮,拆開來寫的形式:

    var f = objB.showName = objA.showName;
    f(); //打印 空字符串

1.5 this綁定總結(jié)

當函數(shù)的調(diào)用位置確定后缤骨,我們可以順序應(yīng)用下面的四條規(guī)則來判斷this的綁定對象

① 是否由new調(diào)用? 如果是尺借,則綁定到構(gòu)造函數(shù)新創(chuàng)建的實例對象身上绊起。
② 是否由call或者apply調(diào)用?如果是燎斩,則綁定到第一個參數(shù)指定的對象身上虱歪。
③ 是有作為對象的方法調(diào)用蜂绎?如果是,則綁定到這個引用的對象身上笋鄙。
④ 默認普通函數(shù)調(diào)用师枣,如果是嚴格模式則綁定到undefined,否則綁定到全局對象萧落。


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末陨倡,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子许布,更是在濱河造成了極大的恐慌兴革,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蜜唾,死亡現(xiàn)場離奇詭異杂曲,居然都是意外死亡,警方通過查閱死者的電腦和手機灵妨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進店門解阅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來友扰,“玉大人管引,你說我怎么就攤上這事∑芽纾” “怎么了朱转?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵蟹地,是天一觀的道長。 經(jīng)常有香客問我藤为,道長怪与,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任缅疟,我火速辦了婚禮分别,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘存淫。我一直安慰自己耘斩,他們只是感情好,可當我...
    茶點故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布桅咆。 她就那樣靜靜地躺著括授,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上荚虚,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天薛夜,我揣著相機與錄音,去河邊找鬼版述。 笑死梯澜,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的渴析。 我是一名探鬼主播腊徙,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼檬某!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起螟蝙,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤恢恼,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后胰默,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體场斑,經(jīng)...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年牵署,在試婚紗的時候發(fā)現(xiàn)自己被綠了漏隐。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,566評論 1 339
  • 序言:一個原本活蹦亂跳的男人離奇死亡奴迅,死狀恐怖青责,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情取具,我是刑警寧澤脖隶,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站暇检,受9級特大地震影響产阱,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜块仆,卻給世界環(huán)境...
    茶點故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一构蹬、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧悔据,春花似錦庄敛、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春隐绵,著一層夾襖步出監(jiān)牢的瞬間之众,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工依许, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留棺禾,地道東北人。 一個月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓峭跳,卻偏偏與公主長得像膘婶,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蛀醉,可洞房花燭夜當晚...
    茶點故事閱讀 43,440評論 2 348

推薦閱讀更多精彩內(nèi)容