菜单

JavaScript继承基础讲解(原型链、借用构造函数、混合模式、原型式继承、寄生式继承、寄生组合式继承),javascript构造函数

2018年11月16日 - JavaScript

平篇稿子理解JS继承——原型链/构造函数/组合/原型式/寄生式/寄生组合/Class extends

2018/08/02 · JavaScript
· 继承

初稿出处:
随即是您的玩具车吗   

说其实话,以前自己仅待理解“寄生组合继承”是无与伦比好的,有只祖传代码模版用便实施。最近坐有事情,几独星期以来直心心念念想整理出来。本文为《JavaScript高级程序设计》上之情节呢骨架,补充了ES6
Class的有关内容,从自身觉得重新便于掌握的角度将持续这档子事叙述出来,希望大家会有所获。

JavaScript继承基础讲解(原型链、借用构造函数、混合模式、原型式继承、寄生式继承、寄生组合式继承),javascript构造函数

说好的讲解JavaScript继承,可是迟迟交本教学。废话不多说,直接进去正题。

  既然你想询问继承,证明你对JavaScript面向对象已经来早晚之摸底,如还有什么不知道的足参照《面向对象JS基础讲解,工厂模式、构造函数模式、原型模式、混合模式、动态原型模式》,接下说话一般通过那些方法就JavaScript的接续。

  原型链

  JavaScript中贯彻持续最简便的措施就是动原型链,将子类型的原型指向父类型的实例即可,即“子类型.prototype
= new 父类型();”,实现方式如下:

// 为父类型创建构造函数
function SuperType() {
  this.name = ['wuyuchang', 'Jack', 'Tim'];
  this.property = true;
}

// 为父类型添加方法
SuperType.prototype.getSuerperValue = function() {
  return this.property;
}

// 为子类型创建构造函数
function SubType() {
  this.test = ['h1', 'h2', 'h3', 'h4'];
  this.subproperty = false;
}

// 实现继承的关键步骤,子类型的原型指向父类型的实例
SubType.prototype = new SuperType();

// 在此处给子类型添加方法,一定要在实现继承之后,否则会在将指针指向父类型的实例,则方法为空
SubType.prototype.getSubValue = function() {
  return this.subproperty;
}


/* 以下为测试代码示例 */
var instance1 = new SubType();
instance1.name.push('wyc');
instance1.test.push('h5');
alert(instance1.getSuerperValue());    // true
alert(instance1.getSubValue());      // false
alert(instance1.name);          // wuyuchang,Jack,Tim,wyc
alert(instance1.test);          // h1,h2,h3,h4,h5


var instance2 = new SubType();
alert(instance2.name);          // wuyuchang,Jack,Tim,wyc
alert(instance2.test);          // h1,h2,h3,h4

得视而达到的代码就是经原型链实现之一个简的接续,但相测试代码示例中要在些问题。相信看了自己之博文《面向对象JS基础讲解,工厂模式、构造函数模式、原型模式、混合模式、动态原型模式》的童鞋一定懂原型链代码存在的首先只问题是出于子类型的原型是父类型的实例,也就算是子类型的原型中含有的父类型的特性,从而造成引用类型值的原型属性会于所有实例所共享。以上代码的instance1.name.push(‘wyc’);就好说明这个题材的在。而原型链的次只问题就是是:于开创子类型的实例时,不克往超类型的构造函数中传递参数。因此我们当其实开支被,很少单独行使原型链。 

   借用构造函数

  为了化解原型链中存在的鲜个问题,开发人员开始使用相同种植叫做借用构造函数的技能来化解原型链中存在的问题。这种技术的落实思路也杀简单,只需要在子类型的构造函数内调用父类型的构造函数即可。别忘了,函数只不过是当特定环境遭受执代码的目标,因此可透过apply()或call()方法执行构造函数。代码如下:

// 为父类型创建构造函数
function SuperType(name) {
  this.name = name;
  this.color = ['pink', 'yellow'];
  this.property = true;

  this.testFun = function() {
    alert('http://tools.jb51.net/');
  }
}

// 为父类型添加方法
SuperType.prototype.getSuerperValue = function() {
  return this.property;
}

// 为子类型创建构造函数
function SubType(name) {
  SuperType.call(this, name);
  this.test = ['h1', 'h2', 'h3', 'h4'];
  this.subproperty = false;
}

// 在此处给子类型添加方法,一定要在实现继承之后,否则会在将指针指向父类型的实例,则方法为空
SubType.prototype.getSubValue = function() {
  return this.subproperty;
}


