JavaScript——我理解的OOP

此篇文章是看完《 JavaScript程序設計》—— 第五章 函數這一篇有感而發,以爲本身做爲新手,而JavaScript又做爲本身學的第一門語言,對原型鏈、構造器、this等特性概念的理解掌握對面向對象這一編程思想的造成有重要意義,因此我會把本身的不那麼準確但儘可能準確的理解寫一寫。

做爲屬性的函數

唔,就從5.5.2 做爲屬性的函數這裏提及吧~我會常常引用書裏的原話,儘可能去理解做者的含義。

因爲函數也是值,因此能夠做爲對象的屬性。把函數放在對象內部有兩個主要理由第一個理由是把許多相關函數放在一組。把許多函數組合到單個對象中,有助於組織和理解大型程序。人類不但願去嘗試理解一個擁有數百個甚至數千個函數的系統,若是一個系統只有數十個軟件組成部分,那咱們理解起來會容易不少。例如,在一個遊戲程序中,咱們會很天然地爲玩家、地貌、物理屬性、消息傳遞、裝備、圖像等分別建立出子系統,每一個都是一個很大的對象。編程

將函數做爲屬性的第二個理由是讓程序從面向過程轉向面向對象。例如,咱們不必定要將函數看做對形狀執行操做,將函數存儲爲形狀的屬性。將函數放在對象的內部,可讓人們專一於這些函數,讓函數扮演對象行爲的角色。數組

面向對象編程這詞兒相信不少人據說過,啥是對象?!我沒有對象啊!亂說啥?好了,那咱們就用JavaScript生成一個對象不就行了嘛~

var firstGirlfriend = {
            // 屬性
            sex:"woman",
            behaviorA:function () {return ("漂亮")},
            behaviorB:function () {return ("前凸後翹")},
            skill:{first:"作家務",second:"按摩",thirdly:"PAPAPA"}
            // 方法
            behaviorC:function () {
                return ("我會:"+this.skill.first+" | "+this.skill.second+" | ")
            },
            test:function () {return this;}
        };
        alert(firstGirlfriend.behaviorC())                 // "我會:作家務 | 按摩 | "
        alert(firstGirlfriend.test()===firstGirlfriend)    // true

YEAH!!我有對象啦,嗯哼一看,臥槽廣大羣衆義憤填膺了,TM的有妹子就不錯了還作家務!按摩!!最後那是神馬?哦,沒看見。咳咳,都說了自定義的妹子嘛~確定得全能一些嘛~實際狀況是,那些作家務、按摩這些技能樹確定是我來點啦,妹子只負責貌美如花就OK了~blablabla~

這裏先給個結論,不是很準確,關於this我會另開一篇文章談個人理解:

  • 在最外層代碼中,this引用的是全局對象。

  • 在函數內,this引用根據函數調用方式不一樣而有所不一樣。

  • 接收方對象:經過點運算符或中括號運算符調用對象的方法時,在運算符左側所指定的對象。


var obj = {
    x:3,
    doit:function () {alert("method is called."+this.x);}
};
obj.doit();            // 對象obj時接收方對象。doit是方法
obj["doit"]();         // 同上

回到我們的"妹子"那兒,我想知道妹子有有什麼技能,因此我用一個方法(behaviorC())讓她本身說出來,這時誰是接收方呢?沒錯,是妹子firstGirlfriend,妹子本身說本身會什麼嘛,固然是她本身了,test()也證實了函數

方法中,this.skill.first,先肯定this引用的對象,而後讀取屬性值,最後成爲全局函數alert的傳入值,被彈出。學習

好了大概知道this是什麼咱們就要繼續了。this

想想古代的皇帝,三宮六妾的,想一想如今的一夫一妻制,哇哈哈,崩潰ing。那怎麼辦,我想要更多的妹子啊:)(程序媛看到這裏不要拉黑我,我只是想一想而已),難道一個一個碼嗎?固然不會,因此咱們寫一個函數來生成妹子

