胖蔡说技术
随便扯扯

JavaScript 继承实现

对于面向对象类的语言而言,继承是基础实现之一,也是编程过程中讨论的较多的话题。常见的继承方式分为两种:接口继承和实现继承。前者继承方法签名,后者继承实际方法。由于在JavaScript中没有签名,实现继承称为JavaScript中的唯一继承方法,而JavaScript中的继承也是通过原型链来实现的。更多有关原型与原型链的知识,请阅读《JS 的原型与原型链》,这里就不在重复赘述。

原型与继承

原型与实例的关系可以通过两种方式来确定。第一种方式是通过instanceof操作符,若一个实例的原型链中出现对应的构造函数,则instanceof返回true。如下实例所示:

function SuperType(){
  this.property = true;
}

SuperType.prototype.getSuperValue = function () {
  return this.property;
}

function SubType() {
  this.subproperty =  false;
}

// 通过将SubType的 prototype指向SuperType实例,从而实现继承SuperType的属性和方法
SubType.prototype = new SuperType();

SubType.prototype.getSubValue = function () {
  return this.subproperty;
}

let instance  = new SubType();
console.log(instance.getSuperValue()); // true

原型式继承

通过原型来实现JS的继承,但这种方式存在的缺点就是多个实例共用属性的问题,如下为示例:

function Person(name, age) {
    this.name = [name]
    this.age = age
}
Person.prototype.fun = function () {
    console.log(this.name);
}
function Son(sex) {
    this.sex = sex
}
Son.prototype = new Person('zs', 14);//这样设置导致s1和s2都是一样的name和age,肯定不合理
//不写也不影响,但是原型链有个规则,构造函数的原型.constructor指向构造函数,如果不写会发现Son.prototype.constructor;//Person
Son.prototype.constructor = Son;
let s1 = new Son('男')
let s2 = new Son('女')
s1.name.push('科比')//子类改变父类的引用类型
//导致了s2一起改变
console.log(s1.name);// ['zs', '科比']
console.log(s2.name);// ['zs', '科比']
s1.fun()// ['zs', '科比']
s2.fun()// ['zs', '科比']
// 所以这样的继承,有缺点,不实用

盗用构造函数

盗用构造函数又被称之为对象伪装或者是经典继承,是一种使用call或者bind方式调用父类构造函数,从而避免prototype继承导致的prototype属性在多个实例之间共有的问题。其实现如下:

function Person () {
    this.name = {
        firstName: 'San',
        lastName: 'Zhang'
    };
    this.age = 20;
    this.sayName = function () {
        console.log(this.name.lastName, this.name.firstName);
    }
}

function Student () {
    this.school = 'Tsinghua University';
    Person.call(this);
}

let stu1 = new Student();
let stu2 = new Student();

stu1.name.lastName = 'Li'; //改变了stu1对象实例的name属性

console.log(stu1.name, stu1.age, stu1.school);
console.log(stu2.name, stu2.age, stu2.school);

// { firstName: 'San', lastName: 'Li' } 20 Tsinghua University
// { firstName: 'San', lastName: 'Zhang' } 20 Tsinghua University。stu2的name属性并没有改变!

组合继承

组合继承就是将原型链继承和盗用构造函数继承集成在一起,其示例代码如下:

function Person(name, age) {
    this.name = [name]
    this.age = age
}
Person.prototype.fun = function () {
    console.log(this.name);
}
function Son(name, age, sex) {
    // 通过在子类Son中调用父类构造函数,实现给每个子类单独设置属性
    Person.call(this, name, age)
    this.sex = sex
}
 //通过原型让子类型继承父类型中的方法
Son.prototype = new Person();
Son.prototype.constructor = Son
let s1 = new Son('哈登', 30, '男')
let s2 = new Son('小哈', 21, '男')
s1.fun()//['哈登']
s2.fun()//['小哈']

寄生式继承

是一种基于原型式的继承方式,它通过创建一个仅用于封装继承过程的函数,该函数在内部调用原型式继承创建一个对象,然后增强该对象,最后返回这个对象。其实就是原型式样继承和工厂模式的实现组合。

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

var person = {     
name: "Nicholas",     
friends: ["Shelby", "Court", "Van"] 
}; 
 
var anotherPerson = createAnother(person); 
anotherPerson.sayHi(); //"hi"

寄生式组合继承

即通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。 不必为了指定子类型的原型而调用父类的构造函数,我们所需要的无非就是父类原型的一个副本而已。

function inheritPrototype(subType, superType){
     var prototype = object(superType.prototype); //创建对象
     prototype.constructor = subType; //增强对象
     subType.prototype = prototype; //指定对象
} 

function inheritPrototype(subType, superType){
  var prototype = Object.create(superType.prototype); // 创建对象,创建父类原型的一个副本
  prototype.constructor = subType;                    // 增强对象,弥补因重写原型而失去的默认的constructor 属性
  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);
}
 
var instance1 = new SubType("jack", 23);
var instance2 = new SubType("rose", 20);
 
instance1.colors.push("2"); // ["red", "blue", "green", "2"]
instance1.colors.push("3"); // ["red", "blue", "green", "3"]
赞(0) 打赏
转载请附上原文出处链接:胖蔡说技术 » JavaScript 继承实现
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!

 

请小编喝杯咖啡~

支付宝扫一扫打赏

微信扫一扫打赏