Javascript是一种弱类型语言,不存在类的概念,但在js中可以模仿类似于JAVA中的类,实现类与继承
第一种方法:利用Javascript中的原型链
1 1 //首先定义一个父类 2 2 3 3 function Animal(name,age){ 4 4 //定义父类的属性 5 5 this.name = name || "Animal"; 6 6 this.age = age ; 7 7 //定义父类的方法 8 8 this.sleep = function(){ 9 9 console.log("这只"+this.name+"正在睡觉!"); 10 10 }; 11 11 this.showAge = function(){ 12 12 console.log("这只"+this.name+"的年龄是"+this.age+"岁!"); 13 13 } 14 14 }; 15 15 16 16 //为父类添加原型方法 17 17 18 18 Animal.prototype.eat =function(food){ 19 19 console.log("这只"+this.name+"正在吃"+food+"!") 20 20 } 21 21 22 22 Animal.prototype.weight = 10; 23 23 24 24 Animal.prototype.bark = function(){ 25 25 console.log("bark"); 26 26 } 27 27 28 28 //Javascript实现继承的方法一: 29 29 30 30 //原型链法 核心:将父类的实例作为子类的原型 31 31 32 32 function Cat(){ 33 33 34 34 }; 35 35 36 36 Cat.prototype = new Animal(); 37 37 38 38 Cat.prototype.name = "猫"; 39 39 40 40 Cat.prototype.age = 3; 41 41 42 42 Cat.prototype.bark = function(){ 43 43 console.log("喵喵"); 44 44 } 45 45 46 46 //测试上面的例子 47 47 48 48 var cat = new Cat(); 49 49 50 50 console.log(cat.name); //输出猫 51 51 52 52 console.log(cat.age); //输出 3 53 53 54 54 console.log(cat.weight); // 输出10 55 55 56 56 cat.sleep(); //输出 这只猫正在睡觉! 57 57 58 58 cat.showAge(); //输出 这只猫的年龄是3岁! 59 59 60 60 cat.eat("鱼"); //输出 这只猫正在吃鱼! 61 61 62 62 cat.bark(); //输出喵喵 63 63 64 64 console.log(cat instanceof Animal); //输出 true 65 65 66 66 console.log(cat instanceof Cat); //输出 true 67 67 68 68 69 69 var Dog = new Function; // 为什么这样写也可以呢? 例子最后将做解释 70 70 71 71 Dog.prototype = new Animal(); 72 72 73 73 Dog.prototype.name = "狗"; 74 74 75 75 Dog.prototype.age = 4; 76 76 77 77 Dog.prototype.bark = function(){ 78 78 console.log("汪汪"); 79 79 } 80 80 81 81 var dog = new Dog(); 82 82 83 83 console.log(dog.name); //输出 狗 84 84 85 85 console.log(dog.age); //输出 4 86 86 87 87 console.log(dog.weight); //输出 10 88 88 89 89 dog.sleep(); //输出 这只狗正在睡觉! 90 90 91 91 dog.eat("骨头"); //输出 这只狗正在吃骨头 92 92 93 93 dog.bark(); //输出 汪汪 94 94 95 95 console.log(dog instanceof Dog); //输出true 96 96 97 97 console.log(dog instanceof Animal); //输出true 98 98 99 99 console.log(Animal.constructor); //输出 Function 100100 101101 console.log(Cat.constructor); //输出 Function 102102 103103 console.log(Dog.constructor); //输出 Function 104104 105105 console.log(cat.constructor); //输出 function Animal(){...} 106106 107107 console.log(dog.constructor); //输出 function Animal(){...} 108108 109109 /*有上面四行的输出结果可以看书,Animal 父类,以及Cat 和 Dog 子类,都是由Function 创建出来的构造函数, 110110 而 cat 和 dog 是由Animal 父类构建出来的子类 */
原型链法实现继承的特点分析
优点: 1.是非常纯粹的继承关系,实例是子类的实例,也是父类的实例, 2.父类新增原型属性和原型方法,子类都可以访问到 3.简单方便,易于实现
缺点 : 1.想要为子类添加原型属性和原型方法,则必须在new Animal() 语句之后执行 2.无法实现多继承 3.来自原型对象的引用属性是所有实例共享的(由Cat 和 Dog 都可以使用weight 属性 和 eat 方法可以看出) 4.创建子类实例时,无法向父类构造函数传参
第二种方法: 构造函数法
1//首先定义一个父类 2 3function 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 18Animal.prototype.eat =function(food){ 19 console.log("这只"+this.name+"正在吃"+food+"!") 20} 21 22Animal.prototype.weight = 10; 23 24Animal.prototype.bark = function(){ 25 console.log("bark"); 26} 27 28 29//Javascript 实现继承的方法二 : 30 31//构造函数法 核心:使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类,但父类的原型属性和原型方法在子类中不能提现 32 33function Cat(name,age){ 34 Animal.call(this); //使用call方法继承父类的属性和方法 35 this.name = name || "猫"; //使用子类自己的属性覆盖从父类继承来的属性 36 this.age = age; //使用子类自己的属性覆盖从父类继承来的属性 37 this.bark = function(){ //定义子类自己的方法 38 console.log("喵喵"); 39 } 40}; 41 42//测试 43 44var cat = new Cat("猫",4); //新创建一个子类实例化对象,并传参 45 46console.log(cat.name); // 输出 猫 47 48console.log(cat.age); //输出 4 49 50 51console.log(cat.weight); //这一句会报undefined,因为父类的原型属性在子类中无法使用 52 53cat.sleep(); // 输出 这只猫正在睡觉 54 55cat.bark(); //输出喵喵 56 57//cat.eat("鱼"); //这一句会报错,因为父类的原型方法在这里不能使用 58 59cat.showAge(); //输出 60 61console.log(cat instanceof Animal); // false 62 63console.log(cat instanceof Cat); // true 64 65//上面这两句可以看书,cat 并不属于Animal 父类的实例对象,而只是Cat 子类的实例对象 66 67 68//总结 69 70/*构造函数法实现继承的方法的特点分析 71 72 优点: 73 1.解决了原型链法中,子类实例对象会共享父类引用属性的问题; 74 2.创建子类实例对象时,可以向父类传递参数 75 3.可以实现多继承(通过call多个父类对象) 76 77 缺点: 78 1.实例对象并不是父类的实例对象,而只是子类的实例对象,(举例(小花猫属于猫类,却不属于动物类),这点很不好) 79 2.只能继承父类的实例属性和实例方法,不能继承父类的原型属性和原型方法 80 3.无法实现函数的复用,每个子类都有父类实例函数的副本,影响性能
构造函数法实现继承的优缺点分析:
优点: 1.解决了原型链法中,子类实例对象会共享父类引用属性的问题; 2.创建子类实例对象时,可以向父类传递参数 3.可以实现多继承(通过call多个父类对象)
缺点: 1.实例对象并不是父类的实例对象,而只是子类的实例对象,(举例(小花猫属于猫类,却不属于动物类),这点很不好) 2.只能继承父类的实例属性和实例方法,不能继承父类的原型属性和原型方法 3.无法实现函数的复用,每个子类都有父类实例函数的副本,影响性能
第三种方法:实例继承法
1//首先定义一个父类 2 3function 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 18Animal.prototype.eat =function(food){ 19 console.log("这只"+this.name+"正在吃"+food+"!") 20} 21 22Animal.prototype.weight = 10; 23 24Animal.prototype.bark = function(){ 25 console.log("bark"); 26} 27 28//Javascript 实现继承的方法二 : 29 30//实例继承 核心:为父类实例添加新特性,作为子类实例返回 31 32function Cat(name,age){ 33 var obj = new Animal(); // 创建一个父类的实例对象obj 34 //这里为obg添加了一个不可枚举属性 sex 35 Object.defineProperty(obj, "sex", { 36 value: "female", 37 enumerable: false 38 39 }); 40 obj.name = "猫"; // 为实例对象添加 属性 41 obj.age = 3; // 为实例对象添加 属性 42 obj.bark = function(){ // 为实例对象添加方法 43 console.log("喵喵"); 44 } 45 return obj; // 将这个实例对象作为返回值返回 46}; 47 48//测试代码 49 50var cat = new Cat(); 51 52console.log(cat.name); //输出 猫 53 54console.log(cat.age); //输出 3 55 56console.log(cat.weight); // 输出 10 57 58console.log(cat.sex); //输出female 59 60cat.sleep(); // 输出 这只猫正在睡觉 61 62cat.eat("鱼"); // 输出 这只猫正在吃鱼 63 64cat.bark(); //输出 喵喵 65 66console.log(cat instanceof Cat); //输出false 67 68//instanceof 运算符用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性。 69//instanceof 运算符可以用来判断一个对象是否属于某种对象类型 70//instanceof 运算符更好的用途是可以判断一个实例是否属于它的父类 71 72console.log(cat instanceof Animal); //输出 true 73 74//上面这两句可以看书,cat 并不属于Cat的实例对象,而只是Animal父类的实例对象 75 76//总结 77 78/*实例继承法的特点分析 79 80 优点: 81 1.不限制调用方式 82 83 确定: 84 1.实例是父类的实例,并不是子类的实例(这里好像违反了继承的理念,就好像小花猫属于动物,却不属于猫类一样) 85 2.不支持多继承
优点: 1.不限制调用方式
确定: 1.实例是父类的实例,并不是子类的实例(这里好像违反了继承的理念,就好像小花猫属于动物,却不属于猫类一样) 2.不支持多继承
第四种法法:组合继承法
1/首先定义一个父类 2 3function 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 18Animal.prototype.eat =function(food){ 19 console.log("这只"+this.name+"正在吃"+food+"!") 20} 21 22Animal.prototype.weight = 10; 23 24Animal.prototype.bark = function(){ 25 console.log("bark"); 26} 27 28 29//Javascript 实现继承的方法五 : 30 31//组合继承法 核心:通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用 32 33function Cat(name,age){ 34 Animal.call(this); 35 this.name = name; 36 this.age = age; 37 this.bark = function(){ 38 console.log("喵喵"); 39 } 40}; 41 42Cat.prototype = new Animal(); 43 44//测试代码 45 46var cat = new Cat("猫",3); 47 48console.log(cat.name); //猫 49 50console.log(cat.age); // 3 51 52console.log(cat.weight); // 10 53 54cat.showAge(); // 这只猫的年龄是3岁 55 56cat.eat("鱼"); // 这只猫正在吃鱼 57 58cat.sleep(); // 这只猫正在睡觉 59 60cat.bark(); // 喵喵 61 62console.log(cat instanceof Animal); // true 63 64console.log(cat instanceof Cat); // true 65 66//从上面两行的输出结果来看,cat 是 父类 Animal 的 实例 ,也是子类Cat 的 实例
组合继承法实现继承的特点分析
优点: 1.弥补了构造函数法的缺陷,可以继承实例属性和方法,也可以继承原型属性和方法 2.既是子类的实例,也是父类的实例(比较符合继承的理念) 3.不存在引用属性共享问题 4.子类的实例对象可传参 5.函数可复用 缺点: 1.在生成子类实例对象的过程中,调用了两次父类构造函数,生成了两份实例(子类实例将子类原型上的那份屏蔽了)
结论 : 一般情况下,推荐使用这种方式的继承,缺点是多消耗一点内存
第五种方法:寄生组合继承法
1//第六种,寄生组合继承法实现继承 2 3//首先定义一个父类 4 5function Animal(name,age){ 6 //定义父类的属性 7 this.name = name || "Animal"; 8 this.age = age ; 9 //定义父类的方法 10 this.sleep = function(){ 11 console.log("这只"+this.name+"正在睡觉!"); 12 }; 13 this.showAge = function(){ 14 console.log("这只"+this.name+"的年龄是"+this.age+"岁!"); 15 } 16}; 17 18//为父类添加原型方法 19 20Animal.prototype.eat =function(food){ 21 console.log("这只"+this.name+"正在吃"+food+"!") 22} 23 24Animal.prototype.weight = 10; 25 26Animal.prototype.bark = function(){ 27 console.log("bark"); 28} 29 30 31//Javascript 实现继承的方法六 : 32 33//寄生组合继承 通过寄生方式,砍掉父类的实例属性, 34//这样,在调用两次父类的构造的时候,就不会初始化两次实例方法/属性,避免的组合继承的缺点 35 36function Cat(name,age){ 37 Animal.call(this); 38 this.name = name; 39 this.age = age ; 40 this.bark = function(){ 41 console.log("喵喵"); 42 } 43}; 44 45//通过一个立即执行函数将父类的实例作为子类的原型 46(function(){ 47 //创建一个没有实例方法的类 48 var superClass = new Function; // 这里也可以写成,var sup = function(){}; 总之就是定义一个空构造函数 49 superClass.prototype = Animal.prototype; 50 //将实例作为子类的原型 51 Cat.prototype = new superClass(); 52})(); 53 54//测试代码 55 56var cat = new Cat("猫",3); 57 58console.log(cat.name); // 猫 59 60console.log(cat.age); // 3 61 62console.log(cat.weight); // 10 63 64cat.showAge(); //这只猫的年龄是3岁! 65 66cat.eat("鱼"); //这只猫正在吃鱼! 67 68cat.sleep(); //这只猫正在睡觉! 69 70cat.bark(); //喵喵 71 72console.log(cat instanceof Animal); // true 73 74console.log(cat instanceof Cat); //true 75 76//上面两行可以看出,cat 是 子类 Cat 的实例对象, 也是 父类 Animal 的 实例对象
寄生组合继承法的特点分析
优点 : 1.集中了几种方法的优点,堪称完美
缺点 : 1.实现比较复杂
结论: 在实际项目和工程中,推荐使用这种方法