javascript有几种承继方法?

都0202年了,你还不知道javascript有几种承继办法?

前语
    当面试官问你:你了解js哪些承继办法?es6的class承继是怎么完成的?你心中有很明晰的答案吗?假如没有的话,能够经过阅览本文,协助你更深刻地了解js的一切承继办法。

    js承继一共分红5种,包含结构函数式承继、原型链式承继、组合式承继、寄生式承继和寄生组合式承继。

结构函数式承继

    首要来看第一种,结构函数式承继,望文生义,也便是运用函数去完成承继;

    假定咱们现在有一个父类函数:

// 父类结构函数
function Parent(color) {

this.color = color;
this.print = function() {
console.log(this.color);
}

}

现在要编写一个子类函数来承继这个父类,如下:

// 子类结构函数
function Son(color) {

Parent.call(this, color);

}
上面代码能够看到,子类Son是经过Parent.call的办法去调用父类结构函数,然后把this目标传进去,履行父类结构函数之后,子类Son就具有了父类界说的color和print办法。

调用一下该办法,输出如下:

// 测验
var son1 = new Son('red');
son1.print(); // red

var son2 = new Son('blue');
son2.print(); // blue
    能够看到son1和son2都正常承继了父类的print办法和各自传进去的color特点;

    以上便是结构函数式承继的完成了,这是最原始的js完成承继的办法;

    可是当咱们深化想一下会发现,这种底子就不是传统意义上的承继!

    由于每一个Son子类调用父类生成的目标,都是各自独立的,也便是说,假如父类期望有一个公共的特点是一切子类实例同享的话,是没办法完成的。什么意思呢,来看下面的代码:

function Flower() {

this.colors = ['黄色', '赤色'];
this.print = function () {
console.log(this.colors)
}

}

function Rose() {

Flower.call(this);

}

var r1 = new Rose();
var r2 = new Rose();

console.log(r1.print()); // [ '黄色', '赤色' ]
console.log(r2.print()); // [ '黄色', '赤色' ]

咱们现在有一个基类Flower,它有一个特点colors,现在咱们把某一个实例的colors值改一下:

r1.colors.push('紫色');

console.log(r1.print()); // [ '黄色', '赤色', '紫色' ]
console.log(r2.print()); // [ '黄色', '赤色' ]
    成果如上,明显,改动的只需r1的值,由于经过结构函数发明出来的实例目标中,一切的特点和办法都是实例内部独立的,并不会跟其他实例同享。

    总结一下结构函数的优缺陷:
长处:一切的根本特点独立,不会被其他实例所影响;
缺陷:一切期望同享的办法和特点也独立了,没有办法经过修正父类某一处来到达一切子实例一起更新的作用;一起,每次创立子类都会调用父类结构函数一次,所以每个子实例都复制了一份父类函数的内容,假如父类很大的话会影响功能;
原型链承继

    下面咱们来看第二种承继办法,原型链式承继;

    相同先来看下比如:

function Parent() {

this.color = 'red';
this.print = function() {
console.log(this.color);
}

}
function Son() {
}

咱们有一个父类和一个空的子类;

Son.prototype = new Parent();
Son.prototype.constructor = Son;
    接着咱们把子函数的原型特点赋值给了父函数的实例;

var son1 = new Son();
son1.print(); // red
    终究新建子类实例,调用父类的办法,成功拿到父类的color和print特点办法;

    咱们重点来剖析一下下面两行代码:
Son.prototype = new Parent();
Son.prototype.constructor = Son;
    这段代码中,子函数的原型赋给了父函数的实例,咱们知道prototype是函数中的一个特点,js的一个特性便是:假如一个目标某个特点找不到,会沿着它的原型往上去寻觅,直到原型链的终究才会中止寻觅。

    关于原型更多根底的常识,能够参阅一下其他文章,或许今后我也会出一期专门解说原型和原型链的文章。

    回到代码,咱们看到终究实例son成功调用了Print办法,输出了color特点,这是由于son从函数Son的prototype特点上面去找到的,也便是从new Parent这个目标里边找到的;

   
    这种办法也不是真实的承继,由于一切的子实例的特点和办法,都在父类同一个实例上了,所以一旦某一个子实例修正了其间的办法,其他一切的子实例都会被影响,来看下代码: 

function Flower() {

this.colors = ['黄色', '赤色'];
this.print = function () {
console.log(this.colors)
}

}

function Rose() {}
Rose.prototype = new Flower();
Rose.prototype.constructor = Rose;

var r1 = new Rose();
var r2 = new Rose();

console.log(r1.print()); // [ '黄色', '赤色' ]
console.log(r1.print()); // [ '黄色', '赤色' ]

r1.colors.push('紫色');

