今天总结了下javascript关于原型继承和对象创建方面的东西,因为javascript的原型继承在使用传统面向对象语言开发的同学看来比较怪异,原型继承确实比传统OOP语言的继承理解和运用起来困难一些,当然个人觉得传统OOP的继承相对比较简单,因为中规中矩。
下面逐个的用示例说明javascript中对象创建方式,专业一点叫什么模式,主要有直接单个创建;工厂模式;提出方法类函数公用方式;构造函数模式;构造函数+原型方式;使用原型本质的方式构建(这种受过李站的《悟透javascript》一书的影响)。下面一边示例一边总结每种方式/模式的优点和缺点。还是那句话,哪位看官觉得我哪里说的错误或者片面,留言讨论,tks!
1.逐个创建,这种方式不必说了,做js开发的我想没有人会用这种方式来创建对象。缺点一目了然,单个创建,占用内存大,繁琐,纯粹成了人肉工作,那我们的编程还有什么乐趣
1 /* 2 var people1 = new Object(); 3 people1.name = ‘wangming’; 4 people1.say = function () { 5 alert(people1.name); 6 }people1.say();var people2 = new Object(); 7 people2.name = ‘wanghua’; 8 people2.say = function () { 9 alert(people2.name); 10 }people2.say(); 11 */
2.工厂模式,稍微的封装了,但是缺陷很大,每个对象的构造函数都是function Object(),无从知道对象之间的关系;每次创建都会单独生成一份实例,对象的方法应该共有一个,这里却是每次都单独创建,占用多余的内存,不是我们想要的。
// 工厂模式/*function CreatPerson (name) { var o = new Object(); o.name = name; o.say = function () { alert(this.name); } return o; }var people1 = CreatPerson(‘wangming’); var people2 = CreatPerson(‘wanghua’);people1.say(); people2.say();*/
3.针对工厂模式的缺点,将对象的方法提到外面单独定义,在对象内部引用之。虽然克服了上面的缺点,但却引出了俩个问题,1看起来没有封装,不是一个整体嘛,这显然不是OOP开发所希望看到的;2实际上确实没有封装,因为在外面定义的函数是属于window对象的方法,在外部也是可以访问的,那我们对象内部的方法怎么可以让在外部直接就访问呢,这种创建方式也不是理想的。
// 提出引用的属性和方法在外单独定义 /* function People (name) { this.name = name; this.say = say; }function say () { alert(this.name); }var people1 = new People(‘wangming’); var people2 = new People(‘wanghua’);people1.say(); people2.say();say();*/
/* function People (name) { this.name = name; this.say = function () { alert(this.name); } }var people1 = new People(‘wangming’); var people2 = new People(‘wanghua’);people1.say(); people2.say(); */
5.构造函数+原型配合,这种方式解决了上面的缺点,却是封装性不够,不是最为理想的方式。
// 构造函数+原型 但这种方式显得不够封装 /* function People (name) { this.name = name; }People.prototype.say = function () { alert(this.name); }var people1 = new People(‘wangming’); var people2 = new People(‘wanghua’);people1.say(); people2.say(); */
6.通过上面5个步骤,还未有最优的解决方案。先看下javascript中构造函数创建对象的本质,同时看看javascript的原型继承,再去寻找。
在javascript中,个人认为函数就是对象的构造器,同时函数也是对象中的一种,函数又是javascript中的功能执行块……总之函数是至关重要的,呵呵。其实我们在写一个var obj = new Object()的时候javascript实际上这么走的,第一先创建一个对象var 0bj = {};第二将这个对象的内部属性[[prototype]](火狐下可以写作_proto_且是可以在javascript程序中访问到)指向构造函数的原型prototype;第三使用这个对象进行对象冒充调用构造函数。如此一来,我们可以清楚的看到,构造函数执行完毕,我们新创建的对象再也没有和这个构造函数有半毛钱关系,而是与构造函数的原型关系密切,对象的内部属性_proto_指向构造函数的原型prototype。这就是javascript的原型继承。
总结一张图,着重说明javascript的原型继承,我想这个理解透彻后应该不再觉得javascript的原型继承很怪异了。
// 构造函数创建对象的实质: /* function People (name) { this.name = name; this.say = function () { alert(this.name); } }var o = {}; o.__proto__ = People.prototype; //firefox 内置prototype属性暴漏出来写作__proto__ // o.[[prototype]] = People.prototype; //其他浏览器未暴漏 写作[[prototype]] People.call(o, name); */
7.上面是分析了构造函数的本质和javascript原型继承,那我们就使用原型继承来进行对象的创建,下面的这个示例虽然能够执行函数,却无法访问到内部的属性。因为没有执行内部相当于构造函数的方法。
// 使用原型本质方式构建对象1 能够执行函数 却无法访问属性 /* var People = { Create : function (name) { this.name = name; }, say : function () { console.log(this.name); } };function OnePeople (name) {}OnePeople.prototype = People;var people1 = new OnePeople(‘wangming’);people1.say(); */
8.结合构造函数本质以及javascript原型继承本质这么来进行对象的创建。首先我们创建一个对象作为原型。然后使用构造函数作为空壳将要创建的对象实例与原型对象联系起来。
var People = { // 构造函数 // constructor : New, //指定对象的构造函数 字面量方式默认构造函数为Object Create : function (name) { this.name = name; }, // 方法 say : function () { console.log(this); } };function New (obj, aParams) { function _new () { obj.Create.apply(this, aParams); };_new.prototype = obj;return new _new(); }var people1 = New(People, [‘wangming‘]); people1.say(); alert(People.constructor == Object); alert(people1.constructor == Object); */
至此javascript对象的创建及原型继承的研究分享完毕,这里只涉及javascript这门基于对象的面向对象语言关于对象的创建的实际操作及原型继承,暂时先写到这里,稍后再分享javascript作为OOP编程关于继承 多态详细的心得和示例。
文章来自w3cshow前端博客