JavaScript继承基础讲解(原型链、借用构造函数、混

来源:http://www.sh-fengwen.com 作者: 营养排行 人气:74 发布时间:2019-11-01
摘要:后生可畏篇文章驾驭JS承继——原型链/构造函数/组合/原型式/寄生式/寄生组合/Class extends 2018/08/02 · 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的继续相像,观念也比较轻巧:在子类型构造函数的里边调用超类型构造函数。

原型式承袭是依靠原来就有的对象创制新的靶子,将子类的原型指向父类,就约等于加入了父类那条原型链

而你的 下边这段代码不是严俊意义上的类式承袭,遵照NicolasC.扎卡s的传教,这一个应该叫做组合式承袭。它调用了两回parent2()。第一回是 child2.prototype=new parent2('param'); child2就能够收获多个本性param,getParam(),他们都是parent2的质量,可是她们在child2的原型中。第三遍是parent2.call(this,cparam); 本次又在新指标上成立了实例属性param,getParam()。于是,这两性情格就屏蔽了原型中的八个同名属性。那有哪些受益呢,就是你在营造三个child3时也持续parent2()的性质,还足以定义自个儿的习性。与此同不经常候他长的就和她兄弟分歧了,但又有平等的“血统(使用父类的艺术)”。

纯手打,接待继续研究  

说好的讲授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属性,进而覆盖了子类原型中的同名参数。这种被覆盖的景况导致了品质上的荒凉。

本文由美高梅游戏平台网站发布于 营养排行,转载请注明出处:JavaScript继承基础讲解(原型链、借用构造函数、混

关键词:

最火资讯