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.实现比较复杂
结论: 在实际项目和工程中,推荐使用这种方法