摘要:所有的对象都含有一个隐藏的链接,用以指向在对象生成过程中所使用的构造函数对象的对象。如果能获知对象的构造函数,也就能够知道该对象的原型继承情况了,于是便可以了解这个对象的一部分操作。嗯,不懂,先暂时理解为获取构造函数吧弄懂回来补充
此篇文章是看完《 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) // trueYEAH!!我有对象啦,嗯哼一看,卧槽广大群众义愤填膺了,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是什么我们就要继续了。
想一想古代的皇帝,三宫六妾的,想想现在的一夫一妻制,哇哈哈,崩溃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 | 我会:做家务 | 按摩 | ")); // :)
瞧,是不是不用一个一个的写啦?而且我还高度定制了一些"功能",比如......咳咳,我是异性恋,但是男生力气大做家务也快不是?
但是!还是有不足的地方,每个妹子出生就自带这些技能和属性了,可是有些妹子是不愿学习某些技能的,比如一些方法,一些属性也不想表露出来,怎么办?
这段代码看上去没问题,却有一个严重缺陷,每次创建一个对象,都另行创建了额外的属性、方法。在创建多个对象时,会浪费大量的内存来保存这些函数方法的冗余副本,这是很糟糕的事情,因为内存资源是有限的。当脚本耗尽内存就会崩溃。但是,我们还有一个方法来解决它们!
// 工厂设置制造车间,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"
看,我们的妹子不但满足自定义属性方法,并且这些属性方法并不保存在每个妹子对象上,而是在她们的工厂中,只要需要随时可以调用它们。(好吧这里实在抽象不起来了)
每个通过middleGirlPlant创建的对象都有自己的sex、skill、behaviorA()、behaviorB()、behaviorC()属性,方法,这些属性方法是由每个对象里的prototype引用的对象提供的,每个对象的隐藏链接都指向唯一共享原型,其中包含了上面所述的那些属性方法。不过还有一个小小缺陷。我们使用了两个全局变量middleGirlPlant、protoMGP。如果有一个就更好了,这样我们的原型作为函数的一个属性(对象)。接下来,就是引出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我理解不是很深,到时也会在研究一下。
对于原型链(_proto_)这个玩意儿,他就是那个神秘的隐式链接,在new生成的新对象中,里面的_proto_引用的对象就是原型对象。
GirlA.__proto__===highGirlfactory.prototype
构造函数的prototype引用的对象里还有一个constructor属性,这个东东引用的是构造函数,没错就是自己找自己。书中是这么写的:
highGirlfactory.prototype.constructor===highGirlfactory
可以通过使用对象的constructor属性来从对象处获取其构造函数。如果能获知对象的构造函数,也就能够知道该对象的原型继承情况了,于是便可以了解这个对象的一部分操作。 constructor属性不是对象的之前属性,而是通过原型链查找到的属性。嗯,不懂~,先暂时理解为获取构造函数吧(弄懂回来补充)
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/82952.html
摘要:写在前面写这篇文章计划想了很久,终于付诸行动。此时所指向的空间变成了一个,因为在中函数名相同的后面的会覆盖前面。特别注意函数的传入参数与调用无关如果调用传入两个参数,而形参只有一个,就只会匹配一个参数。 写在前面 : 写这篇文章计划想了很久,终于付诸行动。一直不知道该从哪里写,或许文章有错误的地方,如果您发现了文章的错误,请指正!谢谢!好句欣赏:人生为棋,我愿为卒行动虽慢可谁曾见我退后...
摘要:避免脆弱的基类问题。红牌警告没有提到上述任何问题。单向数据流意味着模型是单一的事实来源。单向数据流是确定性的,而双向绑定可能导致更难以遵循和理解的副作用。原文地址 1. 你能说出两种对 JavaScript 应用开发者而言的编程范式吗? 希望听到: 2. 什么是函数编程? 希望听到: 3. 类继承和原型继承的不同? 希望听到 4. 函数式编程和面向对象编程的优缺点? ...
摘要:函数式编程一开始我并不理解。渐渐地,我熟练掌握了使用函数式的方法去编程。但是自从学习了函数式编程,我将循环都改成了使用和来实现。只有数据和函数,而且因为函数没有和对象绑定,更加容易复用。在函数式的中,这些问题不复存在。 译者按: 当从业20的JavaScript老司机学会函数式编程时,他扔掉了90%的特性,也不用面向对象了,最后发现了真爱啊!!! 原文: How I rediscov...
摘要:构造函数通过原型继承了构造函数和原型,这就形成了一个链条,通俗的讲就是原型链继承。而且方法只能冒充构造函数里面的属性和方法而无法冒充原型对象里面的属性和方法还有最大的问题就是重复使用。 前言: 写到这里,差不多就把OOP完结了,写了几篇OOP的文章,但是只是略懂皮毛,可能深入的OOP还有很多,但是我感觉写到这里也算是差不多完结了。 继承 继承是面向对象比较核心的概念,其他语言可能实现...
摘要:前面一段时间对和两者的关系感到比较困惑我使用的动态语言揉合太多范式在这一点上很难做出明确透彻的区分不过经过这段时间琢磨相对之前感觉要好一些了有了一些自己的想法后面自己的部分会有不少没有验证的地方所以应该当成感想来看需要说明我的经验来自动态语 前面一段时间对 FP 和 OOP 两者的关系感到比较困惑 我使用的动态语言揉合太多范式, 在这一点上很难做出明确透彻的区分 不过经过这段时间琢磨相...
阅读 1534·2021-11-24 09:39
阅读 1003·2021-11-22 15:11
阅读 2145·2021-11-19 11:35
阅读 1611·2021-09-13 10:37
阅读 2424·2021-09-03 10:47
阅读 2106·2021-08-30 09:47
阅读 1608·2021-08-20 09:39
阅读 2883·2019-08-30 14:13