Javascript中,实现类与继承的方法和优缺点分析

Javascript是一种弱类型语言,不存在类的概念,但在js中可以模仿类似于JAVA中的类,实现类与继承

第一种方法:利用Javascript中的原型链

  1 //首先定义一个父类
  2 
  3 function Animal(name,age){
  4     //定义父类的属性
  5     this.name = name || "Animal";
  6     this.age    =  age ;
  7     //定义父类的方法
  8     this.sleep = function(){
  9         console.log("这只"+this.name+"正在睡觉!");
 10     };
 11     this.showAge = function(){
 12         console.log("这只"+this.name+"的年龄是"+this.age+"岁!");
 13     }
 14 };
 15 
 16 //为父类添加原型方法
 17 
 18 Animal.prototype.eat =function(food){
 19     console.log("这只"+this.name+"正在吃"+food+"!")
 20 }
 21 
 22 Animal.prototype.weight = 10;
 23 
 24 Animal.prototype.bark = function(){
 25     console.log("bark");
 26 }
 27 
 28 //Javascript实现继承的方法一:
 29 
 30 //原型链法    核心:将父类的实例作为子类的原型
 31 
 32 function Cat(){
 33 
 34 };
 35 
 36 Cat.prototype = new Animal();
 37 
 38 Cat.prototype.name = "猫";
 39 
 40 Cat.prototype.age = 3;
 41 
 42 Cat.prototype.bark = function(){
 43     console.log("喵喵"); 
 44 }
 45 
 46 //测试上面的例子
 47 
 48 var  cat = new Cat();
 49 
 50 console.log(cat.name);                                //输出猫
 51 
 52 console.log(cat.age);                                   //输出 3
 53 
 54 console.log(cat.weight);                              // 输出10
 55 
 56 cat.sleep();                                                  //输出 这只猫正在睡觉!
 57 
 58 cat.showAge();                                            //输出 这只猫的年龄是3岁!
 59  
 60 cat.eat("鱼");                                               //输出 这只猫正在吃鱼!
 61 
 62 cat.bark();                                                   //输出喵喵
 63 
 64 console.log(cat instanceof Animal);            //输出 true
 65 
 66 console.log(cat instanceof  Cat);                 //输出 true
 67 
 68 
 69 var Dog = new Function;                  // 为什么这样写也可以呢? 例子最后将做解释
 70 
 71 Dog.prototype = new Animal();
 72 
 73 Dog.prototype.name = "狗";
 74 
 75 Dog.prototype.age = 4;
 76 
 77 Dog.prototype.bark = function(){
 78    console.log("汪汪");
 79 }
 80 
 81 var dog = new Dog();
 82 
 83 console.log(dog.name);                               //输出 狗
 84 
 85 console.log(dog.age);                                  //输出 4
 86 
 87 console.log(dog.weight);                             //输出 10
 88 
 89 dog.sleep();                                                 //输出 这只狗正在睡觉!
 90 
 91 dog.eat("骨头");                                           //输出 这只狗正在吃骨头
 92 
 93 dog.bark();                                                  //输出 汪汪
 94 
 95 console.log(dog instanceof Dog);                //输出true
 96 
 97 console.log(dog instanceof Animal);            //输出true
 98  
 99 console.log(Animal.constructor);                 //输出 Function
100 
101 console.log(Cat.constructor);                       //输出 Function 
102 
103 console.log(Dog.constructor);                      //输出 Function 
104 
105 console.log(cat.constructor);                        //输出 function Animal(){...}
106 
107 console.log(dog.constructor);                      //输出 function Animal(){...}
108 
109 /*有上面四行的输出结果可以看书,Animal 父类,以及Cat  和  Dog 子类,都是由Function 创建出来的构造函数,
110 而 cat  和  dog   是由Animal 父类构建出来的子类 */

原型链法实现继承的特点分析

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

    缺点 :           1.想要为子类添加原型属性和原型方法,则必须在new Animal() 语句之后执行     2.无法实现多继承     3.来自原型对象的引用属性是所有实例共享的(由Cat 和 Dog 都可以使用weight 属性 和 eat 方法可以看出)     4.创建子类实例时,无法向父类构造函数传参

第二种方法:  构造函数法

//首先定义一个父类

function Animal(name,age){
    //定义父类的属性
    this.name = name || "Animal";
    this.age    =  age ;
    //定义父类的方法
    this.sleep = function(){
        console.log("这只"+this.name+"正在睡觉!");
    };
    this.showAge = function(){
        console.log("这只"+this.name+"的年龄是"+this.age+"岁!");
    }
};

//为父类添加原型方法

Animal.prototype.eat =function(food){
    console.log("这只"+this.name+"正在吃"+food+"!")
}

Animal.prototype.weight = 10;

