JavaScript实现继承的多种方式(ES5)

3,403 阅读5分钟
  1. 谈起JS继承,我们首先来了解下什么是原型以及什么是原型链。在此如果JS基础不是很扎实的人会有疑问:“原型不就是原型链吗?”。不他们不一样,他们的差别很大。那下面来解释下他么的区别。
  • ①:原型: 在JavaScript高级程序设计中给出的解释是· 每一个构造函数(对应的就是类函数)都有一个prototype属性(强调下是属性),这个prototype属性会指向一个原型对象(强调下是对象)。该原型属性指向的原型对象称之为原型
  • ②:原型链: 每一个构造函数的原型属性会链式指向原型对象,每个原型对象都会有个constructor属性会指向构造函数(未定义时默认指向构造函数)其中形成了一种链式结构,我们称之为原型链。
  • 在此普及一个常识。所有的对象类的底层原型链最终会指向浏览器原生Object的原型。因此所以对象类均可调用Object的原型方法。以下贴图举个例子
2. 回归正题,对象继承有几种方式,都是如何实现的呢? 看一下详解。
  • ①·构造继承(当然构造继承又有三种实现方式,如下)
借用构造函数(constructor stealing)的技术(有时候也叫做伪造对象或经典继承)。这种技术的基本思想相当简单,即在子类型构造函数的内部调用超类型构造函数。别忘了,函数只不过是在特定环境中执行代码的对象,因此通过使用 apply()和 call()方法也可以在(将来)新创建的对象上执行构造函数,如下所示
  • 冒充继承
function Person(name,age){
       this.name = name ;
       this.age = age;
       this.showName = function(){
           console.log('我是'+name);
    }
}

/** 
 *  @description 以自身运行环境运行函数,这时函数内的this均指向Child,
 *  因此父类的属性全部移植到子类中
 */
function Child(){
       //这三句代码最关键
       this.temp = Person; //创建一个自身缓存函数并将父类构造赋值
       this.temp('李端','26');
       delete this.temp;//删除缓存函数
}
var child = new Child();
child.showName();//我是李端
  • 绑定this方式实现
function Person(name,age){
       this.name = name ;
       this.age = age;
       this.showName = function(){
           console.log('我是'+name);
    }
}

/** 
 *  @description 以自身运行环境运行函数,这时函数内的this均指向Child,
 *  因此父类的属性全部移植到子类中
 */
function Child(){
       Person.bind(this)('李端','26'); //绑定this到Person运行环境执行函数
}
var child = new Child();
child.showName();//我是李端
  • call的方式实现
function Person(name,age){
    this.name = name ;
    this.age = age;
    this.showName = function(){
        console.log('我是'+name);
    }
}
function Child(){
    Person.call(this,'李端','26');
};
var child = new Child();
child.showName();
  • apply方式实现
function Person(name,age){
    this.name = name ;
    this.age = age;
    this.showName = function(){
        console.log('我是'+name);
    }
 }
 function Child(){
    Person.apply(this,['李端','26']);
 };
 var child = new Child();
 child.showName();
  • ②·原型继承
function Person(name,age){
    this.name = name;
    this.age = age;
}
Person.prototype.sayHello = function(){
    alert('使用原型得到'+this.name);
}
var per = new Person('李端','26');
per.sayHello();
//创建新对象并实现继承
function Student(){};
Student.prototype = new Person('端瑞','23')
var stu = new Student();
stu.sayHello();
  • ③·组合继承(组合构造和原型方式实现继承)
组合继承(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();
SubType.prototype.constructor = 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 
  • ④·寄生继承
寄生式(parasitic)继承是与原型式继承紧密相关的一种思路,并且同样也是由克罗克福德推而广之的。寄生式继承的思路与寄生构造函数和工厂模式类似,即创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再像真地是它做了所有工作一样返回对象。以下代码示范了寄生式继承模式。 在这个例子中,createAnother()函数接收了一个参数,也就是将要作为新对象基础的对象。然后,把这个对象(original)传递给 object()函数,将返回的结果赋值给 clone。再为 clone 对象添加一个新方法 sayHi(),最后返回 clone 对象。可以像下面这样来使用 createAnother()函数:
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 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);
};