在Javascript中实现继承主要是依靠原型链来实现的;其基本思想是通过原型实现一个引用类型继承另一个引用类型的属性和方法。
1.原型链
1 2 3 4 5 6 7 8 9 10 11 12 13
| function SuperType(){ this.super = 'SuperType' } SuperType.prototype.sayType = function(){ alert(this.super) }
function SubType(){ this.sub = 'SubType' } SubType.prototype = new SuperType()
|
优点: 写法方便简洁,容易理解
缺点:对象实例共享所有继承的属性和方法;无法向父类构造函数传参
2.借用构造函数
子类型构造函数的内部调用父类型构造函数
1 2 3 4 5 6 7 8 9 10
| function SuperType(){ this.colors = ["red", "blue", "green"]; } function SubType(){ SuperType.call(this); } const c1 = new SubType(); c1.colors.push("black"); const c2 = new SubType();
|
优点:解决了原型链继承不能传承和父类原型共享的问题
缺点:借用构造函数的方法都是在构造函数中定义的,因此无法实现函数复用;在父类的原型中定义的方法,对于子类型而言也是不可见的,所有的类型都只能使用构造函数模式
3.组合继承
将原型链和借用构造函数的技术组合到一块,从而发挥二者之长的一种继承模式。其背后的思路是使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。这样,既通过在原型上定义方法实现了函数复用,又能够保证每个实例都有它自己的属性。所以组合继承避免了原型链和借用构造函数的缺陷,融合了它们的优点,成为 JavaScript 中最常用的继承模式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| function SuperType(name){ this.name = name; this.colors = ["red", "blue", "green"]; } SuperType.prototype.sayName = function(){ alert(this.name); }
function SubType(name){ SuperType.call(this, name); }
SubType.prototype = new SuperType();
var p1 = new SubType("Nicholas", 29); instance1.colors.push("black"); alert(p1.colors); p1.sayName();
var p2 = new SubType("Greg", 27); alert(p2.colors); p2.sayName();
|
优点:解决了原型链继承和借用构造函数继承的问题
缺点:无论在什么情况下,都会调用两次父类构造函数(一次在创建子类型的时候,另一次在子类构造函数内)
4.原型式继承
将需要被继承的对象挂载到函数内部新创建的构造函数原型上并返回该构造函数的实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
|
function object(o,o2={}){ var F = function(){ for (const key in o2) { for (const key2 in o2[key]) { this[key] = o2[key][key2] } } }; F.prototype = o return new F() }
var person = { name: 'no-Name', friend:[1,2,3], sayName:function(){ alert(this.name) } } let a1 = object(person)
|
ECMAScript 5 通过新增 Object.create()方法规范化了原型式继承。这个方法接收两个参数:一个用作新对象原型的对象和(可选的)一个为新对象定义额外属性的对象
1 2 3 4 5 6 7 8 9 10 11
| var person = { name: "Nicholas", friends: ["Shelby", "Court", "Van"] }; var anotherPerson = Object.create(person); anotherPerson.name = "Greg"; anotherPerson.friends.push("Rob"); var yetAnotherPerson = Object.create(person); yetAnotherPerson.name = "Linda"; yetAnotherPerson.friends.push("Barbie"); alert(person.friends);
|
5.寄生式继承
寄生式(parasitic)继承是与原型式继承紧密相关的一种思路 ,其与工厂模式类似,即创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再像真地是它做了所有工作一样返回对象
1 2 3 4 5 6 7 8 9 10 11 12 13
| function createAnother(original){ var clone = object(original) clone.sayHi = function(){ alert('hi!') } return clone } var person = { name: "Jiangwen", friends: ["A", "B", "C"] }; var anotherPerson = createAnother(person); anotherPerson.sayHi();
|
在主要考虑对象而不是自定义类型和构造函数的情况下,寄生式继承也是一种有用的模式。前面示范继承模式时使用的 object()函数不是必需的;任何能够返回新对象的函数都适用于此模式。
6.寄生组式继承
所谓寄生组合式继承,即通过借用构造函数来继承属性,通过原型链的混成形式来继承方法 ; 本质上,就是使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型。
1 2 3 4 5
| function inheritPrototype(subType, superType){ var prototype = object(superType.prototype); prototype.constructor = subType; subType.prototype = prototype; }
|
7. ES6的Class类继承
class通过extends关键字实现继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| class Animal { constructor (kind) { this.kind = kind } getKind() { return '我是一只' + this.kind } }
class Cat extends Animal { constructor (name) { super('猫') this.name = name } getCatInfo() { console.log(this.name + ':' + super.getKind() + '...喵喵喵...'); } }
const c = new Cat('没头脑') c.getCatInfo()
|
优点:语法简单易懂,操作简便
缺点:浏览器没有全面兼容class关键字