15分钟深入了解JS继承分类、原理与用法(2)

组合继承(combination inheritance),有时候也叫做伪经典继承,其背后的思路是使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。其继承代码大致如下:

function SuperType(name){ this.name = name; this.colors = ["red", "blue", "green"]; } SuperType.prototype.sayName = function(){ alert(this.name); }; function SubType(name, age){ SuperType.call(this, name); // 继承属性 this.age = age; // 先继承,后定义新的自定义属性 } SubType.prototype = new SuperType(); // 继承方法 Object.defineProperty( SubType.prototype, "constructor", { // 先继承,后定义新的自定义属性 enumerable: false, // 申明该数据属性——constructor不可枚举 value: SubType }); SubType.prototype.sayAge = function(){ // 先继承,后定义新的自定义方法 alert(this.age); }; var instance1 = new SubType("Nicholas", 29); instance1.colors.push("black"); alert(instance1.colors); // "red, blue, green, black" instance1.sayName(); // "Nicholas" instance1.sayAge(); // 29 var instance2 = new SubType("Greg", 27); alert(instance2.colors); // "red, blue, green" instance2.sayName(); // "Greg"; instance2.sayAge(); // 27

优点:

融合了原型链继承和借用构造函数继承的优点,避免了他们的缺陷;

instanceOf() 和 isPrototypeOf() 也能够用于识别基于组合继承创建的对象。

缺点:

在实现继承的时候,无论什么情况下,都会调用两次超类型构造函数:一次是在创建子类型原型的时候,另一次是在子类型构造函数内部。子类型的原型最终会包含超类型对象的全部实例属性,但我们不得不在定义子类型构造函数时重写这些属性,因为子类型的原型中最好不要有引用类型值。但这在实际中,就造成了内存的浪费。

4. 原型式继承

原型式继承所秉承的思想是:在不必创建自定义类型的情况下,借助原型链,基于已有的对象创建新对象。这其中会用到 Object.create() 方法,让我们先来看看该方法的原理代码吧:

function object(o){ function F(){} F.prototype = o; return new F(); }

从本质上讲,object() 对传入其中的对象执行了一次浅复制。

ECMAScript 5 想通过 Object.create() 方法规范化原型式继承。这个方法接受两个参数:一参是被用来作为新对象原型的一个对象;二参为可选,一个为新对象定义额外属性的对象,这个参数的格式与 Object.defineProperties() 的二参格式相同。以下为原型式继承的示例代码:

var person = { name: "Nicholas", friends: ["Shelby", "Court", "Van"] }; var anotherPerson = Object.create(person, { name: { value: "Greg" } }); anotherPerson.friends.push("Rob"); alert(anotherPerson.name); //"Greg" var yetAnotherPerson = Object.create(person); yetAnotherPerson.name = "Linda"; yetAnotherPerson.friends.push("Barbie"); alert(person.friends); //"Shelby,Court,Van,Rob,Barbie"

缺点:所有实例始终都会共享源对象中的引用类型属性值。

5. 寄生式继承

寄生式(parasitic)继承的思路与寄生构造函数和工厂模式类似,即创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再像真的是它做了所有工作一样返回对象。下面来看看,寄生式继承的示例代码:

function object(o){ function F(){} F.prototype = o; return new F(); } function createAnother(original){ var clone = object(original); // 通过调用函数创建一个新对象 clone.sayHi = function(){ // 以某种方式来增强这个对象 alert("hi"); }; return clone; // 返回这个对象 }

该继承方式其实就是将原型式继承放入函数内,并在其内部增强对象,再返回而已。就相当于原型式继承寄生于函数中,故而得名寄生式继承。

前面示范继承模式时使用的 object() 函数不是必需的;任何能够返回新对象的函数都适用于此模式。

缺点:不能做到函数复用,效率低下。

6. 寄生组合式继承(推荐)

寄生组合式继承,即通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。其背后的基本思路是:不必为了指定子类型的原型而调用超类型的构造函数,我们所需要的无非就是超类型原型的一个副本而已。本质上,就是使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型。以下为寄生组合式继承的实例代码:

function object(o){ function F(){} F.prototype = o; return new F(); } function inheritPrototype(subType, superType){ var prototype = object(superType.prototype); //创建对象 prototype.constructor = subType; //增强对象 subType.prototype = prototype; //指定对象 } function SuperType(name){ this.name = name; this.colors = ["red", "blue", "green"]; } SuperType.prototype.sayName = function(){ alert(this.name); }; function SubType(name, age){ SuperType.call(this, name); // 继承属性 this.age = age; } inheritPrototype(SubType, SuperType); // 继承原型方法 SubType.prototype.sayAge = function(){ alert(this.age); };

优点:

只调用一次超类型构造函数;

避免了在子类原型上创建不必要的、多余的属性,节省内存空间;

原型链还能正常保持不变,也就意味着能正常使用 instanceOf 和 isPrototypeOf() 进行对象识别。

寄生组合式继承是最理想的继承方式。

7. ES6 中的 extend 继承

来看看 ES6 中 extend 如何实现继承的示例代码:这一块的内容解释,我阅读的是这篇文章,欲知原文,请戳这里~

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

转载注明出处:http://www.heiqu.com/6515e5a3620f673ed67ea15e0d5c9f1b.html