JavaScript 继承详解及示例代码(2)

组合继承避免了单方面使用原型链或构造函数来实现继承的缺陷,融合了它们的优点,成为 JavaScript 中最常用的继承模式,但是它也是有缺陷的,组合继承的缺陷会在后面专门提到。

原型式继承(Prototypal Inheritance)

实现思路:借助原型基于已有的对象创建新对象,同时不必因此而创建自定义类型。

为了达到这个目的,引入了下面的函数(obj)

functionobj(o){ functionF(){} F.prototype = o; returnnewF(); } varperson1 = { name: "percy", friends: ['aaa','bbb'] }; varperson2 = obj(person1); person2.name = "zyj"; person2.friends.push('ccc'); console.log(person1.name);// percy console.log(person2.name);// zyj console.log(person1.friends);// ["aaa", "bbb", "ccc"] console.log(person2.friends);// ["aaa", "bbb", "ccc"] ECMAScript 5 通过新增 Object.create() 方法规范化了原型式继承。在传入一个参数的情况下, Object.create() 和 obj() 方法的行为相同。 varperson1 = { name: "percy", friends: ['aaa','bbb'] }; varperson2 =Object.create(person1); person2.name = "zyj"; person2.friends.push('ccc'); console.log(person1.name);// percy console.log(person2.name);// zyj console.log(person1.friends);// ["aaa", "bbb", "ccc"] console.log(person2.friends);// ["aaa", "bbb", "ccc"]

在没有必要兴师动众地创建构造函数,而只想让一个对象与另一个对象保持类似的情况下,可以选择使用这种继承。

寄生式继承(Parasitic Inheritance)

寄生式继承是与原型式继承紧密相关的一种思路。

实现思路:创建一个仅仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再返回对象。

functionobj(o){ functionF(){} F.prototype = o; returnnewF(); } functioncreatePerson(original){// 封装继承过程 varclone = obj(original);// 创建对象 clone.showSomething = function(){// 增强对象 console.log("Hello world!"); }; returnclone;// 返回对象 } varperson = { name: "percy" }; varperson1 = createPerson(person); console.log(person1.name);// percy person1.showSomething(); // Hello world!

寄生组合式继承(Parasitic Combination Inheritance)

先来说说我们前面的组合继承的缺陷。 组合继承最大的问题就是无论什么情况下,都会调用两次父类的构造函数:一次是创建子类的原型的时候,另一次是在调用子类构造函数的时候,在子类构造函数内部又调用了父类的构造函数。

functionFather(name,friends){ this.name = name; this.friends = friends; } Father.prototype.money = "100k $"; Father.prototype.getName = function(){ console.log(this.name); }; functionSon(name,age){ // 继承父类的属性 Father.call(this,name,['aaa','bbb']);// 第二次调用 Father() , 实际是在 new Son() 时才会调用 this.age = age; } // 继承父类原型中的属性和方法 Son.prototype = newFather();// 第一次调用 Father() Son.prototype.constructor = Son;

第一次调用使的子类的原型成了父类的一个实例,从而子类的原型得到了父类的实例属性;第二次调用会使得子类的实例也得到了父类的实例属性;而子类的实例属性默认会屏蔽掉子类原型中与其重名的属性。所以,经过这两次调用, 子类原型中出现了多余的的属性 ,从而引进了寄生组合式继承来解决这个问题。

寄生组合式继承的背后思路是: 不必为了指定子类的原型而调用父类的构造函数,我们所需要的无非就是父类原型的一个副本而已 。

本质上,就是使用寄生式继承来继承父类的原型,然后将结果返回给子类的原型。

functionobj(o){ functionF(){} F.prototype = o; returnnewF(); } functioninheritPrototype(son,father){ varprototype = obj(father.prototype);// 创建对象 prototype.constructor = son; // 增强对象 son.prototype = prototype; // 返回对象 } functionFather(name,friends){ this.name = name; this.friends = friends; } Father.prototype.money = "100k $"; Father.prototype.getName = function(){ console.log(this.name); }; functionSon(name,age){ // 继承父类的属性 Father.call(this,name,['aaa','bbb']); this.age = age; } // 使用寄生式继承继承父类原型中的属性和方法 inheritPrototype(Son,Father); Son.prototype.getAge = function(){ console.log(this.age); }; vars1 =newSon('son1',12); s1.friends.push('ccc'); console.log(s1.friends);// ["aaa", "bbb", "ccc"] console.log(s1.money);// 100k $ s1.getName(); // son1 s1.getAge(); // 12 vars2 =newSon('son2',24); console.log(s2.friends);// ["aaa", "bbb"] console.log(s2.money);// 100k $ s2.getName(); // son2 s2.getAge(); // 24

优点:使子类原型避免了继承父类中不必要的实例属性。

开发人员普遍认为寄生组合式继承是实现基于类型继承的最理想的继承方式。

最后

最后,强烈推荐两篇很硬的文章

Javascript – How Prototypal Inheritance really works
JavaScript's Pseudo Classical Inheritance diagram (需要翻墙)

摘第二篇文章的一张硬图过来:

JavaScript 继承详解及示例代码


看完之后,秒懂原型链,有木有?

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wzfwsg.html