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属性不是对象的以前属性,而是经过原型链查找到的属性。

嗯,不懂~,先暂时理解为获取构造函数吧(弄懂回来补充)

相关文章
相关标签/搜索