var GirlfriendPlant = function (s) {
            return {
                sex:s,
                behaviorA:function () {return ("漂亮")},
                behaviorB:function () {return ("前凸後翹")},
                skill:{first:"作家務",second:"按摩",thirdly:"PAPAPA"},
                // 方法
                behaviorC:function () {
                    return ("我會:"+this.skill.first+" | "+this.skill.second+" | ")
                },
                test:function () {return this;}
            };
        };
           
        var girl_a = GirlfriendPlant("woman");
        var girl_b = GirlfriendPlant("man");
        alert(girl_a.behaviorB());            // "前凸後翹"
        alert(girl_b.sex+" | "+girl_b.behaviorC("man | 我會:作家務 | 按摩 | "));  // :)

瞧,是否是不用一個一個的寫啦?並且我還高度定製了一些"功能",好比......咳咳,我是異性戀,可是男生力氣大作家務也快不是?
可是!仍是有不足的地方,每一個妹子出生就自帶這些技能和屬性了,但是有些妹子是不肯學習某些技能的,好比一些方法,一些屬性也不想表露出來,怎麼辦?spa

這段代碼看上去沒問題,卻有一個嚴重缺陷,每次建立一個對象,都另行建立了額外的屬性、方法。在建立多個對象時,會浪費大量的內存來保存這些函數方法的冗餘副本,這是很糟糕的事情,由於內存資源是有限的。當腳本耗盡內存就會崩潰。可是,咱們還有一個方法來解決它們!

// 工廠設置製造車間,protoMGP對象表明一個技能屬性坑齊全可是未命名未定製的妹子
        var protoMGP = {
            sex:undefined,
            behaviorA:function () {return ("漂亮")},
            behaviorB:function () {return ("前凸後翹")},
            skill:{first:undefined,second:undefined,thirdly:undefined},
            // 方法
            behaviorC:function () {
                alert("我會:"+this.skill.first+" | "+this.skill.second+" | "+this.skill.thirdly);
            }
        };
        // 工廠參數輸入車間
        var middleGirlPlant = function (sex,f,s,t) {
            // 得到車間製造的妹子(對象),注意得到的只是粗胚,爲設置參數,但已經留好坑了
            var girlObj = Object.create(protoMGP);        
            // 開始定製妹子                
            girlObj.sex = sex;                            
            girlObj.skill.first = f;
            girlObj.skill.second = s;
            girlObj.skill.thirdly = t;
            // 返回定製好的妹子(對象)
            return girlObj;
        };  
        //如今開始製造妹子
        
        var gA = middleGirlPlant("woman","洗衣","作飯","LOL");
        console.log(gA.skill.first+" | "+gA.skill.second+" | "+gA.skill.thirdly);
        // "洗衣 | 作飯 | LOL"
        
        var gB = middleGirlPlant("man","Java","C","JavaScript");
        console.log(gB.skill.first+" | "+gB.skill.second+" | "+gB.skill.thirdly);            
        // "Java | C | JavaScript"

看,咱們的妹子不但知足自定義屬性方法,而且這些屬性方法並不保存在每一個妹子對象上,而是在她們的工廠中,只要須要隨時能夠調用它們。(好吧這裏實在抽象不起來了)prototype

每一個經過middleGirlPlant建立的對象都有本身的sexskillbehaviorA()behaviorB()behaviorC()屬性,方法,這些屬性方法是由每一個對象裏的prototype引用的對象提供的,每一個對象的隱藏連接都指向惟一共享原型,其中包含了上面所述的那些屬性方法。不過還有一個小小缺陷。咱們使用了兩個全局變量middleGirlPlantprotoMGP。若是有一個就更好了,這樣咱們的原型做爲函數的一個屬性(對象)。接下來,就是引出new這個構造器了。


JavaScript中的每一個函數對象都自動包含一個prototype屬性,prototype是函數兩個預約義屬性中的第二個,第一個length。只要函數一經定義,它的prototype屬性就會被初始化爲一個全新對象。(這個全新對象有本身的一個屬性,叫作constructor)。