console.log(r1.print()); // [ '黄色', '赤色', '紫色' ]
console.log(r2.print()); // [ '黄色', '赤色', '紫色' ]

    仍是方才的比如,这次Rose子类挑选了原型链承继,所以,子实例r1修正了colors之后,r2实例的colors也被改动了,这便是原型链承继欠好的当地。

    来总结下原型链承继的优缺陷:
长处:很好的完成了办法的同享;
缺陷:正是由于什么都同享了,所以导致一切的特点都是同享的,只需某一个实例进行修正,那么一切的特点都会改变
组合式承继

    这儿来介绍第三种承继办法,组合式承继;

    这种承继办法很好了解,已然结构函数式承继和原型链承继都有各自的优缺陷,那么咱们把它们各自的长处整合起来,不就完美了吗?

 

组合式承继做的便是这个工作~来看一段代码比如:

​function Parent(color) {

this.color = color;

}
Parent.prototype.print = function() {

console.log(this.color);

}
function Son(color) {

Parent.call(this, color);

}
Son.prototype = new Parent();
Son.prototype.constructor = Son;

var son1 = new Son('red');
son1.print(); // red

var son2 = new Son('blue');
son2.print(); // blue

    上面代码中,在Son子类中,运用了Parent.call来调用父类结构函数,一起又将Son.prototype赋给了父类实例;为什么要这样做呢?为什么这样就能处理上面两种承继的问题呢?

    咱们接着剖析一下,运用Parent.call调用了父类结构函数之后,那么,今后一切经过new Son创立出来的实例,就独自复制了一份父类结构函数里边界说的特点和办法,这是前面结构函数承继所说到的相同的原理;

    然后,再把子类原型prototype赋值给父类的实例,这样,一切子类的实例目标就能够同享父类原型上界说的一切特点和办法。这也不难了解,由于子实例会沿着原型链去找到父类函数的原型。

    因而,只需咱们界说父类函数的时分,将私有特点和办法放在结构函数里边,将同享特点和办法放在原型上,就能让子类运用了。

    以上便是组合式承继,它很好的交融了结构函数承继和原型链承继,发挥两者的优势之处,因而,它算是真实意义上的承继办法。

寄生式承继

    已然上面的组合式承继都现已这么完美了,为什么还需求其他的承继办法呢?

    咱们细想一下,Son.prototype = new Parent();这行代码,它有什么问题没有?

    明显,每次咱们实例化子类的时分,都需求调用一次父类结构函数,那么,假如父类结构函数是一个很大很长的函数,那么每次实例化子类就会履行很长时刻。

    实际上咱们并不需求从头履行父类函数,咱们仅仅想要承继父类的原型。

    寄生式承继便是在做这个工作,它是根据原型链式承继的改良版:

var obj = {

color: 'red',
print: function() {
console.log(this.color);
}

};

var son1 = Object.create(obj);
son1.print(); // red

var son2 = Object.create(obj);
son2.print(); // red

寄生式承继本质上仍是原型链承继,Object.create(obj);办法意思是以obj为原型结构目标,所以寄生式承继不需求结构函数,可是相同有着原型链承继的优缺陷,也便是它把一切的特点和办法都同享了。

寄生组合式承继

    接下来到咱们终究一个承继办法,也便是现在业界最为完美的承继处理方案:寄生组合式承继。

    没错,它便是es6的class语法完成原理。

    可是假如你了解了组合式承继,那么了解这个办法也很简单,只需记住,它呈现的首要意图,是为了处理组合式承继中每次都需求new Parent导致的履行多一次父类结构函数的缺陷。

    下面来看代码:

function Parent(color) {

this.color = color;

}
Parent.prototype.print = function() {

console.log(this.color);

}
function Son(color) {

Parent.call(this, color);

}
Son.prototype = Object.create(Parent.prototype);
Son.prototype.constructor = Son;

var son1 = new Son('red');
son1.print(); // red

var son2 = new Son('blue');
son2.print(); // blue

    这段代码不同之处只需一个,便是把本来的Son.prototype = new Parent();修正为了Son.prototype = Object.create(Parent.prototype);

    咱们前面讲过,Object.create办法是以传入的目标为原型,创立一个新目标;创立了这个新目标之后,又赋值给了Son.prototype,因而Son的原型终究指向的其实便是父类的原型目标,和new Parent是相同的作用;

 
    到这儿,咱们5中js的承继办法也就讲完了;
 

    假如你对上面的内容感到疑问或许不了解的,能够留言和我沟通,或许重视大众号直接联络我~

    终究感谢小伙伴的阅览,假如觉得文章写的还能够的话,欢迎点个赞、点个重视,我会继续输出优质的技能共享文章。​
原文地址https://www.cnblogs.com/oujiamin/p/12924625.html