NT:菜鸟为了好理解,加了很多注释。

 

4.书籍:《javascript高级程序设计》第六章

1.原型链继承

核心:基本思想,是利用原型让一个基本引用类型继承另一个引用类型的属性和方法。

以下代码定义了两个类型Animal和Cat类,侮个类型分别有一个属性和一个方法。它们的主要试别是Cat继承了Animal,而继承是通过创建Animal的实例,并将该实例赋给Cat.prototype实现的。实现的本质是重新原型对象,代之以一个新类型的实例。

// 定义一个动物类
function Animal (name) {
    // 属性
    this.name = name || 'Animal';
    // 实例方法
    this.sleep = function(){
        return this.name + '正在睡觉!';
    }
}
// 原型方法
Animal.prototype.eat = function(food) {
    return this.name + '正在吃:' + food;
};
//定义一个猫类
function Cat(name){ 
    this.name = name;
}
//Javascript规定,每一个构造函数都有一个prototype属性,指向另一个对象。这个对象的所有属性和方法,都会被构造函数的实例继承。任何一个prototype对象都有一个constructor属性,指向它的构造函数
console.log(Cat.prototype);
console.log(Cat.prototype.constructor === Cat);

Cat.prototype = new Animal();   //强行将Cat的prototype对象指向一个Animal的实例。
console.log(Cat.prototype.constructor == Animal);//此时Cat.prototype.constructor指向Animal
Cat.prototype.constructor = Cat;    //修复构造函数指
// Test Code
var cat = new Cat('凯蒂猫');
Cat.prototype.name = 'hello kitty';  //以构造函数里的name优先,若无,则向上找到hello kitty
console.log(cat.name);  //凯蒂猫
console.log(cat.eat('fish'));   //cat正在吃:fish
console.log(cat.sleep());   //cat正在睡觉!
console.log(cat instanceof Animal); //true 
console.log(cat instanceof Cat); //true

特点:

  1. 非常纯粹的继承关系,实例是子类的实例,也是父类的实例
  2. 父类新增原型方法/原型属性,子类都能访问到
  3. 简单,易于实现

缺点:

  1. 可以在Cat构造函数中,为Cat实例增加实例属性。如果要新增原型属性和方法,则必须放在new Animal()这样的语句之后执行
  2. 无法实现多继承
  3. 来自原型对象的引用属性是所有实例共享的(详细请看附录代码: 示例1)
  4. 创建子类实例时,无法向父类构造函数传参

推荐指数:★★(3、4两大致命缺陷)

2.构造函数继承(对象冒充)

核心:

基本思想:在子类型构造函数的内部调用超类型构造函数

使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)。  

为了解决引用共享和超类型无法传参的问题,我们采用一种叫借用构造函数的技术,或者成为对象冒充(伪造对象、经典继承)的技术来解决这两种问题 

//例子一
function Animal(){
    this.species = "动物";
}
function Cat(name,color){
   Animal.apply(this);
  this.name = name;
  this.color = color;
}
var cat1 = new Cat("大毛","黄色");
console.log(cat1.species,cat1.name); // 动物 大毛


//例二
function Box(age){
    this.name=['Lee','Jack','Hello']
    this.age=age;
}
function Desk(age,hahaha){
    Box.call(this,age); //对象冒充,给超类型传参
    this.hahaha = hahaha
}
var desk = new Desk(200,'hhhh');
console.log(desk.age,desk.hahaha);//200
console.log(desk.name);//['Lee','Jack','Hello']
desk.name.push('AAA'); //添加的新数据,只给 desk
console.log(desk.name) //['Lee','Jack','Hello','AAA']
var Box1 = new Box();
console.log(Box1.name); //['Lee','Jack','Hello']

特点:

  1. 解决了1中,子类实例共享父类引用属性的问题
  2. 创建子类实例时,可以向父类传递参数
  3. 可以实现多继承(call多个父类对象)

缺点:

  1. 实例并不是父类的实例,只是子类的实例
  2. 只能继承父类的实例属性和方法,不能继承原型属性/方法
  3. 无法实现函数复用,每个子类都有父类实例函数的副本,影响性能

推荐指数:★★(缺点2,3)

 

 

3.组合继承(原型链继承+构造函数继承)

借用构造函数虽然解决了刚才两种问题, 但没有原型, 复用则无从谈起。 所以, 我们需
要原型链+借用构造函数的模式,这种模式成为组合继承。

核心:通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用

// 定义一个动物类
function Animal(name, age) {
    // 属性
    console.log("*************");   //************* ,************* 超类构造函数被调用两次
    this.name = name || 'Animal';
    this.age = age || 5;
    // 实例方法
    this.sleep = function () {
        return this.name + '正在睡觉!';
    }
}
// 原型方法
Animal.prototype.eat = function (food) {
    return this.name + '正在吃:' + food;
};

