<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    posts - 8,  comments - 6,  trackbacks - 0

      

    4)    this、new、call和apply的相關(guān)問題

      講解this指針的原理是個(gè)很復(fù)雜的問題,如果我們從javascript里this的實(shí)現(xiàn)機(jī)制來說明this,很多朋友可能會(huì)越來越糊涂,因此本篇打算換一個(gè)思路從應(yīng)用的角度來講解this指針,從這個(gè)角度理解this指針更加有現(xiàn)實(shí)意義。

      下面我們看看在java語言里是如何使用this指針的,代碼如下:

    復(fù)制代碼
    public class Person {
        private String name;
        private String sex;
        private int age;
        private String job;
        public Person(String name, String sex, int age, String job) {
            super();
            this.name = name;
            this.sex = sex;
            this.age = age;
            this.job = job;
        }
        private void showPerson(){
            System.out.println("姓名:" + this.name);
            System.out.println("性別:" + this.sex);
            System.out.println("年齡:" + this.age);
            System.out.println("工作:" + this.job);
        }
        public void printInfo(){
            this.showPerson();
        }
        public static void main(String[] args) {
            Person person = new Person("馬云", "男", 46, "董事長(zhǎng)");
            person.printInfo();
        }
    }
    //姓名:馬云
    //性別:男
    //年齡:46
    //工作:董事長(zhǎng)
    復(fù)制代碼

      

      上面的代碼執(zhí)行后沒有任何問題,下面我修改下這個(gè)代碼,加一個(gè)靜態(tài)的方法,靜態(tài)方法里使用this指針調(diào)用類里的屬性,如下圖所示:

     

      我們發(fā)現(xiàn)IDE會(huì)報(bào)出語法錯(cuò)誤“Cannot use this in a static context”,this指針在java語言里是不能使用在靜態(tài)的上下文里的。

      在面向?qū)ο缶幊汤镉袃蓚€(gè)重要的概念:一個(gè)是類,一個(gè)是實(shí)例化的對(duì)象,類是一個(gè)抽象的概念,用個(gè)形象的比喻表述的話,類就像一個(gè)模具,而實(shí)例化對(duì)象就是通過這個(gè)模具制造出來的產(chǎn)品,實(shí)例化對(duì)象才是我們需要的實(shí)實(shí)在在的東西,類和實(shí)例化對(duì)象有著很密切的關(guān)系,但是在使用上類的功能是絕對(duì)不能取代實(shí)例化對(duì)象,就像模具和模具制造的產(chǎn)品的關(guān)系,二者的用途是不相同的。

      有上面代碼我們可以看到,this指針在java語言里只能在實(shí)例化對(duì)象里使用,this指針等于這個(gè)被實(shí)例化好的對(duì)象,而this后面加上點(diǎn)操作符,點(diǎn)操作符后面的東西就是this所擁有的東西,例如:姓名,工作,手,腳等等。

      其實(shí)javascript里的this指針邏輯上的概念也是實(shí)例化對(duì)象,這一點(diǎn)和java語言里的this指針是一致的,但是javascript里的this指針卻比java里的this難以理解的多,究其根本原因我個(gè)人覺得有三個(gè)原因:

      原因一:javascript是一個(gè)函數(shù)編程語言,怪就怪在它也有this指針,說明這個(gè)函數(shù)編程語言也是面向?qū)ο蟮恼Z言,說的具體點(diǎn),javascript里的函數(shù)是一個(gè)高階函數(shù),編程語言里的高階函數(shù)是可以作為對(duì)象傳遞的,同時(shí)javascript里的函數(shù)還有可以作為構(gòu)造函數(shù),這個(gè)構(gòu)造函數(shù)可以創(chuàng)建實(shí)例化對(duì)象,結(jié)果導(dǎo)致方法執(zhí)行時(shí)候this指針的指向會(huì)不斷發(fā)生變化,很難控制。

      原因二:javascript里的全局作用域?qū)his指針有很大的影響,由上面java的例子我們看到,this指針只有在使用new操作符后才會(huì)生效,但是javascript里的this在沒有進(jìn)行new操作也會(huì)生效,這時(shí)候this往往會(huì)指向全局對(duì)象window

      原因三:javascript里call和apply操作符可以隨意改變this指向,這看起來很靈活,但是這種不合常理的做法破壞了我們理解this指針的本意,同時(shí)也讓寫代碼時(shí)候很難理解this的真正指向

      上面的三個(gè)原因都違反了傳統(tǒng)this指針使用的方法,它們都擁有有別于傳統(tǒng)this原理的理解思路,而在實(shí)際開發(fā)里三個(gè)原因又往往會(huì)交織在一起,這就更加讓人迷惑不解了,今天我要為大家理清這個(gè)思路,其實(shí)javascript里的this指針有一套固有的邏輯,我們理解好這套邏輯就能準(zhǔn)確的掌握好this指針的使用。

      我們先看看下面的代碼:

    復(fù)制代碼
    <script type="text/javascript">
        this.a = "aaa";
        console.log(a);//aaa
        console.log(this.a);//aaa
        console.log(window.a);//aaa
        console.log(this);// window
        console.log(window);// window
        console.log(this == window);// true
        console.log(this === window);// true
    </script>
    復(fù)制代碼

      在script標(biāo)簽里我們可以直接使用this指針,this指針就是window對(duì)象,我們看到即使使用三等號(hào)它們也是相等的。全局作用域常常會(huì)干擾我們很好的理解javascript語言的特性,這種干擾的本質(zhì)就是:

      在javascript語言里全局作用域可以理解為window對(duì)象,記住window是對(duì)象而不是類,也就是說window是被實(shí)例化的對(duì)象,這個(gè)實(shí)例化的過程是在頁面加載時(shí)候由javascript引擎完成的,整個(gè)頁面里的要素都被濃縮到這個(gè)window對(duì)象,因?yàn)槌绦騿T無法通過編程語言來控制和操作這個(gè)實(shí)例化過程,所以開發(fā)時(shí)候我們就沒有構(gòu)建這個(gè)this指針的感覺,常常會(huì)忽視它,這就是干擾我們?cè)诖a里理解this指針指向window的情形。

      干擾的本質(zhì)還和function的使用有關(guān),我們看看下面的代碼:

    復(fù)制代碼
    <script type="text/javascript">
        function ftn01(){
           console.log("I am ftn01!");
        }
        var ftn02 = function(){
            console.log("I am ftn02!");
        }
    </script>    
    復(fù)制代碼

     

      上面是我們經(jīng)常使用的兩種定義函數(shù)的方式,第一種定義函數(shù)的方式在javascript語言稱作聲明函數(shù),第二種定義函數(shù)的方式叫做函數(shù)表達(dá)式,這兩種方式我們通常認(rèn)為是等價(jià)的,但是它們其實(shí)是有區(qū)別的,而這個(gè)區(qū)別常常會(huì)讓我們混淆this指針的使用,我們?cè)倏纯聪旅娴拇a:

    復(fù)制代碼
    <script type="text/javascript">
        console.log(ftn01);//ftn01()  注意:在firebug下這個(gè)打印結(jié)果是可以點(diǎn)擊,點(diǎn)擊后會(huì)顯示函數(shù)的定義
        console.log(ftn02);// undefined
        function ftn01(){
           console.log("I am ftn01!");
        }
        var ftn02 = function(){
            console.log("I am ftn02!");
        }
    </script>    
    復(fù)制代碼

     

      這又是一段沒有按順序執(zhí)行的代碼,先看看ftn02,打印結(jié)果是undefined,undefined我在前文里講到了,在內(nèi)存的棧區(qū)已經(jīng)有了變量的名稱,但是沒有棧區(qū)的變量值,同時(shí)堆區(qū)是沒有具體的對(duì)象,這是javascript引擎在預(yù)處理(群里東方說預(yù)處理比預(yù)加載更準(zhǔn)確,我同意他的說法,以后文章里我都寫為預(yù)處理)掃描變量定義所致,但是ftn01的打印結(jié)果很令人意外,既然打印出完成的函數(shù)定義了,而且代碼并沒有按順序執(zhí)行,這只能說明一個(gè)問題:

      在javascript語言通過聲明函數(shù)方式定義函數(shù),javascript引擎在預(yù)處理過程里就把函數(shù)定義和賦值操作都完成了,在這里我補(bǔ)充下javascript里預(yù)處理的特性,其實(shí)預(yù)處理是和執(zhí)行環(huán)境相關(guān),在上篇文章里我講到執(zhí)行環(huán)境有兩大類:全局執(zhí)行環(huán)境和局部執(zhí)行環(huán)境,執(zhí)行環(huán)境是通過上下文變量體現(xiàn)的,其實(shí)這個(gè)過程都是在函數(shù)執(zhí)行前完成,預(yù)處理就是構(gòu)造執(zhí)行環(huán)境的另一個(gè)說法,總而言之預(yù)處理和構(gòu)造執(zhí)行環(huán)境的主要目的就是明確變量定義,分清變量的邊界,但是在全局作用域構(gòu)造或者說全局變量預(yù)處理時(shí)候?qū)τ诼暶骱瘮?shù)有些不同,聲明函數(shù)會(huì)將變量定義和賦值操作同時(shí)完成,因此我們看到上面代碼的運(yùn)行結(jié)果。由于聲明函數(shù)都會(huì)在全局作用域構(gòu)造時(shí)候完成,因此聲明函數(shù)都是window對(duì)象的屬性,這就說明為什么我們不管在哪里聲明函數(shù),聲明函數(shù)最終都是屬于window對(duì)象的原因了

      關(guān)于函數(shù)表達(dá)式的寫法還有秘密可以探尋,我們看下面的代碼:

    復(fù)制代碼
    <script type="text/javascript">
        function ftn03(){
            var ftn04 = function(){
                console.log(this);// window
            };
            ftn04();
        }
        ftn03();
    </script>
    復(fù)制代碼

     

      運(yùn)行結(jié)果我們發(fā)現(xiàn)ftn04雖然在ftn03作用域下,但是執(zhí)行它里面的this指針也是指向window,其實(shí)函數(shù)表達(dá)式的寫法我們大多數(shù)更喜歡在函數(shù)內(nèi)部寫,因?yàn)槁暶骱瘮?shù)里的this指向window這已經(jīng)不是秘密,但是函數(shù)表達(dá)式的this指針指向window卻是常常被我們所忽視,特別是當(dāng)它被寫在另一個(gè)函數(shù)內(nèi)部時(shí)候更加如此。

      其實(shí)在javascript語言里任何匿名函數(shù)都是屬于window對(duì)象,它們也都是在全局作用域構(gòu)造時(shí)候完成定義和賦值,但是匿名函數(shù)是沒有名字的函數(shù)變量,但是在定義匿名函數(shù)時(shí)候它會(huì)返回自己的內(nèi)存地址,如果此時(shí)有個(gè)變量接收了這個(gè)內(nèi)存地址,那么匿名函數(shù)就能在程序里被使用了,因?yàn)槟涿瘮?shù)也是在全局執(zhí)行環(huán)境構(gòu)造時(shí)候定義和賦值,所以匿名函數(shù)的this指向也是window對(duì)象,所以上面代碼執(zhí)行時(shí)候ftn04的this也是指向window,因?yàn)閖avascript變量名稱不管在那個(gè)作用域有效,堆區(qū)的存儲(chǔ)的函數(shù)都是在全局執(zhí)行環(huán)境時(shí)候就被固定下來了,變量的名字只是一個(gè)指代而已。

      這下子壞了,this都指向window,那我們到底怎么才能改變它了?

      在本文開頭我說出了this的秘密,this都是指向?qū)嵗瘜?duì)象,前面講到那么多情況this都指向window,就是因?yàn)檫@些時(shí)候只做了一次實(shí)例化操作,而這個(gè)實(shí)例化都是在實(shí)例化window對(duì)象,所以this都是指向window。我們要把this從window變成別的對(duì)象,就得要讓function被實(shí)例化,那如何讓javascript的function實(shí)例化呢?答案就是使用new操作符。我們看看下面的代碼:

    復(fù)制代碼
    <script type="text/javascript">
        var obj = {
            name:"sharpxiajun",
            job:"Software",
            show:function(){
                console.log("Name:" + this.name + ";Job:" + this.job);
                console.log(this);// Object { name="sharpxiajun", job="Software", show=function()}
            }
        };
        var otherObj = new Object();
        otherObj.name = "xtq";
        otherObj.job = "good";
        otherObj.show = function(){
            console.log("Name:" + this.name + ";Job:" + this.job);
            console.log(this);// Object { name="xtq", job="good", show=function()}
        };
        obj.show();//Name:sharpxiajun;Job:Software
        otherObj.show();//Name:xtq;Job:good
    </script>    
    復(fù)制代碼

     

       這是我上篇講到的關(guān)于this使用的一個(gè)例子,寫法一是我們大伙都愛寫的一種寫法,里面的this指針不是指向window的,而是指向Object的實(shí)例,firebug的顯示讓很多人疑惑,其實(shí)Object就是面向?qū)ο蟮念悾罄ㄌ?hào)里就是實(shí)例對(duì)象了,即obj和otherObj。Javascript里通過字面量方式定義對(duì)象的方式是new Object的簡(jiǎn)寫,二者是等價(jià)的,目的是為了減少代碼的書寫量,可見即使不用new操作字面量定義法本質(zhì)也是new操作符,所以通過new改變this指針的確是不過攻破的真理。

      下面我使用javascript來重寫本篇開頭用java定義的類,代碼如下:

    復(fù)制代碼
    <script type="text/javascript">
        function Person(name,sex,age,job){
            this.name = name;
            this.sex = sex;
            this.age = age;
            this.job = job;
            this.showPerson = function(){
                console.log("姓名:" + this.name);
                console.log("性別:" + this.sex);
                console.log("年齡:" + this.age);
                console.log("工作:" + this.job);
                console.log(this);// Person { name="馬云", sex="男", age=46, 更多...}
            }
        }
        var person = new Person("馬云", "男", 46, "董事長(zhǎng)");
        person.showPerson();
    </script>
    復(fù)制代碼

      看this指針的打印,類變成了Person,這表明function Person就是相當(dāng)于在定義一個(gè)類,在javascript里function的意義實(shí)在太多,function既是函數(shù)又可以表示對(duì)象,function是函數(shù)時(shí)候還能當(dāng)做構(gòu)造函數(shù),javascript的構(gòu)造函數(shù)我常認(rèn)為是把類和構(gòu)造函數(shù)合二為一,當(dāng)然在javascript語言規(guī)范里是沒有類的概念,但是我這種理解可以作為構(gòu)造函數(shù)和普通函數(shù)的一個(gè)區(qū)別,這樣理解起來會(huì)更加容易些

      下面我貼出在《javascript高級(jí)編程》里對(duì)new操作符的解釋:

      new操作符會(huì)讓構(gòu)造函數(shù)產(chǎn)生如下變化:

      1.       創(chuàng)建一個(gè)新對(duì)象;

      2.       將構(gòu)造函數(shù)的作用域賦給新對(duì)象(因此this就指向了這個(gè)新對(duì)象);

      3.       執(zhí)行構(gòu)造函數(shù)中的代碼(為這個(gè)新對(duì)象添加屬性);

      4.       返回新對(duì)象

      關(guān)于第二點(diǎn)其實(shí)很容易讓人迷惑,例如前面例子里的obj和otherObj,obj.show(),里面this指向obj,我以前文章講到一個(gè)簡(jiǎn)單識(shí)別this方式就是看方法調(diào)用前的對(duì)象是哪個(gè)this就指向哪個(gè),其實(shí)這個(gè)過程還可以這么理解,在全局執(zhí)行環(huán)境里window就是上下文對(duì)象,那么在obj里局部作用域通過obj來代表了,這個(gè)window的理解是一致的。

      第四點(diǎn)也要著重講下,記住構(gòu)造函數(shù)被new操作,要讓new正常作用最好不能在構(gòu)造函數(shù)里寫return,沒有return的構(gòu)造函數(shù)都是按上面四點(diǎn)執(zhí)行,有了return情況就復(fù)雜了,這個(gè)知識(shí)我會(huì)在講prototype時(shí)候講到。

      Javascript還有一種方式可以改變this指針,這就是call方法和apply方法,call和apply方法的作用相同,就是參數(shù)不同,call和apply的第一個(gè)參數(shù)都是一樣的,但是后面參數(shù)不同,apply第二個(gè)參數(shù)是個(gè)數(shù)組,call從第二個(gè)參數(shù)開始后面有許多參數(shù)。Call和apply的作用是什么,這個(gè)很重要,重點(diǎn)描述如下:

      Call和apply是改變函數(shù)的作用域(有些書里叫做改變函數(shù)的上下文)

      這個(gè)說明我們參見上面new操作符第二條:

      將構(gòu)造函數(shù)的作用域賦給新對(duì)象(因此this就指向了這個(gè)新對(duì)象);

      Call和apply是將this指針指向方法的第一個(gè)參數(shù)。

      我們看看下面的代碼:

    復(fù)制代碼
    <script type="text/javascript">
        var name = "sharpxiajun";
        function ftn(name){
            console.log(name);
            console.log(this.name);
            console.log(this);
        }
        ftn("101");
        var obj = {
          name:"xtq"
        };
        ftn.call(obj,"102");
        /*
        * 結(jié)果如下所示:
        *101
         T002.html (第 73 行)
         sharpxiajun
         T002.html (第 74 行)
         Window T002.html
         T002.html (第 75 行)
         102
         T002.html (第 73 行)
         xtq
         T002.html (第 74 行)
         Object { name="xtq"}
        * */
    </script>
    復(fù)制代碼

      我們看到apply和call改變的是this的指向,這點(diǎn)在開發(fā)里很重要,開發(fā)里我們常常被this所迷惑,迷惑的根本原因我在上文講到了,這里我講講表面的原因:

      表面原因就是我們定義對(duì)象使用對(duì)象的字面表示法,字面表示法在簡(jiǎn)單的表示里我們很容易知道this指向?qū)ο蟊旧恚沁@個(gè)對(duì)象會(huì)有方法,方法的參數(shù)可能會(huì)是函數(shù),而這個(gè)函數(shù)的定義里也可能會(huì)使用this指針,如果傳入的函數(shù)沒有被實(shí)例化過和被實(shí)例化過,this的指向是不同,有時(shí)我們還想在傳入函數(shù)里通過this指向外部函數(shù)或者指向被定義對(duì)象本身,這些亂七八糟的情況使用交織在一起導(dǎo)致this變得很復(fù)雜,結(jié)果就變得糊里糊涂。

      其實(shí)理清上面情況也是有跡可循的,就以定義對(duì)象里的方法里傳入函數(shù)為例:

      情形一:傳入的參數(shù)是函數(shù)的別名,那么函數(shù)的this就是指向window

      情形二:傳入的參數(shù)是被new過的構(gòu)造函數(shù),那么this就是指向?qū)嵗膶?duì)象本身;

      情形三:如果我們想把被傳入的函數(shù)對(duì)象里this的指針指向外部字面量定義的對(duì)象,那么我們就是用apply和call

      我們可以通過代碼看出我的結(jié)論,代碼如下:

    復(fù)制代碼
    <script type="text/javascript">
    var name = "I am window";
    var obj = {
        name:"sharpxiajun",
        job:"Software",
        ftn01:function(obj){
            obj.show();
        },
        ftn02:function(ftn){
            ftn();
        },
        ftn03:function(ftn){
            ftn.call(this);
        }
    };
    function Person(name){
        this.name = name;
        this.show = function(){
            console.log("姓名:" + this.name);
            console.log(this);
        }
    }
    var p = new Person("Person");
    obj.ftn01(p);
    obj.ftn02(function(){
       console.log(this.name);
       console.log(this);
    });
    obj.ftn03(function(){
        console.log(this.name);
        console.log(this);
    });
    </script>
    復(fù)制代碼

      結(jié)果如下:

     

      最后再總結(jié)一下:

      如果在javascript語言里沒有通過new(包括對(duì)象字面量定義)、call和apply改變函數(shù)的this指針,函數(shù)的this指針都是指向window

     

    posted on 2014-12-09 17:54 ♂游泳的魚 閱讀(6098) 評(píng)論(1)  編輯  收藏

    FeedBack:
    # re: javascript技術(shù)難點(diǎn)(三)之this、new、apply和call詳解
    2014-12-16 15:19 | 獵頭
    看完之后,終于把this、new、call和apply的相關(guān)問題搞清楚了。  回復(fù)  更多評(píng)論
      

    只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


    網(wǎng)站導(dǎo)航:
     

    <2014年12月>
    30123456
    78910111213
    14151617181920
    21222324252627
    28293031123
    45678910

    留言簿(1)

    隨筆分類

    隨筆檔案

    文章分類

    文章檔案

    搜索

    •  

    最新評(píng)論

    閱讀排行榜

    主站蜘蛛池模板: 久久夜色精品国产噜噜亚洲AV| 亚洲最大天堂无码精品区| 99精品视频免费观看| 亚洲国产精品综合久久20| 国产免费看插插插视频| 国产一二三四区乱码免费| 亚洲视频中文字幕在线| 日韩在线天堂免费观看| 99久久99这里只有免费的精品| 亚洲精品mv在线观看| 国产免费观看黄AV片| 成全动漫视频在线观看免费高清版下载| 亚洲麻豆精品果冻传媒| 国产精品免费一级在线观看| 人妻在线日韩免费视频| 中日韩亚洲人成无码网站| 国产亚洲精品无码拍拍拍色欲| 日韩在线播放全免费| 一级毛片免费一级直接观看| 亚洲国产精品yw在线观看| 亚洲狠狠爱综合影院婷婷| 1000部拍拍拍18勿入免费凤凰福利| 香港经典a毛片免费观看看| 亚洲综合无码一区二区| 亚洲电影日韩精品 | 免费无码黄十八禁网站在线观看| 一级毛片免费毛片毛片| 亚洲无码一区二区三区| 亚洲午夜久久影院| 国产精品亚洲mnbav网站| 拨牐拨牐x8免费| 免费A级毛片av无码| 亚欧乱色国产精品免费视频| 亚洲综合色婷婷在线观看| 久久精品国产精品亚洲艾草网| 国产一级淫片a免费播放口之| 曰批全过程免费视频播放网站 | 久别的草原电视剧免费观看| 一级做a爱片特黄在线观看免费看 一级做a爱过程免费视 | 内射无码专区久久亚洲| 最新欧洲大片免费在线 |