原型链实现继承的几种模式
2016-07-02 13:31
337 查看
原型链很强大,可以用来实现继承,但也存在一些问题。其中最主要的问题是来自包含引用类型值的原型。看如下例子:
我们看到Sub()的两个实例,由于Sub.prototype继承了Super(),因此这两个实例实际上是共享了Super的属性和方法,因此colors是Sub所有实例共享的属性,而这并不是我们的意图。
第二个问题是:在创建子类型的实例时,不能向超类型的构造函数中传递函数。
1.来看看第一个方法借用构造函数:
在子类中我们用call方法在当前环境下(即Sub下)调用了超类的构造函数,并在子类中添加自己的属性,虽然解决了原型链的问题,但是又有新问题就是:函数不能复用。
2.接下来我们看看最常用的组合继承方法:
思路是:使用原型链实现对原型链实现对原型属性和方法的继承,通过借用构造函数来实现对实例属性的继承,这样既通过了在原型上定义的方法实现了继承,又可以保证每个实例有自己的属性。
组合继承了避免了原型链和构造函数的缺陷,是最常用的继承模式。
3.原型式继承
在object函数内创建一个临时性的构造函数,然后将传入的对象作为这个构造函数的原型,最后返回这个临时对象的实例。看个例子:
所有object创建的对象都会共享object()方法传入对象的方法和属性,采用这种方法可以不必创建构造函数,而只想让一个对象与另一个对象保持类似的情况下使用,不过引用类型值得属性始终都会共享相应的值,和原型模式一样。
ES5通过新增了Object.create()方法规范了原型式继承。
4.前面说过组合继承是最常用的继承模式,不过它也有自己的不足:无论在什么情况下,都会调用两次超类型构造函数:一次是在创建子类原型的时候,一次是在子类构造函数内部。
例如:
还好有解决办法,几声组合式继承:不必为子类型的原型而调用超类型的构造函数,我们只是需要超类型的一个副本,就是使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型。:
第一行代码是创建超类型原型的一个副本,第二行代码是为创建的副本添加constructor属性,弥补因重写原型而失去默认的constructor属性。最后一行代码是将副本赋值给子类型的原型。
例如:
寄生组合式继承只调用一次Super构造函数,并且避免了Sub.prototype上面创建不必要的、多余的属性。普遍认为是引用类型最理想的继承方式。
function Super() { this.colors = ['red', 'blue', 'green']; } function Sub() { } Sub.prototype = new Super(); var instance1 = new Sub(); instance1.colors.push('black'); alert(instance1.colors); //red,blue,green,black var instance2 = new Sub(); alert(instance2.colors); //red,blue,green,black
我们看到Sub()的两个实例,由于Sub.prototype继承了Super(),因此这两个实例实际上是共享了Super的属性和方法,因此colors是Sub所有实例共享的属性,而这并不是我们的意图。
第二个问题是:在创建子类型的实例时,不能向超类型的构造函数中传递函数。
1.来看看第一个方法借用构造函数:
function Super(name) { this.name = name; } function Sub() { Super.call(this, 'Shen'); this.age = 30; } var instance = new Sub(); alert(instance.name); //Shen alert(instance.age); //30
在子类中我们用call方法在当前环境下(即Sub下)调用了超类的构造函数,并在子类中添加自己的属性,虽然解决了原型链的问题,但是又有新问题就是:函数不能复用。
2.接下来我们看看最常用的组合继承方法:
思路是:使用原型链实现对原型链实现对原型属性和方法的继承,通过借用构造函数来实现对实例属性的继承,这样既通过了在原型上定义的方法实现了继承,又可以保证每个实例有自己的属性。
function Super(name) { this.name = name; this.colors = ['red', 'blue', 'green']; } Super.prototype.sayName = function() { alert(this.name); }; function Sub(name, age) { Super.call(this, name); this.age = age; } //继承方法 Sub.prototype = new Super(); Sub.prototype.constructor = Sub; Sub.prototype.sayAge = function() { alert(this.age); } var instance1 = new Sub('Shen', 30); instance1.colors.push('black'); alert(); //red,blue,green,black instance1.sayName(); //shen instance1.sayAge(); //30 var instance2 = new Sub('Han', 22); alert(instance2.colors); //red,blue,green instance2.sayName(); //Han instance2.sayAge(); //22
组合继承了避免了原型链和构造函数的缺陷,是最常用的继承模式。
3.原型式继承
function object(o) { function F() {} F.prototype = o; return new F(); }
在object函数内创建一个临时性的构造函数,然后将传入的对象作为这个构造函数的原型,最后返回这个临时对象的实例。看个例子:
var person = { name: 'Shen', friends: ['Wang', 'Han', 'Li'] }; var anotherPerson = object(person); anotherPerson.name = 'Han'; anotherPerson.friends.push('Row'); var otherPerson = object(person); otherPerson.name = 'Col'; otherPerson.friends.push('zzz'); alert(person.friends); //Wang,Han,Li,Row,zzz
所有object创建的对象都会共享object()方法传入对象的方法和属性,采用这种方法可以不必创建构造函数,而只想让一个对象与另一个对象保持类似的情况下使用,不过引用类型值得属性始终都会共享相应的值,和原型模式一样。
ES5通过新增了Object.create()方法规范了原型式继承。
4.前面说过组合继承是最常用的继承模式,不过它也有自己的不足:无论在什么情况下,都会调用两次超类型构造函数:一次是在创建子类原型的时候,一次是在子类构造函数内部。
例如:
var instance = new Sub('Jack', 30);第一次调用Super是
Sub.prototype = new Super();第二是
Super.call(this, name);第二次调用创建的两个实例属性会屏蔽掉第一次调用生成的实例属性。
还好有解决办法,几声组合式继承:不必为子类型的原型而调用超类型的构造函数,我们只是需要超类型的一个副本,就是使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型。:
function inheritPrototype(sub, super) { var prototype = object(super.prototype); prototype.constructor = sub; sub.prototype = prototype; }
第一行代码是创建超类型原型的一个副本,第二行代码是为创建的副本添加constructor属性,弥补因重写原型而失去默认的constructor属性。最后一行代码是将副本赋值给子类型的原型。
例如:
function Super(name) { this.name = name; this.colors = ['red', 'blue', 'green']; } Super.prototype.sayName = function() { alert(this.name); }; function Sub(name, age) { Super.call(this, name); this.age = age; } inheritPrototype(Sub, Super); Sub.prototype.sayAge = function() { alert(this.age); };
寄生组合式继承只调用一次Super构造函数,并且避免了Sub.prototype上面创建不必要的、多余的属性。普遍认为是引用类型最理想的继承方式。
相关文章推荐
- Mootools 1.2教程 函数
- autoit InputBox 函数
- 文件遍历排序函数
- PostgreSQL教程(三):表的继承和分区表详解
- 关于C#中排序函数的总结
- Oracle 函数大全[字符串函数,数学函数,日期函数]第1/4页
- ASP下经常用的字符串等函数参考资料
- PostgreSQL教程(五):函数和操作符详解(1)
- DOS批处理 函数定义与用法
- asp Chr 函数 数字转字母的方法
- Lua中的函数精讲笔记
- Lua中的闭合函数、非全局函数与函数的尾调用详解
- Lua中调用C++函数示例
- Lua面向对象之类和继承浅析
- Lua实现split函数
- Lua常用时间函数使用实例
- Lua函数与字符串处理简明总结
- Lua学习笔记之表和函数
- Lua中实现sleep函数功能的4种方法
- Lua函数用法研究