/* 以下为测试代码示例 */
var instance1 = new SubType(['wuyuchang', 'Jack', 'Nick']);
instance1.name.push('hello');
instance1.test.push('h5');
instance1.color.push('blue');
instance1.testFun();            // http://tools.jb51.net/
alert(instance1.name);            // wuyuchang,Jack,Nick,hello
// alert(instance1.getSuerperValue());    // error 报错
alert(instance1.test);            // h1,h2,h3,h4,h5    
alert(instance1.getSubValue());        // false    
alert(instance1.color);            // pink,yellow,blue

var instance2 = new SubType('wyc');
instance2.testFun();            // http://tools.jb51.net/
alert(instance2.name);            // wyc    
// alert(instance2.getSuerperValue());    // error 报错
alert(instance2.test);            // h1,h2,h3,h4
alert(instance2.getSubValue());        // false
alert(instance2.color);            // pink,yellow

可观看上述代码中子类型SubType的构造函数内通过调用父类型”SuperType.call(this,
name);”,从而实现了性能的持续,也堪在子类型创建实例的上吧父类型传递参数了,但新的题材还要来了。可以见见我在父类型的构造函数中定义了一个计:testFun,在父类型的原型中定义了一个艺术:getSuperValue。可是以实例化子类型后还是是无力回天调用父类型的原型中定义之计getSuperValue,只能调用父类型中构造函数的不二法门:testFun。这便和创建对象中只使用构造函数模式一样,使得函数没有复用性可言。考虑到这些题材,借用构造函数的技能呢是杀少单独采取的。

构成继承(原型链+借用构造函数)

  顾名思义,组合继承就是结合使用原型链与假构造函数的长处,组合而成的一个模式。实现呢甚粗略,既然是组成,那本做了零星方的亮点,即原型链继承方法,而于构造函数继承属性。具体代码实现如下:

// 为父类型创建构造函数
function SuperType(name) {
  this.name = name;
  this.color = ['pink', 'yellow'];
  this.property = true;

  this.testFun = function() {
    alert('http://tools.jb51.net/');
  }
}

// 为父类型添加方法
SuperType.prototype.getSuerperValue = function() {
  return this.property;
}

// 为子类型创建构造函数
function SubType(name) {
  SuperType.call(this, name);
  this.test = ['h1', 'h2', 'h3', 'h4'];
  this.subproperty = false;
}

SubType.prototype = new SuperType();

// 在此处给子类型添加方法,一定要在实现继承之后,否则会在将指针指向父类型的实例,则方法为空
SubType.prototype.getSubValue = function() {
  return this.subproperty;
}


/* 以下为测试代码示例 */
var instance1 = new SubType(['wuyuchang', 'Jack', 'Nick']);
instance1.name.push('hello');
instance1.test.push('h5');
instance1.color.push('blue');
instance1.testFun();            // http://tools.jb51.net/
alert(instance1.name);            // wuyuchang,Jack,Nick,hello
alert(instance1.getSuerperValue());      // true
alert(instance1.test);            // h1,h2,h3,h4,h5    
alert(instance1.getSubValue());        // false    
alert(instance1.color);            // pink,yellow,blue

var instance2 = new SubType('wyc');
instance2.testFun();            // http://tools.jb51.net/
alert(instance2.name);            // wyc    
alert(instance2.getSuerperValue());      // true
alert(instance2.test);            // h1,h2,h3,h4
alert(instance2.getSubValue());        // false
alert(instance2.color);            // pink,yellow

以上代码通过SuperType.call(this,
name);继承父类型的习性,通过SubType.prototype = new
SuperType();继承父类型的法门。以上代码很方便之解决了原型链与假构造函数所遇到的问题,成为了JavaScript中极常用的实例继承的道。但混合模式吗无须无缺陷,可以看到在以上代码中在持续方法的上实在已经持续了父类型的性能,只不过此时对此引用类型属于共享的,因此在子类型的构造函数内在次调用父类型的构造函数从而持续了父类型的习性而错过盖了原型中所累的属性,这样调用单薄不善构造函数显然没必要,但出啊方式可缓解吗?在解决这个题材常常事先押之下简单个模式。

原型式继承

  原型式继承的底落实方式以及一般继承的实现方式不同,原型式继承并不曾使用严格意义及之构造函数,而是借助原型可以因已部分对象创建新目标,同时还不必因此创立于定义类型。具体代码如下:

function object(o) {
  function F() {}
  F.prototype = o;
  return new F();
}

代码示例:

/* 原型式继承 */
function object(o) {
  function F() {}
  F.prototype = o;
  return new F();
}

var person = {
  name : 'wuyuchang',
  friends : ['wyc', 'Nicholas', 'Tim']
}

var anotherPerson = object(person);
anotherPerson.name = 'Greg';
anotherPerson.friends.push('Bob');

var anotherPerson2 = object(person);
anotherPerson2.name = 'Jack';
anotherPerson2.friends.push('Rose');

alert(person.friends);  // wyc,Nicholas,Tim,Bob,Rose

寄生式继承

/* 寄生式继承 */
function createAnother(original) {
  var clone = object(original);
  clone.sayHi = function() {
    alert('hi');
  }
  return clone;
}

下示例:

/* 原型式继承 */
function object(o) {
  function F() {}
  F.prototype = o;
  return new F();
}

/* 寄生式继承 */
function createAnother(original) {
  var clone = object(original);
  clone.sayHi = function() {
    alert('hi');
  }
  return clone;
}

var person = {
  name : 'wuyuchang',
  friends : ['wyc', 'Nicholas', 'Rose']
}
var anotherPerson = createAnother(person);
anotherPerson.sayHi();

寄生组合式继承

  前面说了了JavaScrip中组成模式实现持续的短处,现在咱们即便来解决其的短,实现思路是,对于构造函数继承属性,而原型链的混成形式继续方法,即未用当继续方法的下实例化父类型的构造函数。代码如下:

function object(o) {
  function F() {}
  F.prototype = o;
  return new F();
}

/* 寄生组合式继承 */
function inheritPrototype(subType, superType) {
  var prototype = object(superType.prototype);
  prototype.constructor = subType;
  subType.prototype = prototype;
}

假定于运时仅需要拿成模式受到之“SubType.prototype = new
SuperType();”这行代码替换成inheritPrototype(subType,
superType);即可。寄生组合式继承的赛效率体现于其只是调用了同样潮父类型构造函数,避免了创不必要之要多余的性能。与此同时,原型链还会维持无转移,因此,还能够正常使用instanceof和isPrototypeof()。这为是眼下以来最好理想的继承方式了,目前啊于通往这种模式转型。(YUI也祭了这种模式。)

此博文参考《JavaScript高级程序设计第3本子》,代码为经改写,更切实,并加了诠释使大家还易于亮。如针对JS继承方面来独到见解的童鞋不别吝啬,回复您的观点供大家参考!

1. 蝉联分类

先期来单整印象。如图所示,JS中继承可以按是否用object函数(在下文中会提到),将持续分成两局部(Object.create是ES5新增加的法子,用来规范化是函数)。

里面,原型链继承和原型式继承来平等的利害,构造函数继承和寄生式继承也互相照应。寄生组合继承基于Object.create,
同时优化了咬合继承,成为了圆满的存续方式。ES6 Class
Extends的结果以及寄生组合继承基本一致,但是贯彻方案同时略有不同。

脚这上正题。

图片 1

以JavaScript的原型链继承方式面临,为何子类在调用父类的构造函数时未可以污染参数?

原先自己在圈开时也赶上了这样的题材,找了不少材料都无明了的说明。
自家当,并无是语法上无克落实对构造函数的参数传递,而是这样做不符合面向对象编程的规则:对象(实例)才是性之拥有者。
要是在子类定义时便以属于性赋了价值,对象实例就非可知重复转移自己的性质了。这样就是变成了近乎具有属性,而无是目标具备属性了。
推个例子,子类 Children 继承父类 Parents,Parents 构造函数:
function Parents(name){ this.name=name; }
行使原型链并受父类构造函数传参数:
Children.prototype=new Parents(“Hello”);
这就是说这,Children 类就持有了 name=“Hello” 属性,而 Children
类的实例对象 c1、c2、c3 等等只能被迫奉之 name 属性。Children 是
“Hello” 的拥有者而 c1、 c2、c3免是!
这样形容了失去了面向对象编程的意思,所以于原型链继承方式被确定未可知对父类构造函数传递参数。也因为这个原因,原型链继承方式并无实用。
 

2. 继续方式

达成图上半区的原型链继承,构造函数继承,组合继承,网上内容比较多,本文不作详细描述,只指出要。这里叫起了本人看最容易了解的如出一辙篇《JS中之接轨(上)》。如果对上半区的情节无熟识,可以先押这首文章,再回去继续读书;如果既于熟悉,这片可以便捷小过。另,上半区大气借出了yq前端的同等首延续篇[1]。