Animal.prototype.bark = function(){
    console.log("bark");
}


//Javascript 实现继承的方法二  :

//构造函数法    核心:使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类,但父类的原型属性和原型方法在子类中不能提现

function Cat(name,age){
    Animal.call(this);                          //使用call方法继承父类的属性和方法
    this.name = name || "猫";            //使用子类自己的属性覆盖从父类继承来的属性
    this.age = age;                           //使用子类自己的属性覆盖从父类继承来的属性
    this.bark = function(){                //定义子类自己的方法
        console.log("喵喵");
    }
};

//测试

var  cat = new Cat("猫",4);             //新创建一个子类实例化对象,并传参

console.log(cat.name);                 // 输出 猫

console.log(cat.age);                    //输出 4


console.log(cat.weight);                //这一句会报undefined,因为父类的原型属性在子类中无法使用

cat.sleep();                                   // 输出 这只猫正在睡觉

cat.bark();                                    //输出喵喵

//cat.eat("鱼");                              //这一句会报错,因为父类的原型方法在这里不能使用

cat.showAge();                             //输出         

console.log(cat instanceof Animal); // false

console.log(cat instanceof Cat); // true

//上面这两句可以看书,cat 并不属于Animal 父类的实例对象,而只是Cat 子类的实例对象


//总结

