参考链接: js继承的6种方式
6种方法
1、原型链继承
核心:将父类的实例作为子类的原型
//父类 function superType(){ this.name='father'; this.colors= ['red','blue','green']; } //方法 superType.prototype.getName=function(){ console.log(this.name); console.log(this.colors); } //子类 function subType(){ // this.name='son'; 如果加上这句x.getName会输出son } //实质:把superType的实例赋值给subType的原型(实例包含指向原型对象的指针) //于是superType中的所有属性和方法,subType都有了。 subType.prototype = new superType(); console.log('原型链'); var x = new subType();//创建实例 var y = new subType(); x.getName();//输出father y.name='s'; y.getName();//输出s y.colors=['1','2','3'];//这是深拷贝(maybe)所以y改变了x不会改变 x.getName();//father ['red','blue','green']; y.getName();//s ['1','2','3'] //如果使用y.colors.push('a');则x和y的值会一样,即共享同一个引用类型属性
原型链的缺点:(所以其实实践过程中很少使用)
1.所有subType的实例共享一个superType的引用类型属性
2.创建subType时没办法向superType传参
实例可以继承的属性有:
实例的构造函数属性,父类原型的属性、父类构造函数属性
特点:
- 非常纯粹的继承关系,实例是子类的实例,也是父类的实例
- 父类新增原型方法/原型属性,子类都能访问到
- 简单,易于实现
缺点:
- 可以在构造函数中,为实例增加实例属性。如果要新增原型属性和方法,则必须放在new superType()这样的语句之后执行。
- 无法实现多继承
- 致命缺陷:来自原型对象的所有属性被所有实例共享
- 致命缺陷:创建子类实例时,无法向父类构造函数传参
2、构造继承(经典继承)
核心:使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类
function Person(name){ this.name=name; this.colors= ['red','blue','green']; } Person.prototype.getName=function(){ console.log(this.name); } //子类 function Child(){ Person.call(this,'a');//调用父类的构造函数实现继承效果 } //实例 var child1 = new Child(); var child2 = new Child(); console.log('构造函数'); child1.colors.push('white'); console.log(child1.name); // child2.getName();不能通过,因为这是父类的原型的方法 console.log(child1.colors);//['red', 'blue', 'green', 'yellow'] console.log(child2.colors);//['red', 'blue', 'green']
特点:
- 解决了1中,子类实例共享父类引用属性的问题
- 创建子类实例时,可以向父类传递参数
- 可以实现多继承(call多个父类对象)即继承多个构造函数属性
缺点:
- 实例并不是父类的实例,只是子类的实例
- 只能继承父类的实例属性和方法,不能继承父类原型属性/方法
- 无法实现函数复用,每个子类都有父类实例函数的副本,影响性能
3、组合继承(组合原型链继承和借用构造函数继承)(常用)
核心:结合了两种模式的优点,传参和复用
使用原型链实现对原型方法的继承,而通过借用构造函数来实现对实例属性的继承
function Parent(name){ this.name=name; this.arr=['red','blue']; } Parent.prototype.getName=function(){ console.log(this.name); } //构造函数的方法 function child(name,age){ Parent.call(this,name);//第二次调用Parent() this.age=age; } //原型链继承的方法 child.prototype = new Parent();//第一次调用Parent() var c1=new child('hhh',18); var c2=new child('yyy',19); console.log('组合继承'); c1.getName();//hhh c2.getName();//yyy console.log(c1.age);//18 console.log(c2.age);//19 c1.arr.push('green'); console.log(c1.arr);//['red', 'blue', 'green'] console.log(c2.arr);//['red', 'blue'] console.log(c1 instanceof child);//true console.log(c1 instanceof Parent);//true
特点:
- 可以继承父类原型上的属性,可以传参,可以复用
- 每个新实例引入的构造函数属性是私有的
缺点
- 覅用了两次父类构造函数(耗内存),子类的构造函数会代替原型上的那个父类构造函数。
4、原型式继承
核心:用一个函数包装一个对象,然后返回这个函数的调用,这个函数就变成了可以随意增添属性的实例或对象。object.create()就是这个原理
function createObj(o){ function f(){}//在内部创建一个临时构造函数, f.prototype = o;//将传入的对象作为这个构造函数的原型, return new f();//最后返回这个临时类型的新实例 } var person={//父类 name:'xiao', friend:['ddd','ccc'] }; var anotherPerson = createObj(person); var anotherPerson2 = createObj(person); console.log('原型式继承'); anotherPerson.name='hscod';//给anotherPerson添加了name值,但是并没有修改原型上的name值 anotherPerson.friend.push('aaa'); console.log(anotherPerson);//{name: 'hscod'} anotherPerson2.friend.push('bbb'); console.log(anotherPerson2);//{} console.log(person.friend);//[ 'ddd', 'ccc', 'aaa', 'bbb' ] //缺点和原型链继承一样,所有的引用类型会被共享
特点:类似于复制一个对象,用函数来包装。
缺点:1、所有实例都会继承原型上的属性。
2、无法实现复用。(新实例属性都是后面添加的)
5、寄生式继承
//创建一个仅用于封装继承过程的函数,该函数在内部以某种形式来做增强对象,最后返回对象。 //可以理解成在原型式继承的基础上新增一些函数或属性 console.log('寄生式继承'); var ob1=createObj(person); var ob2=Object.create(person);//ES5的规范写法 console.log(ob1.name);//xiao console.log(ob2.name);//xiao function createSon(s){//子类 var newob=createObj(s);//创建对象 newob.sayName = function(){//增强对象 console.log(this.name); } return newob;//指定对象 } var p1=createSon(person); p1.sayName();//xiao //缺点:和构造函数一样,每次创建对象都会创建一遍方法
重点:就是给原型式继承外面套了个壳子。
优点:没有创建自定义类型,因为只是套了个壳子返回对象(这个),这个函数顺理成章就成了创建的新对象。
缺点:没用到原型,无法复用。
6、寄生组合式继承(常用)
寄生:在函数内返回对象然后调用
组合:1、函数的原型等于另一个实例。2、在函数中用apply或者call引入另一个构造函数,可传参
//子类构造函数复制父类的自身属性和方法,子类原型只接受父类的原型属性和方法 function pa(name){ this.name=name; this.attr=['a','b']; } pa.prototype.speak = function(){ console.log('my name is '+this.name); } function ch(name,age){ pa.call(this,name); this.age=age; } function createOb(o){ function F(){}; F.prototype =o; return new F(); } //Child.prototype = new Parent(); function inheritPrototype(child,parent){ var prototype = createOb(parent.prototype);//创建对象 prototype.constructor=child;//增强对象 child.prototype=prototype;//指定对象 } inheritPrototype(ch,pa); console.log('寄生组合式'); var ch1=new ch('zyy','18'); console.log(ch1);//ch { name: 'zyy', attr: [ 'a', 'b' ], age: '18' } console.log(ch1 instanceof ch);//true console.log(ch1 instanceof pa);//true //优点:只调用了一次pa构造函数,因此避免了在pa.prototype上面创建不必要的多余的属性。