6种继承方式:
1.原型链继承
2.借用构造函数(经典继承)
3.组合继承
4.原型式继承
5.寄生式继承
6.寄生组合式继承
1.原型链:
缺点:所有实例会共享属性
新实例无法向父类构造函数传参
function FirstProto() { this.name = 'xiaoming'; this.colors = ['red', 'yellow']; }; function SecProto() {} SecProto.prototype = new FirstProto(); var instance1 = new SecProto(); instance1.colors.push('black'); console.log(instance1.name, instance1.colors); //xiaoming ["red", "yellow", "black"] var instance2 = new SecProto(); console.log(instance2.name, instance2.colors); //xiaoming ["red", "yellow", "black"]
2.借用构造函数 :各自继承,不会共享
function FirstProto(){ this.colors=['red','yellow']; }; function SecProto(){ FirstProto.call(this); } var instance1=new SecProto(); instance1.colors.push('black'); console.log(instance1.colors);//["red", "yellow", "black"] var instance2=new SecProto(); console.log(instance2.colors);//["red", "yellow"]
还可以传递参数
function FirstProto(col){ this.colors=['red','yellow',col]; }; function SecProto(){ FirstProto.call(this,'blue'); } var instance1=new SecProto(); instance1.colors.push('black'); console.log(instance1.colors);// ["red", "yellow", "blue", "black"] var instance2=new SecProto(); console.log(instance2.colors);// ["red", "yellow", "blue"]
存在的问题:方法都在构造函数中定义好了,不存在函数的复用
即:1.只是子类的实例,不是父类的实例
2.方法都在构造函数中定义,每次创建实例都会创建一遍方法
3.组合继承
思路是:使用原型链实现对原型方法的继承,而通过借用构造函数来实现对实例属性的继承。
function FirstProto(name){ this.colors=['red','yellow']; this.name=name; }; FirstProto.prototype.sayname=function(){ console.log(this.name); } function SecProto(name,age){ //继承属性 FirstProto.call(this,name); //第二次调用父类 this.age=age; } SecProto.prototype=new FirstProto(); //第一次调用父类 SecProto.prototype.constructor=SecProto; SecProto.prototype.sayage=function(){ console.log(this.age); } var instance1=new SecProto('Sun menghua',24); instance1.colors.push('black'); instance1.sayname();//Sun menghua instance1.sayage();//24 console.log(instance1.colors);//["red", "yellow", "black"] var instance2=new SecProto('Qi Haiyang',25); instance2.sayname();// Qi Haiyang instance2.sayage();//25 console.log(instance2.colors);//["red", "yellow"]
优点:融合原型链继承和构造函数的优点,是JavaScript中最常用的继承模式
缺点:调用了两次父类构造函数
(组合继承最大的问题是无论什么情况下,都会调用两次超类型构造函数:一次是在创建子类型原型的时候,另一次是在子类型构造函数内部)
缺点:调用了两次父类构造函数
(组合继承最大的问题是无论什么情况下,都会调用两次超类型构造函数:一次是在创建子类型原型的时候,另一次是在子类型构造函数内部)
4.原型式继承
基于已有的对象创建新对象
①obj()浅复制
对象的属性会共享
function obj(o){ function F(){} F.prototype=o; return new F(); } var person={ name:"zhangsan", age:18, friends:['Lisi','wangwu','zhaoliu'] }; var thePerson=obj(person); thePerson. name='Sunmenghua'; thePerson.friends.push('liulaogen'); console.log(thePerson.name); //Sunmenghua console.log(thePerson.friends); //["Lisi", "wangwu", "zhaoliu", "liulaogen"] var theOtherPerson=obj(person); theOtherPerson. name='Qi Haiyang'; theOtherPerson.friends.push('Da liu'); console.log(theOtherPerson.name); //Qi Haiyang console.log(theOtherPerson.friends); // ["Lisi", "wangwu", "zhaoliu", "liulaogen", "Da liu"] console.log(thePerson.name); //Sunmenghua console.log(thePerson.friends); // ["Lisi", "wangwu", "zhaoliu", "liulaogen", "Da liu"] console.log(person.name); //zhangsan
缺点: 包含引用类型的属性值始终都会共享相应的值, 这点跟原型链继承一样
注意: 这里修改了thePerson.name的值,theOtherPerson.name的值并未改变,并不是因为thePerson和theOtherPerson有独立的name值,而是thePerson.name="Sunmenghua'是给thePerson添加了name值,并非修改了原型上的name值。
因为我们找对象上的属性时,总是先找实例上对象,没有找到的话再去原型对象上的属性。实例对象和原型对象上如果有同名属性,总是先取实例对象上的值
还可采用Object.create()方法,具体见https://i.cnblogs.com/posts/edit;postId=8880863
5.寄生式继承
创建一个仅用于封装继承过程的函数,该函数在内部以某种形式来做增强对象,最后返回对象。
可以理解为在原型式继承的基础上新增一些函数或属性。
function obj(o) { function F() {} F.prototype = o; return new F(); } function createPerson(orig) { var thePerson = obj(orig); thePerson.sayHi = function() { console.log('hi'); }; thePerson.sex = 'female'; return thePerson; } var person = { name: "zhangsan", age: 18, friends: ['Lisi', 'wangwu', 'zhaoliu'] }; var newPerson = createPerson(person); newPerson.sayHi(); //hi console.log(newPerson.friends); //["Lisi", "wangwu", "zhaoliu"] console.log(newPerson.sex); //female
函数不能复用,降低效率,但新对象不仅具有person的属性和方法,还有自己的方法。
6.寄生组合式继承
// 寄生组合式继承 function Parent(name) { this.name = name; this.colors = ['blue', 'green']; } Parent.prototype.sayName = function() { console.log(this.name); } function Child(name, age) { Parent.call(this, name); this.age = age; } function CreateObj(o) { function F() {}; F.prototype = o; return new F(); } // Child.prototype = new Parent(); // 这里换成下面 function prototype(child, parent) { var prototype = CreateObj(parent.prototype); prototype.constructor = child; child.prototype = prototype; } prototype(Child, Parent); var child1 = new Child('xiaopao', 18); child1.colors.push('red'); console.log(child1); //name: "xiaopao", colors: ["blue", "green", "red"], age: 18 var child2 = new Child('xiao', 19); console.log(child2); //name: "xiao", colors: ["blue", "green"], age: 19
只调用了一次Parent构造函数,并且因此在避免了Parent.prototype上面创建不必要的多余的属性。普遍认为寄生组合式继承是引用类型最理想的继承方式