菜单

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

2019年10月6日 - Ajax

一篇作品精晓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的继续同样,观念也比较轻便:在子类型构造函数的内部调用超类型构造函数。

原型式承袭是依附已有的对象创制新的靶子,将子类的原型指向父类,就约等于参预了父类那条原型链

而你的 上面这段代码不是严厉意义上的类式承袭,依据尼古RussC.扎卡s的说教,这么些相应叫做组合式承接。它调用了一次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: “Nicolas”, 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三回九转的语法糖

不同点:

3. 总结

参考小说:

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

[2]《JavaScript高端编制程序》

1 赞 收藏
评论

图片 2

相关文章

发表评论

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

网站地图xml地图