JS 类继承与原型继承不同

类式继承就比如java的存续一样,思想吗比较简单:在子类型构造函数的内调用超类型构造函数。

原型式继承是依靠已部分对象创建新的目标,将子类的原型指向父类,就相当给加盟了父类这漫长原型链

比方而的 下面这段代码不是严意义上的类式继承,按照Nicholas
C.Zakas的传教,这个理应叫组合式继承。它调用了个别次parent2()。第一软是
child2.prototype=new parent2(‘param’);
child2尽管会见赢得两单属性param,getParam(),他们都是parent2的性质,但是他们当child2的原型中。第二赖是parent2.call(this,cparam);
这次又在初目标及创造了实例属性param,getParam()。于是,这点儿独属性就挡了原型中的少单同名属性。这发生啊利益呢,就是您当构建一个child3时吧延续parent2()的属性,还可定义自己的性能。与此同时他丰富的就算同外兄弟不同了,但以有一致的“血统(使用父类的不二法门)”。

纯手打,欢迎继续讨论
 

http://www.bkjia.com/Javascript/864936.htmlwww.bkjia.comtruehttp://www.bkjia.com/Javascript/864936.htmlTechArticleJavaScript继承基础讲解(原型链、借用构造函数、混合模式、原型式继承、寄生式继承、寄生组合式继承),javascript构造函数
说好之讲解Java…

2.1 原型式继承

着力:将父类的实例作为子类的原型

SubType.prototype = new SuperType() //
所有关乎到原型链继承的继续方式还设改子类构造函数的对准,否则子类实例的构造函数会指向SuperType。
SubType.prototype.constructor = SubType;

1
2
3
SubType.prototype = new SuperType()
// 所有涉及到原型链继承的继承方式都要修改子类构造函数的指向,否则子类实例的构造函数会指向SuperType。
SubType.prototype.constructor = SubType;

瑜:父类方法可复用

缺点:

2.2 构造函数继承

骨干:将父类构造函数的始末复制给了子类的构造函数。这是负有继续中唯一一个休关乎到prototype的连续。

SuperType.call(SubType);

1
SuperType.call(SubType);

瑜:和原型链继承完全翻转。

症结:父类的方无克复用,子类实例的法每次都是单身创建的。

2.3 组合继承

主干:原型式继承与构造函数继承的三结合,兼具了双方的优点。

function SuperType() { this.name = ‘parent’; this.arr = [1, 2, 3]; }
SuperType.prototype.say = function() { console.log(‘this is parent’) }
function SubType() { SuperType.call(this) // 第二蹩脚调动用SuperType }
SubType.prototype = new SuperType() // 第一涂鸦调整用SuperType

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function SuperType() {
    this.name = ‘parent’;
    this.arr = [1, 2, 3];
}
 
SuperType.prototype.say = function() {
    console.log(‘this is parent’)
}
 
function SubType() {
    SuperType.call(this) // 第二次调用SuperType
}
 
SubType.prototype = new SuperType() // 第一次调用SuperType

优点:

缺点:

调用了少软父类的构造函数,第一浅让子类的原型添加了父类的name,
arr属性,第二糟而给子类的构造函数添加了父类的name,
arr属性,从而覆盖了子类原型中之同名参数。这种为遮盖的情导致了性上之荒废。

2.4 原型式继承

核心:原型式继承的object方法本质上是针对性参数对象的一个浅复制。

优点:父类方法可复用

缺点:

function object(o){ function F(){} F.prototype = o; return new F(); }
var person = { name: “Nicholas”, friends: [“Shelby”, “Court”, “Van”]
}; var anotherPerson = object(person); anotherPerson.name = “Greg”;
anotherPerson.friends.push(“Rob”); var yetAnotherPerson =
object(person); yetAnotherPerson.name = “Linda”;
yetAnotherPerson.friends.push(“Barbie”); alert(person.friends);
//”Shelby,Court,Van,Rob,Barbie”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function object(o){
  function F(){}
  F.prototype = o;
  return new F();
}
 
var person = {
    name: "Nicholas",
    friends: ["Shelby", "Court", "Van"]
};
 
