Step-By-Step
Step-By-Step copied to clipboard
借用构造函数和组合继承(伪经典继承)的优缺点是什么?
- 首先借用构造函数继承,指的是继承父类的属性,同时还能传递参数,如
function SuperType(name){
this.name = name
// ...
}
function SubType(){
// 继承了SuperType,同时还可以传递参数
SuperType.call(this)
}
优点:实现属性的继承 缺点:不能继承父类的方法
- 组合继承(伪经典继承)指的是将原型链和构造函数组合到一块,如
function SubType(age) {
// 继承属性
SuperType.call(this, '组合')
this.age = age
}
// 继承方法
SubType.prototype = new SuperType()
SubType.prototype.constructor = SubType // 修正constructor指向
SubType.prototype.sayAge = function(){
alert(this.age)
}
优点:可以实现属性、以及方法的继承 缺点: 调用了2次父类的构造函数,一次是SuperType.call(),还有一次是new SuperType()
构造函数和组合继承
function Animal () {
this.type =['origin']
}
Animal.prototype.tellOrigin = function () {
console.log(this.type)
}
function Dog () {
this.ownType = 'dog'
Animal.call(this)
}
const dog = new Dog() // new 使 this 指向对象
const dog1 = new Dog()
dog1.type.push('hello')
console.log(dog.type) // ['origin']
console.log(dog1.type) // [ 'origin', 'hello']
dog.tellOrigin() // 会报错
如上面代码所示:
- 优点 父类的属性能被子类继承,切不会被所有子类共用
- 缺点 父类的原型对象方法不能被使用
组合继承就是,原型继承和构造函数继承的组合
function Animal () {
this.type =['origin']
}
Animal.prototype.tellOrigin = function () {
console.log(this.type)
}
function Dog () {
this.ownType = 'dog'
Animal.call(this)
}
Dog.prototype = new Animal()
const dog = new Dog()
const dog1 = new Dog()
dog1.type.push('hello')
console.log(dog.type) // ['origin']
console.log(dog1.type) // [ 'origin', 'hello']
dog.tellOrigin() // ['origin']
完美解决了,原型继承和构造函数的缺点
借用构造函数
借用构造函数的技术,其基本思想为:
在子类型的构造函数中调用超类型构造函数。
function SuperType(name) {
this.name = name;
this.colors = ['pink', 'blue', 'green'];
}
function SubType(name) {
SuperType.call(this, name);
}
let instance1 = new SubType('Yvette');
instance1.colors.push('yellow');
console.log(instance1.colors);//['pink', 'blue', 'green', yellow]
let instance2 = new SubType('Jack');
console.log(instance2.colors); //['pink', 'blue', 'green']
优点:
- 可以向超类传递参数
- 解决了原型中包含引用类型值被所有实例共享的问题
缺点:
- 方法都在构造函数中定义,函数复用无从谈起,另外超类型原型中定义的方法对于子类型而言都是不可见的。
组合继承(原型链 + 借用构造函数)
组合继承指的是将原型链和借用构造函数技术组合到一块,从而发挥二者之长的一种继承模式。基本思路:
使用原型链实现对原型属性和方法的继承,通过借用构造函数来实现对实例属性的继承,既通过在原型上定义方法来实现了函数复用,又保证了每个实例都有自己的属性。
function SuperType(name) {
this.name = name;
this.colors = ['pink', 'blue', 'green'];
}
SuperType.prototype.sayName = function () {
console.log(this.name);
}
function SuberType(name, age) {
SuperType.call(this, name);
this.age = age;
}
SuberType.prototype = new SuperType();
SuberType.prototype.constructor = SuberType;
SuberType.prototype.sayAge = function () {
console.log(this.age);
}
let instance1 = new SuberType('Yvette', 20);
instance1.colors.push('yellow');
console.log(instance1.colors); //[ 'pink', 'blue', 'green', 'yellow' ]
instance1.sayName(); //Yvette
let instance2 = new SuberType('Jack', 22);
console.log(instance2.colors); //[ 'pink', 'blue', 'green' ]
instance2.sayName();//Jack
优点:
- 可以向超类传递参数
- 每个实例都有自己的属性
- 实现了函数复用
借用构造函数的基本思想是在子类型构造函数的内部调用超类型构造函数。 借用构造函数的优点:可以在子类型构造函数中向超类型构造函数传递参数。 function SuperType(name){ This.name = name; } Function SubType(){ //继承了SuperType,同时还传递了参数 SuperTYpe.call(this,’yay’); //实例属性 this.age = 29; } Var instance = new SubType(); Console.log(instance.name); //yay Console.log(instance.age); //29 借用构造函数的缺点:无法实现函数复用
组合继承:指的是将原型链和借用构造函数的技术组合到一块,从而发挥二者之长的一种继承模式。其背后的思路是使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。 function SuperTYpe(name){ This.name = name; This.color = [‘red’,’blue’,’green’]; } SuperType.prototype.sayName = function(){ Console.log(this.name); } Function SubType(name,age){ SuperType.call(this,name); This.age = age; } //继承方法 SubType.protype = new SuperType(); SubType.prototype.constructor = SubType; SubType.prototype.sayAge = function(){ Console.log(this.age); } Var instance1 = new SubType(‘yay’,29); Instance1.color.push(‘black’); Console.log(instance1.color); //red,blue,green,black Instance1.sayName(); //yay Instance.sayAge(); //29
Var instance2 = new SubType(‘Greg’,27); Console.log(instance.color); //red,blue,green Instance2.sayName(); //Greg Instance2.sayAge(); //27
组合继承的优点:可以实现函数复用;可以向超类传递参数;每个实例可以拥有自己的属性
借用构造函数继承
function Parent( name ) {
this.name = name;
}
function Child(name) {
Parent.call(this, name)
}
let child1 = new Child('zhangsan')
console.log(child1.name)
let child2 = new Child('lisi')
console.log(child2.name)
优缺点: 优点:避免了实例之间属性的共享,可以向构造函数传参; 缺点:方法都在构造函数中实现,每创建一个实例,都要调用一次方法,没有实现函数的复用。
组合继承
组合继承是原型链继承和构造函数继承相结合。
function Parent(name) {
this.name = name;
}
Parent.prototype.getName = function() {
console.log(this.name)
}
function Child(name) {
Parent.call(this, name)
}
Child.prototype = new Parent()
let child1 = new Child('zhangsan')
console.log(child1.getName())
let child2 = new Child('lisi')
console.log(child2.getName())
优缺点: 优点:具有构造函数继承的优点,同时可以实现函数的复用 缺点:调用了2次父类的构造函数,一次是Parent.call(),一次是new Parent()
借用构造函数 使用原型链继承的一个缺点就是如果原型是另一个类型的实例并且这个实例对象包含一个引用类型的值,那么通过这个实例创建的其他实例都会共享这个引用类型的值,在很多业务情况下可能并不是我们想要的。借用构造函数的本质就是在子类型的构造函数中调用超类型的构造函数,这样即使超类型中包含的有引用类型的属性值那么也可以让子类型的每一个实例对象获取一个副本,子类型的对象之间互不影响~
function SuperType(type) {
this.type = type;
this.friends = ['a', 'b'];
}
function SubType() {
SuperType.call(this, 'SubType');
}
let sub1 = new SubType();
let sub2 = new SubType();
sub1.friends.push('c');
console.log(sub1.friends); // ['a', 'b', 'c']
console.log(sub2.friends); // ['a', 'b']
优点
- 可以给父类的构造函数传递参数
- 可以解决引用类型的值带来的问题
缺点
- 没有利用原型链,无法继承超类型原型链上的方法和属性
组合式继承
将原型链继承和借用构造函数结合起来,使用原型链实现对原型属性和方法的继承,通过借用构造函数来实现对实例属性的继承。 优点 1.在原型上定义方法实现函数的复用 2.保证每个实例都有自己的属性
function SuperType(type) {
this.type = type;
this.friends = ['a', 'b'];
}
SuperType.prototype.getFriends = function() {
return this.friends;
}
function SubType() {
SuperType.call(this, 'SubType');
}
SubType.prototype = new SuperType();
let sub1 = new SubType();
let sub2 = new SubType();
sub1.friends.push('c');
console.log(sub1.getFriends()); // ['a', 'b', 'c']
console.log(sub2.getFriends()); // ['a', 'b']
借用构造函数继承?
function Super(name){
this.name = name
}
function Suber(name){
Super.call(this,name)
}
const sub1 = new Suber('单身狗');
const sub2 = new Suber('没女票');
缺点: 无法继承原型链上的属性和方法 优点: 可以向构造函数传参,避免了原型链继承时引用类型的属性被共享。
组合式继承
function Super(text){
this.text = text
}
Super.prototype.say=function(){
console.log(this.text)
}
function Suber(text){
Super.call(this,text)
}
Suber.prototype = new Super()
var sub1 = new Suber('2019/6/26 17:53 publish on github')
var sub2 = new Suber('2019/6/26 17:53 地铁可能已开始拥挤')
sub1.say()
sub2.say()
优点: 组合式继承 将原型链继承 与借用构造函数继承 结合 使用原型链继承 原型上的方法和属性 使用构造函数 继承实例上的属性。
缺点 : 调用两次构造函数 Super.call(), new Super()
1.借用构造函数 通过使用call方法,我们再未来将要新创建的SubType实例环境下调用了 SuperType构造函数,这样一来,就会在新的SubType对象上执行SuperType()函数中定义的所有对象初始化代码。这样,SubType的每个实例都会具有自己的colors属性的副本。
function SuperType(name){
this.name=name
this.colors=['white','red']
}
function SubType(){
//继承属性,传递参数
SuperType.call(this,'Jodie')
this.age=18
}
var instance1=new SubType()
instance1.colors.push('black')
console.log(instance1.name)
console.log(instance1.age)
console.log(instance1.colors)
var instance2=new SubType()
console.log(instance2.name)
console.log(instance2.age)
console.log(instance2.colors)
Jodie
18
[ 'white', 'red', 'black' ]
Jodie
18
[ 'white', 'red']
问题: 超类型的原型定义的方法对子类型而言是不可见的。
function SuperType(name){
this.name=name
this.colors=['white','red']
this.sayHi=function(){
console.log('123')
}//方法都在构造函数中定义
}
function SubType(){
//继承属性,传递参数
SuperType.call(this,'Jodie')
this.age=18
}
var instance1=new SubType()
instance1.colors.push('black')
console.log(instance1.name)
console.log(instance1.age)
console.log(instance1.colors)
instance1.sayHi()
2.组合继承 将原型链和借用构造函数的技术组合到一起,从而发挥二者所长的一种继承模式。 背后的思路就是使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。这样,既通过原型上定义方法实现函数复用,有能够保证每个实例都有自己的属性。
function SuperType(name){
this.name=name;
this.colors=["red","blue","green"]
}
SuperType.prototype.sayName=function(){
console.log(this.name)
}
function SubType(name,age){
//继承属性
SuperType.call(this,name)
this.age = age
}
//继承方法
SubType.prototype = new SuperType();
SubType.prototype.constructor=SubType
SubType.prototype.sayAge = function(){
console.log(this.age)
}
var instance1 = new SubType("Jodie","18")
instance1.colors.push("black")
console.log(instance1.colors)
instance1.sayName()
instance1.sayAge()
var instance2 = new SubType('Wen','5')
console.log(instance2.colors)
instance2.sayName()
instance2.sayAge()
[ 'red', 'blue', 'green', 'black' ]
Jodie
18
[ 'red', 'blue', 'green' ]
Wen
5
这是JavaScript中最常用的继承模式
1.借用构造函数 在解决原型中包含的引用类型所带来的问题的过程中,开发人员开始使用一种叫做构造函数的技术。这种技术的基本思想相当简单,即在子类型函数的内部调用超类型构造型构造函数。
function SuperType(){
this.colors = ['red','blue','green'];
}
function SubType(){
//继承superType
SuperType.call(this)
}
var instancel = new SubType();
instancel.colors.push("blcak");
console.log(instancel.colors) // ["red","blue","green","blcak"]
var instance2 = new SubType();
console.log(instance2.colors) //["red", "blue", "green"]
借用构造函数的基本思想就是利用call或apply把父类中通过this指定的属性和方法复制(借用)到子类创建的实例中,因为this对象是在运行时基于函数的执行环境绑定的。 优势:相对于原型链而言,借用构造函数有一个很大的优势,即可以在子类型构造函数中向超类型构造函数传参。因为属性是绑定带this上,所以调用时互不影响。 劣势:仅借用构造函数,将无法避免方法都在构造函数中定义,因此函数复用就无从谈起。
2.组合继承(又称 伪经典继承) 将原型链和借用构造函数的技术结合到一起,从而发挥二者之长的一种继承模式。
function SuperTypes(name){
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperTypes.prototype.sayName = function(){
console.log(this.name)
}
function SubTypes(name,age){
//继承属性
SuperTypes.call(this,name)
this.age = age
}
//继承方法
SubTypes.prototype = new SuperTypes()
SubTypes.prototype.constructor = SubTypes;
SubTypes.prototype.sayAge = function () {
console.log(this.age)
}
var instance3 = new SubTypes("Nicholas", 29);
instance3.sayAge(); //29
instance3.sayName(); //Nicholas
var instance4 = new SubTypes("Greg", 27);
优势:组合继承避免了原型链和借用构造函数的缺陷,融合了它们的优点,成为js中最常用的继承模式. 劣势:组合继承最大的问题是无论什么情况下,都会调用两次超类型的构造函数:一次在创建子类型原型的时候,另一次在子类型构造函数内部。输入子类型最终会包含超类型对象的全部实例属性,但我们不得不在调用子类型构造函数时重写这些属性。
借用构造函数的优缺点
代码实现:
function Person(name) {
this.name = name
this.className = 'person'
}
Person.prototype.getName = function () {
console.log(this.name)
}
function Man(name) {
Person.apply(this, arguments)
}
优点: 只继承了父类的属性 缺点: 没有继承方法
组合继承(伪经典继承)的优缺点
这里组合是指同事继承之类构造函数和原型中的属性
function Person(name) {
this.name = name
this.className = 'person'
}
Person.prototype.getName = function () {
console.log(this.name)
}
function Man(name) {
Person.apply(this, arguments)
}
// 注意此处
Man.prototype = Object.create(Person.prototype);
Man.prototype.constructor = Man
var man1 = new Man ("Davin")
这里用到了Object.create()方法,该方法会对传入obj对象进行前拷贝。和上面的主要区别就是:将父类的原型复制给了之类原型。
- 构造函数中继承分类属性/方法,并初始化父类
- 子类原型和父类原型建立联系 优点:结合了原型继承和构造函数继承 缺点:无论什么情况下,会调用两次构造函数Person.call(), new Person()
借用构造函数(伪造对象或经典继承)
基本思想:在子类型构造函数的内部调用超类型的构造函数
优点: 相比原型链而言,借用构造函数有一个很大的优势,就是子类型函数构造函数可以向超类型构造函数传递参数
缺点: 方法都在构造函数中定义,因此函数的复用性就无从谈起了
function Animal(speices){
this.speices = speices;
}
function Dog(){
Animal.call(this,'中华田园犬'); // dog拥有了animal中的name属性
}
var dog1 = new Dog();
var dog2 = new Dog();
console.log(dog1.speices); // 中华田园犬
组合继承(原型链+借用构造函数)
- 基本思想: 原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承
- 优点: 在原型上定义方法是实现了函数复用,又能偶保证每个实例都有它自己的属性
function Animal(speices){
this.speices = speices;
this.skills = ["jump","climb","catch","run"]
}
Animal.prototype.getSpeices = function(){
console.log(this.speices)
}
function Dog(speices,color){
// 借用构造函数继承,继承父类的属性
Animal.call(this,speices);
this.color = color;
}
// 原型继承,继承父类的方法
Dog.prototype = new Animal();
Dog.prototype.getColors = function(){
console.log(this.colors);
}
var dog1 = new Dog('博美','white');
dog1.skills.push('acting');
console.log(dog.skills); // ["jump","climb","catch","run","acting"]
dog1.getSpeices(); // 博美
var dog2 = new Dog('柯基','brown');
console.log(dog2.skills); // ["jump","climb","catch","run"]
dog2.getSpeices(); // 柯基
组合继承融合借用构造函数(属性继承)和原型链继承(方法继承)的优点,使实例都有自己的属性,又能共享统一方法,成为最常用的继承方法