javascript中实现继承的三种方式:原型继承、借用构造函数继承、混合继承:
1 /*
2 js当中的继承
3
4 js中 构造函数 原型对象 实力对象的关系:
5 1 构造函数.prototype = 原型对象
6 2 原型对象.constructor = 构造函数(模板)
7 3 原型对象.isPrototypeOf(实例对象) 判断实例对象的原型是不是当前对象
8 4 构造函数 实力对象 是 类和对象的关系
9 */
10
11
12 /*
13 1 原型继承
14 给子类构造函数的原型对象赋值为父类构造函数的实例
15 赋值之后产生的关系:
16 1 子类的原型对象是父类的一个实例 Son.prototype 是 new Father()
17 2 父类的方法和属性,子类可以在原型中得到
18 3 子类的原型对象的构造器是父类模板 Son.prototype.constructor 是 Father
19 4 子类的原型对象的原型对象是父类的原型对象 Son.prototype.prototype 是Father.prototype
20 原型继承的特点:
21 子类继承了父类所有属性方法发和 父类原型对象中的属性和方法
22 缺点: 不能在子类实例化对象的时候给父类的属性赋值,继承父类的值和子类对象实例化分开了。
23 */
24
25 //父类构造函数
26 function Father(name){
27 this.name = name;
28 }
29 //父类原型对象
30 Father.prototype={
31 constructor: Father,
32 id:10,
33 sayName:function(){
34 alert(this.name);
35 }
36 };
37
38 //子类构造函数
39 function Son(age){
40 this.age = age;
41 }
42 //子类原型对象 这一家子都姓张
43 Son.prototype = new Father("张");
44
45 //子类的实例 能够使用父类的属性和方法
46 var s1 = new Son(18);
47 //s1.sayName(); //张
48 //alert(s1.age); //18
49
50 /*
51 原型继承的特点:
52 子类继承了父类所有属性方法发和 父类原型对象中的属性和方法
53 缺点: 不能在子类实例化对象的时候给父类的属性赋值,继承父类的值和子类对象实例化分开了。
54 */
55
56
57
58
59 /*
60 2 类继承(借用构造函数) 只继承父类的构造函数,父类的原型对象没有继承过来
61 利用call 或者apply进行函数绑定,借用父类构造函数的属性和方法
62 */
63 //父类构造函数
64 function Father(name){
65 this.name = name;
66 }
67 //父类原型对象 在子类中不能继承父类原型对象中的内容
68 Father.prototype.id = 10;
69
70 //子类构造函数
71 function Son(name,age){
72 //name属性是父类的 不是子类的 借用父类的构造函数 把属性绑定给子类的实例对象
73 Father.call(this,name);
74 this.age = age;
75 }
76 //实例化子类对象 在这里可以直接给继承来的属性赋值
77 var s2 = new Son("z3",18);
78 //alert(s2.name); //z3 父类构造函数绑定来的属性
79 //alert(s2.age); //18 子类自己的属性
80
81 /*
82 类继承(借用构造函数) 的特点:
83 优点: 能够在实例化子类对象时候给继承父类的属性赋值
84 缺点:父类的原型对象中的内容无法继承
85 */
86
87
88 /*
89 3 混合继承:原型继承 和 借用构造函数继承 的混合
90 特点:
91 优点:1 能够在实例化子类对象的时候给继承来的属性赋值
92 2 能够继承父类的原型对象中的方法
93 缺点: 继承了两次父类的模版,分别是call绑定和子类原型对象赋值的时候
94 如果继承来的属性特别多,这会很耗费时间来维护
95 */
96 //父类
97 function Father(name){
98 this.name = name;
99 }
100 //父类的原型对象
101 Father.prototype = {
102 constructor:Father,
103 id:10,
104 sayName:function(){
105 alert(this.name);
106 }
107 };
108 //子类
109 function Son(name,age){
110 //借用父类构造函数 绑定子类方法 为子类继承来的属性赋值
111 Father.call(this,name);
112 this.age = age;
113 }
114 //原型继承 不传递参数,继承自父类的属性赋值交给借用父类构造函数去做
115 Son.prototype = new Father();
116
117 //使用子类实例化对象
118 var s3 = new Son("l4",20);
119 //alert(s3.name); //父类属性
120 //alert(s3.id); //父类原型对象的属性
121 //alert(s3.age); //自己的属性
122 //s3.sayName(); //父类原型对象中的方法
123 /*
124 混合集成的特点:
125 优点:1 能够在实例化子类对象的时候给继承来的属性赋值
126 2 能够继承父类的原型对象中的方法
127 缺点: 继承了两次父类的模版,分别是call绑定和子类原型对象赋值的时候
128 如果继承来的属性特别多,这会很耗费时间来维护
129 */
但是 上面三种方式 即使是混合继承也有弊端,下面用js模拟一下extends的继承方,来实现不会重复继承父类的构造函数:
1 /*
2 javascript模拟extends方法,
3 目的:
4 1子类实例化能够赋值继承来的属性
5 2 继承父类的原型对象
6 3 父类的构造函数只继承一次
7 */
8
9 //1 我们模拟一个extends函数 用于实现类之间的继承 传入父类和子类
10 function extend(son,father){
11 //让子类继承父类的原型而不继承父类构造函数的内容
12 var f = new Function(); //临时空函数,目的是函数内部是空的 但是原型是父类的原型
13 f.prototype = father.prototype; //让空函数的原型指向父类的原型
14 son.prototype = new f(); //让子类的原型指向一个空函数,空函数的原型是父类的原型,这就避免了父类构造函数的属性被重复实现
15 //给子类添加一个superClass属性保存父类的原型对象,防止自己重写了父类方法后 无法调用父类原型对象中的方法
16 son.superClass = father.prototype;
17 //为父类原型对象添加构造器 如果父类原型对象忘记加构造器这里给他加上
18 if(father.prototype.constructor == Object.prototype.constructor){
19 father.prototype.constructor = father;
20 }
21 }
22 //2 父类
23 function Father(name){
24 this.name = name;
25 }
26 //父类的原型对象
27 Father.prototype = {
28 constructor: Father,
29 sayName:function(){
30 alert(this.name);
31 }
32 };
33
34
35 //3 子类
36 function Son(name,age){
37 //借用构造函数:为子类绑定父类的构造函数中属性
38 // Father.call(this , name); 这里不这样写,为了解耦,增加通用性,经过extentds函数之后,子类里面有了父类的属性
39 Son.superClass.constructor.call(this , name); //Son.superClass是父类的原型对象,consturctor就是父类构造函数
40 this.age = age;
41 }
42 //原型继承
43 //Son.prototype = new Father(); //会再次绑定一次父类的构造函数 ,这里为了不重复继承父类构造函数,只继承父类原型对象 我们用自己
44 //调用自己的继承方法 实现为子类继承父类的原型对象 而不继承父类的构造函数
45
46
47
48 extend(Son,Father);
49
50
51
52 //使用子类
53 var s4 = new Son("haha",33);
54 alert(s4.name); //haha 父类继承来的属性
55 alert(s4.age); //33 自己的属性
56 s4.sayName(); //haha 父类继承来的方法
57 //重写父类方法
58 s4.sayName = function(){
59 alert("hello i am son");
60 };
61 s4.sayName(); //hello i am son 重写方法覆盖了父类的sayName
62 //重写之后还想调用父类的方法 需要绑定一下 指定是我这个对象来调用
63 Son.superClass.sayName.call(s4); //haha
64
65
66