javascript笔记:javascript前世续篇(从javascript起源谈下它的继承)
悲催悲催悲催啊!!!我的《javascript笔记:javascript的前世,至于今生嘛地球人都知道了哈 》,在我不小心的操作里,后半部分丢掉了,哎,博客使用还是不熟练,真悲剧!只得在写一篇,把丢掉的内容补上,该文的下半部分是通过Brandan Eich设计javascript的思路来讲讲javascript的继承机制。
补写下半部分,我的提提我这两篇的博文的原始资料来源,大伙可以在百度里搜索下阮一峰老师的博客,我虽然尽全力想用自己的语言表达自己的理解但还是摆脱不了他的博客里的叙述,阮一峰老师的博客写的不错,虽然博文数量不多,但是讲解十分的清楚,大伙有空可以看看。
下面我就按照他的文章讲解下Brendan Eich是如何设计javascript的继承机制的。
我第一次接触javascript面向对象编程时候,是忍住刺痛和模糊看完的,那时只是猎奇,坚持看完也只不过是为了要保持良好的学习态度,而且当时对javascript有误解,觉得javascript面向对象编程是代码爱好者的游戏,使用价值不大,但是当我接触到一些优秀的javascript源码后我才发现,javascript面向对象编程用途是如此之多令我叹为观止,最后总结出一个结论:最好的javascript代码都应该是面向对象的。
那么javascript里是如何实现继承的啊?javascript的继承机制如何啊?
首先javascript里面没有"子类"和"父类"的概念,也没有"类"(class)和"实例"(instance)的区分,全靠一种很奇特的"原型链"(prototype chain)模式,来实现继承。
我首先要讲的是网景公司在发明javascript为javascript设计的目标,其中很重要的两点:
1. 简易版的java;
2. 简易,简易还是简易。
Brendan Eich设计javascript的时候引入了java一个非常重要的概念:一切皆对象。既然javascript里面有了对象,那么设不设计继承就是困扰Brendan Eich的一个问题,如果真是要设计一个简易的语言其实可以不要继承机制,继承属于专业的程序员,但是javascript里那么多的对象,如果没有一种机制,他们之间将如何联系了,这必然会对编写程序的可靠性带来很大的问题,但是引入了继承又会使用javascript变成了完整的面向对象的语言,从而提高了它的门槛,让很多初学者望而却步,折中之下,Brendan Eich还是选择设计继承,但绝不是标准的继承(说道这里我想起了同样使用EMCAScript标准设计的语言actionscript,它里面就有很完整的继承,做起来很惬意,我常想这是不是javascript以后的趋势,说不定哪天javascript会变的搄更完美写了?)。折中是指Brendan Eich不打算引入类(class),这样javascript至少看起来不像面向对象的语言了,那么初学者就不会望而却步了(这是欺骗啊,进来后倒腾死你,这就是所谓的关门打狗了)。
Brendan Eich思考之后,决定借鉴C++和java的new命令,将new命令引入了javascript,在传统的面向对象的语言里,new 用来构造实例对象,new 会调用构造函数,但是传统面向对象的语言new 后面的是类,内部机制是调用构造函数(constructor),而Brendan Eich简化了这个操作,在javascript里面,new 后面直接是构造函数,如是我们可以这么写一个Person类:
function Person(name) { this.name = name; } var per = new Person('Brendan Eich'); console.log(per.name);//Brendan Eich
这样就创建了一个新的实例了。
但是new 有缺陷。用构造函数生成实例对象是无法无法共享属性和方法,例如下面代码:
function Person(name) { this.name = name; this.nation = 'USA'; } var per1 = new Person('Brendan Eich'); var per2 = new Person('IT民工'); per2.nation = 'China'; console.log(per1.nation);//USA console.log(per2.nation);//China
每一个实例对象,都有自己的属性和方法的副本。这不仅无法做到数据共享,也是极大的资源浪费。这个就和我前一篇博文里讲到的javascript工厂模式的缺点一样,过多重复的对象会使得浏览器速度缓慢,造成资源的极大的浪费。
考虑到这一点啊,Brendan Eich决定为构造函数设置一个prototype属性,这个属性都是指向一个prototype对象。下面一句话很重要:
所有实例对象需要共享的属性和方法,都放在这个对象里面;那些不需要共享的属性和方法,就放在构造函数里面。
实例对象一旦创建,将自动引用prototype对象的属性和方法。也就是说,实例对象的属性和方法,分成两种,一种是本地的,另一种是引用的。如是我们可以改写下上面的程序:
function Person(name) { this.name = name; } Person.prototype = {nation:'USA'}; var per1 = new Person('Brendan Eich'); var per2 = new Person('IT民工'); console.log(per1.nation);//USA console.log(per2.nation);//USA
当我们这样写程序时候Person.prototype.nation = ‘China’;所有实例化的类的nation都会变成China。
最后总结下了:由于所有的实例对象共享同一个prototype对象,那么从外界看起来,prototype对象就好像是实例对象的原型,而实例对象则好像"继承"了prototype对象一样。在原博文了阮一峰老师老师最后加了一句“这就是Javascript继承机制的设计思想。”,我不太同意这个说法,我个人认为prototype只是提供了实现javascript继承的一个很方便的途径和手段。
写完这篇博客,得感慨下,软件一定是复杂的,在设计时候就应当难事做,如果还没做之前就想如何简易,如何方便,那就完蛋了,想做套带有简易标语的衣裳很容易,但是里面材料用的不好那还是不好,甚至更不好,我们以后做软件一定要注意,想做出好东西一定要有“如临深渊如履薄冰”的态度,敷衍只是为以后受苦打基础的,当然也可以转移受苦对象,比如我们这些悲惨的程序员啊。