var anotherPerson = object(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");
 
var yetAnotherPerson = object(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");
alert(person.friends);   //"Shelby,Court,Van,Rob,Barbie"
 

ECMAScript 5 通过新增
Object.create()方法规范化了原型式继承。这个主意接收两单参数:一
个用作新对象原型的对象和(可选的)一个乎新对象定义额外属性的靶子。在传出一个参数的景下,
Object.create()与 object()方法的一言一行无异于。——《JAVASCript高级编程》

所以上文中代码可以生成吗

var yetAnotherPerson = object(person); => var yetAnotherPerson =
Object.create(person);

1
var yetAnotherPerson = object(person); => var yetAnotherPerson = Object.create(person);

2.5 寄生式继承

核心:使用原型式继承取得一个对象对象的浅复制,然后增强这个浅复制的力量。

利弊:仅提供相同栽思路,没什么可取

function createAnother(original){ var clone=object(original);
//通过调用函数创建一个新对象 clone.sayHi = function(){
//以某种方式来加强这个目标 alert(“hi”); }; return clone; //返回这个目标
} var person = { name: “Nicholas”, friends: [“Shelby”, “Court”, “Van”]
}; var anotherPerson = createAnother(person); anotherPerson.sayHi();
//”hi”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function createAnother(original){
    var clone=object(original);    //通过调用函数创建一个新对象
    clone.sayHi = function(){      //以某种方式来增强这个对象
        alert("hi");
    };
    return clone;                  //返回这个对象
}
 
var person = {
    name: "Nicholas",
    friends: ["Shelby", "Court", "Van"]
};
 
var anotherPerson = createAnother(person);
anotherPerson.sayHi(); //"hi"

2.6 寄生组合继承

刚说到组合继承来一个会简单潮调整用父类的构造函数造成浪费的短,寄生组合继承就可解决是题目。

function inheritPrototype(subType, superType){ var prototype =
object(superType.prototype); // 创建了父类原型的浅复制
prototype.constructor = subType; // 修正原型的构造函数 subType.prototype
= prototype; // 将子类的原型替换为之原型 } function SuperType(name){
this.name = name; this.colors = [“red”, “blue”, “green”]; }
SuperType.prototype.sayName = function(){ alert(this.name); }; function
SubType(name, age){ SuperType.call(this, name); this.age = age; } //
核心:因为凡针对性父类原型的复制,所以未含有父类的构造函数,也便不见面调用两赖父类的构造函数造成浪费
inheritPrototype(SubType, SuperType); SubType.prototype.sayAge =
function(){ alert(this.age); }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function inheritPrototype(subType, superType){
    var prototype = object(superType.prototype); // 创建了父类原型的浅复制
    prototype.constructor = subType;             // 修正原型的构造函数
    subType.prototype = prototype;               // 将子类的原型替换为这个原型
}
 
function SuperType(name){
    this.name = name;
    this.colors = ["red", "blue", "green"];
}
 
SuperType.prototype.sayName = function(){
    alert(this.name);
};
 
function SubType(name, age){
    SuperType.call(this, name);
    this.age = age;
}
// 核心:因为是对父类原型的复制,所以不包含父类的构造函数,也就不会调用两次父类的构造函数造成浪费
inheritPrototype(SubType, SuperType);
SubType.prototype.sayAge = function(){
    alert(this.age);
}

利弊:这是一模一样种到的接轨方式。

2.7 ES6 Class extends

中心:
ES6继承的结果及寄生组合继承相似,本质上,ES6蝉联是同样种语法糖。但是,寄生组合继承是优先创造子类实例this对象,然后再针对那提高;而ES6事先用父类实例对象的特性和道,加至this上面(所以要优先调用super方法),然后再次用子类的构造函数修改this。

class A {} class B extends A { constructor() { super(); } }

1
2
3
4
5
6
7
class A {}
 
class B extends A {
  constructor() {
    super();
  }
}

ES6兑现连续的实际原理:

class A { } class B { } Object.setPrototypeOf = function (obj, proto) {
obj.__proto__ = proto; return obj; } // B 的实例继承 A 的实例
Object.setPrototypeOf(B.prototype, A.prototype); // B 继承 A 的静态属性
Object.setPrototypeOf(B, A);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class A {
}
 
class B {
}
 
Object.setPrototypeOf = function (obj, proto) {
  obj.__proto__ = proto;
  return obj;
}
 
// B 的实例继承 A 的实例
Object.setPrototypeOf(B.prototype, A.prototype);
 
// B 继承 A 的静态属性
Object.setPrototypeOf(B, A);
 

ES6继往开来和ES5持续的异议:

相同点:本质上ES6后续是ES5继续的语法糖

不同点:


参考文章:

[1]《js继承、构造函数继承、原型链继承、组合继承、组合继承优化、寄生组合继承》

[2]《JavaScript高级编程》

1 赞 收藏
评论

图片 2

相关文章

标签:

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图