/*构造函数法实现继承的方法的特点分析

   优点:
          1.解决了原型链法中,子类实例对象会共享父类引用属性的问题;
          2.创建子类实例对象时,可以向父类传递参数
          3.可以实现多继承(通过call多个父类对象)

    缺点:
           1.实例对象并不是父类的实例对象,而只是子类的实例对象,(举例(小花猫属于猫类,却不属于动物类),这点很不好)
           2.只能继承父类的实例属性和实例方法,不能继承父类的原型属性和原型方法
           3.无法实现函数的复用,每个子类都有父类实例函数的副本,影响性能

构造函数法实现继承的优缺点分析:

优点:           1.解决了原型链法中,子类实例对象会共享父类引用属性的问题;     2.创建子类实例对象时,可以向父类传递参数     3.可以实现多继承(通过call多个父类对象)

 缺点:         1.实例对象并不是父类的实例对象,而只是子类的实例对象,(举例(小花猫属于猫类,却不属于动物类),这点很不好)      2.只能继承父类的实例属性和实例方法,不能继承父类的原型属性和原型方法      3.无法实现函数的复用,每个子类都有父类实例函数的副本,影响性能

第三种方法:实例继承法

//首先定义一个父类

function Animal(name,age){
    //定义父类的属性
    this.name = name || "Animal";
    this.age    =  age ;
    //定义父类的方法
    this.sleep = function(){
        console.log("这只"+this.name+"正在睡觉!");
    };
    this.showAge = function(){
        console.log("这只"+this.name+"的年龄是"+this.age+"岁!");
    }
};

//为父类添加原型方法

Animal.prototype.eat =function(food){
    console.log("这只"+this.name+"正在吃"+food+"!")
}

Animal.prototype.weight = 10;

Animal.prototype.bark = function(){
    console.log("bark");
}

//Javascript 实现继承的方法二  :

//实例继承     核心:为父类实例添加新特性,作为子类实例返回

function Cat(name,age){
    var obj = new Animal();     //   创建一个父类的实例对象obj
    //这里为obg添加了一个不可枚举属性 sex
    Object.defineProperty(obj, "sex", {
        value: "female",
        enumerable: false

    });
    obj.name = "猫";               // 为实例对象添加 属性  
    obj.age = 3;                      // 为实例对象添加 属性  
    obj.bark = function(){        // 为实例对象添加方法 
        console.log("喵喵");
    }
    return obj;                        // 将这个实例对象作为返回值返回
};

//测试代码

var  cat = new Cat();

console.log(cat.name);                            //输出 猫

console.log(cat.age);                               //输出 3

console.log(cat.weight);                          // 输出 10

console.log(cat.sex);                               //输出female

cat.sleep();                                              // 输出 这只猫正在睡觉

cat.eat("鱼");                                           // 输出 这只猫正在吃鱼

cat.bark();                                                //输出 喵喵

console.log(cat instanceof Cat);               //输出false    

//instanceof 运算符用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性。
//instanceof 运算符可以用来判断一个对象是否属于某种对象类型
//instanceof 运算符更好的用途是可以判断一个实例是否属于它的父类

console.log(cat instanceof Animal);          //输出 true

//上面这两句可以看书,cat 并不属于Cat的实例对象,而只是Animal父类的实例对象

//总结 

/*实例继承法的特点分析

    优点:
           1.不限制调用方式

    确定:
           1.实例是父类的实例,并不是子类的实例(这里好像违反了继承的理念,就好像小花猫属于动物,却不属于猫类一样)
           2.不支持多继承

 优点:         1.不限制调用方式

    确定:            1.实例是父类的实例,并不是子类的实例(这里好像违反了继承的理念,就好像小花猫属于动物,却不属于猫类一样)      2.不支持多继承

第四种法法:组合继承法

/首先定义一个父类

function Animal(name,age){
    //定义父类的属性
    this.name = name || "Animal";
    this.age    =  age ;
    //定义父类的方法
    this.sleep = function(){
        console.log("这只"+this.name+"正在睡觉!");
    };
    this.showAge = function(){
        console.log("这只"+this.name+"的年龄是"+this.age+"岁!");
    }
};

//为父类添加原型方法

Animal.prototype.eat =function(food){
    console.log("这只"+this.name+"正在吃"+food+"!")
}

Animal.prototype.weight = 10;

Animal.prototype.bark = function(){
    console.log("bark");
}


//Javascript 实现继承的方法五  :

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

function Cat(name,age){
    Animal.call(this);
    this.name = name;
    this.age = age;
    this.bark = function(){
        console.log("喵喵");
    }
};

Cat.prototype = new Animal();

//测试代码

var  cat  =  new Cat("猫",3);

console.log(cat.name);                                   //

console.log(cat.age);                                     // 3

console.log(cat.weight);                                // 10

cat.showAge();                                             // 这只猫的年龄是3岁

cat.eat("鱼");                                                // 这只猫正在吃鱼

cat.sleep();                                                  // 这只猫正在睡觉

cat.bark();                                                   // 喵喵

console.log(cat instanceof Animal);             // true

console.log(cat instanceof Cat);                   // true

//从上面两行的输出结果来看,cat  是 父类 Animal 的 实例 ,也是子类Cat  的 实例

组合继承法实现继承的特点分析

    优点:             1.弥补了构造函数法的缺陷,可以继承实例属性和方法,也可以继承原型属性和方法    2.既是子类的实例,也是父类的实例(比较符合继承的理念)    3.不存在引用属性共享问题    4.子类的实例对象可传参    5.函数可复用  缺点:          1.在生成子类实例对象的过程中,调用了两次父类构造函数,生成了两份实例(子类实例将子类原型上的那份屏蔽了)

结论 :  一般情况下,推荐使用这种方式的继承,缺点是多消耗一点内存 

第五种方法:寄生组合继承法

//第六种,寄生组合继承法实现继承

//首先定义一个父类

function Animal(name,age){
    //定义父类的属性
    this.name = name || "Animal";
    this.age    =  age ;
    //定义父类的方法
    this.sleep = function(){
        console.log("这只"+this.name+"正在睡觉!");
    };
    this.showAge = function(){
        console.log("这只"+this.name+"的年龄是"+this.age+"岁!");
    }
};

//为父类添加原型方法

Animal.prototype.eat =function(food){
    console.log("这只"+this.name+"正在吃"+food+"!")
}

Animal.prototype.weight = 10;

Animal.prototype.bark = function(){
    console.log("bark");
}


//Javascript 实现继承的方法六  :

//寄生组合继承   通过寄生方式,砍掉父类的实例属性,
//这样,在调用两次父类的构造的时候,就不会初始化两次实例方法/属性,避免的组合继承的缺点

function Cat(name,age){
    Animal.call(this);
    this.name = name;
    this.age = age ;
    this.bark = function(){
        console.log("喵喵");
    }
};

//通过一个立即执行函数将父类的实例作为子类的原型
(function(){
    //创建一个没有实例方法的类
    var superClass = new Function;  //  这里也可以写成,var  sup = function(){}; 总之就是定义一个空构造函数
    superClass.prototype = Animal.prototype;
    //将实例作为子类的原型
    Cat.prototype = new superClass();
})();

//测试代码

var cat = new Cat("猫",3);

console.log(cat.name);                                    //

console.log(cat.age);                                       // 3

console.log(cat.weight);                                  // 10

cat.showAge();                                               //这只猫的年龄是3岁!
 
cat.eat("鱼");                                                  //这只猫正在吃鱼!

cat.sleep();                                                     //这只猫正在睡觉!

cat.bark();                                                      //喵喵

console.log(cat instanceof Animal);               // true

console.log(cat instanceof Cat);                    //true

//上面两行可以看出,cat 是 子类 Cat  的实例对象, 也是 父类 Animal 的 实例对象

寄生组合继承法的特点分析

    优点 :          1.集中了几种方法的优点,堪称完美

    缺点 :           1.实现比较复杂

结论:  在实际项目和工程中,推荐使用这种方法

 

原文地址:https://www.cnblogs.com/liquanjiang/p/8681813.html