js继承学习笔记
1、原型链继承
原型链继承的本质:重写原型对象,将一个父对象的属性和方法作为一个子对象的原型对象的属性和方法。
原型链继承存在的问题:
问题1:父类中的实例属性一旦赋值给子类的原型属性,此时这些属性都属于子类的共享属性
问题2:实例化子类型的时候,不能向父类型的构造函数传参
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>原型链继承</title> 7 </head> 8 <body> 9 10 </body> 11 <script> 12 // 继承:原型对象的所有属性和方法,都能被实例所共享 13 14 // Animal 类 15 16 // Dog类 17 // 定义Animal 18 function Animal(){ 19 this.name = '大黄'; 20 this.colors = ['red','green','blue']; 21 } 22 Animal.prototype.getName = function(){ 23 return this.name; 24 } 25 26 function Dog(){}; 27 // Dog 继承了Animal 28 // 本质:重写原型对象,将一个父对象的属性和方法作为一个子对象的原型对象的属性和方法 29 Dog.prototype = new Animal(); 30 Dog.prototype.constructor = Dog; 31 32 var d1 = new Dog(); 33 var d2 = new Dog(); 34 35 36 console.log(d1.name); //大黄 37 console.log(d1.getName()); //大黄 38 39 40 console.log(d1.colors); //["red", "green", "blue"] 41 console.log(d2.colors); //["red", "green", "blue"] 42 // 给 d1 的 colors 添加一个 pink 43 d1.colors.push('pink'); 44 // 再次打印 ,发现 d2 的 colors 也增加了一个 pink (这就是问题的所在了) 45 console.log(d1.colors); // ["red", "green", "blue", "pink"] 46 console.log(d2.colors); // ["red", "green", "blue", "pink"] 47 48 // 问题1:父类中的实例属性一旦赋值给子类的原型属性,此时这些属性都属于子类的共享属性 49 // 问题2:实例化子类型的时候,不能向父类型的构造函数传参 50 51 52 </script> 53 </html>
原型链继承关系图
2、借用构造函数继承
借用构造函数继承也叫经典继承:在子类的构造函数内部调用父类的构造函数。
缺点:父类定义的共享方法不能被子类继承下来。
1 <!DOCTYPE html> 2 <html lang="en"> 3 4 <head> 5 <meta charset="UTF-8"> 6 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 7 <title>借用构造函数继承</title> 8 </head> 9 10 <body> 11 12 </body> 13 <script> 14 // 也叫经典继承:在子类的构造函数内部调用父类的构造函数 15 // 缺点:父类定义的共享方法不能被子类继承下来 16 17 function Animal(name) { 18 this.name = name; 19 this.colors = ['red', 'green', 'blue']; 20 } 21 Animal.prototype.getName = function () { 22 return this.name; 23 } 24 25 function Dog(name) { 26 // 通过 call 改变 this 指向,继承了 Animal 27 // 当 new 实例的时候,内部构造函数中this指向了d1,然后在当前构造函数内部再去通过 28 // call方法去调用父类构造函数,那么父类的构造函数中的this指向了d1,但是方法不能被继承下来。 29 Animal.call(this, name); 30 }; 31 32 var d1 = new Dog('大黄'); 33 var d2 = new Dog('小红'); 34 35 d1.colors.push('pink'); 36 console.log(d1.name); // 大黄 37 console.log(d2.name); // 小红 38 console.log(d1.colors); // ["red", "green", "blue", "pink"] 39 console.log(d2.colors); // ["red", "green", "blue"] 40 console.log(d1.getName()) // d1.getName is not a function 41 42 </script> 43 44 </html>
3、组合继承
缺点:无论在什么情况下,都会调用父类构造函数两次 1、一个是我们初始化子类的原型对象的时候 2、在子类构造函数内部调用父类的构造函数
1 <!DOCTYPE html> 2 <html lang="en"> 3 4 <head> 5 <meta charset="UTF-8"> 6 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 7 <title>组合继承</title> 8 </head> 9 10 <body> 11 12 </body> 13 <script> 14 function Animal(name) { 15 this.name = name; 16 this.colors = ['red', 'green', 'blue']; 17 } 18 Animal.prototype.getName = function () { 19 return this.name; 20 } 21 22 function Dog(name) { 23 // 让父类的实例属性继承下来,实例修改引用类型的值,另一个实例的引用类型的值不会发生变化 24 Animal.call(this, name); 25 }; 26 27 // 重写原型对象:把父类的共享方法继承下来 28 Dog.prototype = new Animal(); 29 Dog.prototype.constructor = Dog; 30 31 var d1 = new Dog('大黄'); 32 var d2 = new Dog('小红'); 33 34 d1.colors.push('pink'); 35 console.log(d1.name); // 大黄 36 console.log(d2.name); // 小红 37 console.log(d1.colors); // ["red", "green", "blue", "pink"] 38 console.log(d2.colors); // ["red", "green", "blue"] 39 console.log(d1.getName()) // 大黄 40 console.log(d2.getName()) // 小红 41 42 // 缺点:无论在什么情况下,都会调用父类构造函数两次 43 // 1、一个是我们初始化子类的原型对象的时候 44 // 2、在子类构造函数内部调用父类的构造函数 45 </script> 46 47 </html>
4、寄生组合式继承
寄生组合式继承通过
Object.create()
方法将父类原型上共享的方法浅拷贝给子类的原型对象,从而解决了组合继承的缺点。
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>寄生组合式继承</title> 7 </head> 8 <body> 9 10 </body> 11 <script> 12 function Animal(name) { 13 this.name = name; 14 this.colors = ['red', 'green', 'blue']; 15 } 16 Animal.prototype.getName = function () { 17 return this.name; 18 } 19 20 function Dog(name) { 21 // 让父类的实例属性继承下来,实例修改引用类型的值,另一个实例的引用类型的值不会发生变化 22 Animal.call(this, name); 23 }; 24 25 // 重写原型对象:把父类的共享方法继承下来 26 Dog.prototype = Object.create(Animal.prototype); 27 Dog.prototype.constructor = Dog; 28 29 var d1 = new Dog('大黄'); 30 var d2 = new Dog('小红'); 31 32 d1.colors.push('pink'); 33 console.log(d1.name); // 大黄 34 console.log(d2.name); // 小红 35 console.log(d1.colors); // ["red", "green", "blue", "pink"] 36 console.log(d2.colors); // ["red", "green", "blue"] 37 console.log(d1.getName()) // 大黄 38 console.log(d2.getName()) // 小红 39 40 </script> 41 </html>
5、多重继承
多重继承:一个对象同时继承多个对象
Object.assign(targetObj,copyObj)
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>多重继承</title> 7 </head> 8 <body> 9 10 </body> 11 <script> 12 // 多重继承:一个对象同时继承多个对象 13 // Person Parent Me 14 15 function Person(){ 16 this.name = 'Person'; 17 } 18 Person.prototype.sayName = function(){ 19 console.log(this.name); 20 } 21 22 // 定制 Parent 23 function Parent(){ 24 this.age = 18; 25 } 26 Parent.prototype.sayAge = function(){ 27 console.log(this.age); 28 } 29 30 function Me(){ 31 // 继承 Person和 Parent的属性 32 Person.call(this); 33 Parent.call(this); 34 } 35 // 继承Person的方法 36 Me.prototype = Object.create(Person.prototype); 37 // 不能重写原型对象来实现,对另一个对象的继承 38 // Me.prototype = Object.create(Parent.prototype); 39 40 // Object.assign(targetObj,copyObj) 41 // 将Parent原型对象中的方法拷贝给Me的原型对象 42 Object.assign(Me.prototype,Parent.prototype); 43 44 45 // 指定构造函数 46 Me.prototype.constructor = Me; 47 48 var me = new Me(); 49 console.log(me.name); //Person 50 console.log(me.age); //18 51 me.sayName(); //Person 52 me.sayAge(); //18 53 54 55 56 </script> 57 </html>
6、继承总结
1、原型链继承
特点:重写子类的原型对象,父类原型对象上的属性和方法都会被子类继承
问题:在父类中定义的实例引用类型的属性,一旦被修改,其他的实例也会被修改
当实例化子类的时候,不能传递参数到父类
2、借用构造函数模式
特点:在子类构造函数内部间接调用(call(),apply(),bind())父类的构造函数
原理:改变父类中的this指向
优点:仅仅是把父类中的实例属性当作子类的实例属性,并且可以传参
缺点:父类中共有的方法不能被继承下来
3、组合继承
特点:结合了原型链继承和借用构造函数继承的优点
原型链继承:公有的方法能被继承下来
借用构造函数:实例属性能被子类继承下来
缺点:调用了两次父类的构造函数
1、实例化子类对象
2、子类的构造函数内部 (好)
4、寄生组合式继承
var b = Object.create(a);
将a对象作为b实例的原型对象
把子类的原型对象指向了 父类的原型对象
Dog.prototype = Object.create(Animal.prototype);
开发过程中使用最广泛的一种继承模式