// 建立構造函數highGirlfactory
        function highGirlfactory(s) {
            this.sex = s;
            var test = "哇哈哈,我有女友啦!!!!";
            return test;
        };
        // 構造函數的prototype(原型)
        highGirlfactory.prototype.behaviorA = function (a,b,c) {
            this.first = a;
            this.second = b;
            this.thirdly = c;
        };
        // 構造函數的prototype(原型)
        highGirlfactory.prototype.behaviorB = function () {
            console.log("我會:"+this.first+this.second+this.thirdly);
        };

        // GirlA對象
        var GirlA = new highGirlfactory("woman");
        GirlA.behaviorA("Ax","Ay","Az");
        GirlA.behaviorB();                        // "我會:AxAyAz"
        // GirlB對象
        var GirlB = new highGirlfactory("woman");
        GirlB.sex = "SEX";
        console.log(GirlB.sex)                    // "SEX"
        // ---
        var Test = highGirlfactory();
        alert(Test);                              // "哇哈哈,我有女友啦!!!!"
        // 原型鏈
        alert(highGirlfactory.prototype.constructor===highGirlfactory)    // true
        alert(GirlA.__proto__===highGirlfactory.prototype)                // true

圖片描述

就不廢話了.....當你使用new操做符,就無需明確連接(Object.create)原型,也無需返回新建立的對象。當你在函數調用以前加上了new時,會發生什麼?引自《JavaScript程序設計》《JavaScript編程全解》設計

  • 調用構造函數new表達式的值是(被生成的)對象的引用。經過new表達式調用的構造函數內的this引用,引用了(被新生成的)對象。

見人說人話,見鬼說鬼話:)。【這裏要注意的是,構造函數也是函數!因此它也可當普通函數使用喲(看最後代碼)】。咱們調用new表達式後,會隱式生成一個新對象,可是對象不是賦值的,是引用的,因此說完就是生成了一個新對象的引用,代碼中就是var GirlA = new highGirlfactory("woman")將這個引用賦值給了變量GirlA。而後!構造函數裏的(this引用)引用了新對象,嗯這樣斷句應該沒錯@_@,這裏要提到一個以前沒說清楚的知識,this這個功能全稱叫this引用,咱們爲了形象點說指向。意思就是this指向的是新對象,接收方對象就是那個新生成的對象。so.......

  • 這裏先抄點板書,全部的函數(對象)都支持一種成爲原型鏈的功能。使用原型鏈有兩個前提:

1. 全部的函數(對象)都具備名爲prototype的屬性(這個屬性引用的對象成爲prototype對象)。

2. 全部的對象都含有一個(隱藏的)連接,用以指向在對象生成過程當中所使用的構造函數(Function對象)的prototype對象。


  • 知足上面後,便有了咱們的原型鏈。其中對象對屬性的讀取(以及對方法的調用)是按照如下順序查找的:

  • 對象自身的屬性

  • 隱式連接所引用的對象(構造函數prototype對象)的屬性

  • 上面的對象的隱式連接所引用的對象的屬性

  • 反覆按第三項的規則查找直至所有查找完畢,終點是Object.prototype對象。

因此,我在 GirlB.sex = "SEX";這裏從新建立一個鍵值對,在console.log(GirlB.sex)時,因爲自身屬性就存在這個鍵值對,不會在搜索到原型裏的sex屬性。

關於Object.prototype我理解不是很深,到時也會在研究一下。code


  • 對於原型鏈(_proto_)這個玩意兒,他就是那個神祕的隱式連接,在new生成的新對象中,裏面的_proto_引用的對象就是原型對象。

    GirlA.__proto__===highGirlfactory.prototype對象

  • 構造函數的prototype引用的對象裏還有一個constructor屬性,這個東東引用的是構造函數,沒錯就是本身找本身。書中是這麼寫的:

    highGirlfactory.prototype.constructor===highGirlfactory

能夠經過使用對象的constructor屬性來從對象處獲取其構造函數。若是能獲知對象的構造函數,也就可以知道該對象的原型繼承狀況了,因而即可以瞭解這個對象的一部分操做。

constructor屬性不是對象的以前屬性,而是經過原型鏈查找到的屬性。

嗯,不懂~,先暫時理解爲獲取構造函數吧(弄懂回來補充)

相關文章
相關標籤/搜索