//定义一个猫类
function Cat(name, age) {
    Animal.apply(this, arguments);    //对象冒充继承,第二次调用超类
    this.name = name || 'Tom';
}
Cat.prototype = new Animal();   //原型链继承,第一次调用超类

//组合继承也是需要修复构造函数指向的。
Cat.prototype.constructor = Cat;

// Test Code
var cat = new Cat('kitty', 6);
console.log(cat.name, cat.age);  //kitty
console.log(cat.sleep());   //kitty正在睡觉!
console.log(cat.eat('猫粮'));   //kitty正在吃:猫粮
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); // true

特点:

  1. 弥补了方式1的缺陷,可以继承实例属性/方法,也可以继承原型属性/方法
  2. 既是子类的实例,也是父类的实例
  3. 不存在引用属性共享问题
  4. 可传参
  5. 函数可复用

缺点:

  1. 调用了两次父类构造函数,生成了两份实例(子类实例将子类原型上的那份屏蔽了)

推荐指数:★★★★(仅仅多消耗了一点内存)

 

4.拷贝继承

父对象的所有属性和方法,拷贝进子对象

// 定义一个动物类
function Animal (name) {
  // 属性
  this.name = name || 'Animal';
  // 实例方法
  this.sleep = function(){
    return this.name + '正在睡觉!';
  }
}
// 原型方法
Animal.prototype.eat = function(food) {
    return  this.name + '正在吃:' + food;
};
//定义一个猫类
function Cat(name,age){
    var animal = new Animal();
    for(var p in animal){ //for in 能遍历到原型上的属性,但是无法遍历不可枚举的属性
        Cat.prototype[p] = animal[p];
    }
    Cat.prototype.name = name || 'Tom';
    this.age = age;
}
// Test Code
var cat = new Cat('kitty',5);
console.log(cat.name, cat.age);  //Tom 5
console.log(cat.sleep());   //Tom正在睡觉!
console.log(cat.eat('猫粮'));   //Tom正在吃:猫粮
console.log(cat instanceof Animal); // false
console.log(cat instanceof Cat); // true

特点:

  1. 支持多继承

缺点:

  1. 效率较低,内存占用高(因为要拷贝父类的属性)
  2. 无法获取父类不可枚举的方法(不可枚举方法,不能使用for in 访问到)

推荐指数:★(缺点1)

 

5.寄生继承

  寄生式继承的思路与寄生构造函数和工厂模式类似,即创建一个仅用于封装继承过程的函数,该函数在内部以某种方式增强对象,最后再返回对象。
  使用的Object.create(original)函数不是必须的,任何能返回对象的函数都适用于此模式。如new object(original)或者object(original)等,甚至都不使用,只要能返回对象。

function createChild(original) {
    var clone = Object.create(original); //通过调用函数创建一个新对象
    clone.eat = function (food) {    //以某种方式来增强这个对象
        console.log(this.name + '正在吃:' + food);
    };
    return clone;      //返回这个对象
}
var Animal = {
    name: "Bob",
    sleep: function () {
        console.log(this.name + '正在睡觉!');
    }
};
var cat = createChild(Animal);
cat.eat("猫粮");    //Bob正在吃:猫粮
cat.sleep();   //Bob正在睡觉!

 

6.寄生组合式继承

  组合继承两次调用了超类构造函数,并且原型共享。寄生组合继承修复了这个问题,

  寄生组合继承的思想:通过构造函数来继承属性,通过原型链来继承方法。这里的Clone是空对象,所以几乎不占内存。这时,修改Cat的prototype对象,就不会影响到Animal的prototype对象。

// 定义一个动物类
function Animal(name) {
    // 属性
    console.log("*************");   //************* 超类被调用一次
    this.name = name || 'Animal';
    // 实例方法
    this.sleep = function () {
        return this.name + '正在睡觉!';
    }
}
// 原型方法
Animal.prototype.eat = function (food) {
    return this.name + '正在吃:' + food;
};
//定义一个猫类
function Cat(name) {
    Animal.call(this);
    this.name = name || 'Tom';
}
(function () {
    // 创建一个没有实例方法的类
    var Clone = function () { };
    Super.prototype = Animal.prototype;  //创建超类原型的副本
    //将实例作为子类的原型
    Cat.prototype = new Clone();   //将新创建的对象(即副本)赋值给子类的原型
})();
// Test Code
var cat = new Cat();
Cat.prototype.constructor = Cat; // 需要修复下构造函数
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); //true

特点:

  1. 堪称完美(YUI的YAHOO.lang.extend()就是采用寄生组合式继承)

缺点:

  1. 实现较为复杂

推荐指数:★★★★(实现复杂,扣掉一颗星)

 ********************************************************************六种方式到此结束********************************************************************

另附YUI的封装了的寄生组合式继承

function extend(Child, Parent) {
    var F = function () { };
    F.prototype = Parent.prototype;
    Child.prototype = new F();
    Child.prototype.constructor = Child;
    Child.uber = Parent.prototype;
}