复制代码 代码如下,JavaScript未有类承袭的定义

从本质认知JavaScript的原型承继和类承袭

2016/04/06 · JavaScript · 1 评论 · 继承

原作出处: 十年踪迹(@十年踪迹)   

JavaScript发展到前天,和其余语言不均等的二个特色是,有形形色色的“承继情势”,可能稍微正确一点的说教,叫做有丰硕多采的基于prototype的模拟类承继达成格局。

在ES6早先,JavaScript未有类承继的概念,由此使用者为了代码复用的指标,只可以参谋别的语言的“承接”,然后用prototype来模拟出对应的得以完毕,于是有了各类承袭格局,比方《JavaScript高等程序设计》上说的 原型链,借用构造函数,组合承接,原型式承接,寄生式承继,寄生组合式承继 等等

那么多三番伍回格局,让第贰回接触这一块的友人们心中某个崩溃。可是,之所以有那么多三翻五次情势,其实还是因为“模拟”二字,因为大家在说继续的时候不是在斟酌prototype本人,而是在用prototype和JS个性来效仿别的语言的类承袭。

大家今后扬弃那些项目不可胜道的承袭方式,来看一下prototype的面目和我们怎么要模拟类继承。

复制代码 代码如下:

原型承袭

“原型” 那么些词本身源自心绪学,指好玩的事、教派、梦境、幻想、工学中不停重复现身的意境,它源自由民主族纪念和原有经验的集体无意识。

因而,原型是一种浮泛,代表事物表象之下的牵连,用简短的话来讲,正是原型描述事物与事物之间的相似性.

虚构两个孩子如何认识这么些世界:

当小孩子没见过华南虎的时候,大人大概会教他,孟加拉虎呀,就如二头大猫。假使那几个孩子刚刚平常和邻居家的小猫玩耍,那么他不用去动物园见到真实的苏门答腊虎,就能够想象出马来虎大致是长什么样体统。

图片 1

其一逸事有个更简短的公布,叫做“画虎类犬”。假若我们用JavaScript的原型来陈说它,就是:

JavaScript

function Tiger(){ //... } Tiger.prototype = new Cat(); //山兽之君的原型是一头猫

1
2
3
4
5
function Tiger(){
    //...
}
 
Tiger.prototype = new Cat(); //老虎的原型是一只猫

很显眼,“东施东施效颦”(只怕反过来“照虎画猫”,也能够,取决孩子于先认知文虎依然先认知猫)是一种认识情势,它令人类小孩子无需在脑际里再次完全构建一只黑蓝虎的全套新闻,而得以由此他熟谙的猫咪的“复用”获得沙虫妈的非常多音信,接下去他只供给去到动物园,去观望马来虎和小猫的不如部分,就足以准确认识什么是文虎了。这段话用JavaScript可以描述如下:

JavaScript

function Cat(){ } //喵星人喵喵叫 Cat.prototype.say = function(){ return "喵"; } //猫咪会爬树 Cat.prototype.climb = function(){ return "作者会爬树"; } function Tiger(){ } 泰格.prototype = new Cat(); //老虎的喊叫声和猫咪不相同,但苏门答腊虎也会爬树 Tiger.prototype.say = function(){ return "嗷"; }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function Cat(){
 
}
//小猫喵喵叫
Cat.prototype.say = function(){    
  return "喵";
}
//小猫会爬树
Cat.prototype.climb = function(){
  return "我会爬树";
}
 
function Tiger(){
 
}
Tiger.prototype = new Cat();
 
//老虎的叫声和小猫不同,但老虎也会爬树
Tiger.prototype.say = function(){
  return "嗷";
}

进而,原型能够由此陈诉八个东西之间的相似关系来复用代码,我们得以把这种复用代码的情势称为原型承袭。

<script> Function.prototype.createInstance = function(){
var T = function(){};
T.prototype = this.prototype;
T.constructor = this;
var o = new T();
this.apply(o, arguments);
return o;
}</script>

类继承

几年之后,那时候的小孩长大了,随着他的知识结构不断丰盛,她认知世界的主意也爆发了一些变通,她学会了太多的动物,有喵喵叫的猫,百兽之王狮虎兽,文雅的林子之王爪哇虎,还会有豺狼、大象之类。

此时,单纯的相似性的回味格局已经少之甚少被运用在此么丰盛的文化内容里,越来越小心的认识情势——分类,开端被更频仍利用。

图片 2

那会儿当年的少儿会说,猫和狗都以动物,假诺她刚刚学习的是明媒正娶的生物学,她恐怕还可能会说猫和狗都以脊索门哺乳纲,于是,相似性被“类”这一种更加高品位的抽象表达取代,大家用JavaScript来说述:

JavaScript

class Animal{ eat(){} say(){} climb(){} ... } class Cat extends Animal{ say(){return "喵"} } class Dog extends Animal{ say(){return "汪"} }

1
2
3
4
5
6
7
8
9
10
11
12
class Animal{
    eat(){}
    say(){}
    climb(){}
    ...
}
class Cat extends Animal{
    say(){return "喵"}
}
class Dog extends Animal{
    say(){return "汪"}
}

说下方面代码里面 T.constructor = this那句话,作者认为到那句话未有怎么实际效率,
本身T.constructor应该是为Funtion,为何要给它设定为Funtion的实例呢,

原型承袭和类承接

于是,原型承袭和类承袭是三种认识情势,本质上皆感觉着架空(复用代码)。相对于类,原型更初级且更加灵活。由此当贰个系统内并未有太多关系的东西的时候,用原型显然比用类越来越灵活方便。

原型承继的便捷性表现在系统中指标少之甚少的时候,原型承继无需组织额外的抽象类和接口就足以完结复用。(如系统里独有猫和狗三种动物来讲,没必要再为它们协会二个虚无的“动物类”)

原型承接的布帆无恙还表以往复用形式越来越灵敏。由于原型和类的情势分裂样,所以对复用的判定标准也就不雷同,举例把贰个梅红皮球充作贰个阳光的原型,当然是足以的(反过来也行),但分明不可能将“白矮星类”当作太阳和红球的国有父类(倒是能够用“球体”那个类作为它们的公共父类)。

既是原型本质上是一种认识形式能够用来复用代码,那大家为何还要模仿“类承袭”呢?在这里中间大家就得看看原型继承有何难点——

复制代码 代码如下:

原型承接的标题

出于我们刚刚后边举例的猫和东北虎的构造器未有参数,因而大家很恐怕没觉察标题,现在大家试验一个有参数构造器的原型承袭:

JavaScript

function Vector2D(x, y){ this.x = x; this.y = y; } Vector2D.prototype.length = function(){ return Math.sqrt(this.x * this.x + this.y * this.y); } function Vector3D(x, y, z){ Vector2D.call(this, x, y); this.z = z; } Vector3D.prototype = new Vector2D(); Vector3D.prototype.length = function(){ return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z); } var p = new Vector3D(1, 2, 3); console.log(p.x, p.y, p.z, p.length(), p instanceof Vector2D);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function Vector2D(x, y){
  this.x = x;
  this.y = y;
}
Vector2D.prototype.length = function(){
  return Math.sqrt(this.x * this.x + this.y * this.y);
}
 
function Vector3D(x, y, z){
  Vector2D.call(this, x, y);
  this.z = z;
}
Vector3D.prototype = new Vector2D();
 
Vector3D.prototype.length = function(){
  return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
}
 
var p = new Vector3D(1, 2, 3);
console.log(p.x, p.y, p.z, p.length(), p instanceof Vector2D);

地点这段代码里面大家看看大家用 Vector2D 的实例作为 Vector3D 的原型,在 Vector3D 的构造器里面咱们还足以调用 Vector2D 的结构器来开头化 x、y。

然而,假如认真探究方面包车型地铁代码,会发觉贰个小标题,在中等描述原型承袭的时候:

JavaScript

Vector3D.prototype = new Vector2D();

1
Vector3D.prototype = new Vector2D();

咱俩其实无参数地调用了三遍 Vector2D 的构造器!

那二遍调用是不须求的,何况,因为咱们的 Vector2D 的构造器丰硕轻便而且未有副功能,所以我们本次无谓的调用除了稍稍消耗了质量之外,并不会带动太严重的主题素材。

但在骨子里项目中,大家有些组件的构造器相比复杂,或然操作DOM,那么这种气象下无谓多调用二回构造器,显明是有非常大可能引致惨痛难点的。

于是乎,我们得想艺术克服这一遍剩余的构造器调用,而分明,大家发掘我们得以不须要那三回剩余的调用:

JavaScript

function createObjWithoutConstructor(Class){ function T(){}; T.prototype = Class.prototype; return new T(); }

1
2
3
4
5
function createObjWithoutConstructor(Class){
    function T(){};
    T.prototype = Class.prototype;
    return new T();    
}

地点的代码中,我们因而创建三个空的结构器T,援引父类Class的prototype,然后再次回到new T( ),来都行地避开Class构造器的奉行。那样,我们真正能够绕开父类构造器的调用,并将它的调用机会延迟到子类实例化的时候(本来也应该这么才创造)。

JavaScript

function Vector2D(x, y){ this.x = x; this.y = y; } Vector2D.prototype.length = function(){ return Math.sqrt(this.x * this.x + this.y * this.y); } function Vector3D(x, y, z){ Vector2D.call(this, x, y); this.z = z; } Vector3D.prototype = createObjWithoutConstructor(Vector2D); Vector3D.prototype.length = function(){ return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z); } var p = new Vector3D(1, 2, 3); console.log(p.x, p.y, p.z, p.length(), p instanceof Vector2D);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function Vector2D(x, y){
  this.x = x;
  this.y = y;
}
Vector2D.prototype.length = function(){
  return Math.sqrt(this.x * this.x + this.y * this.y);
}
 
function Vector3D(x, y, z){
  Vector2D.call(this, x, y);
  this.z = z;
}
Vector3D.prototype = createObjWithoutConstructor(Vector2D);
 
Vector3D.prototype.length = function(){
  return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
}
 
var p = new Vector3D(1, 2, 3);
console.log(p.x, p.y, p.z, p.length(), p instanceof Vector2D);

那般,大家消除了父类构造器延迟构造的主题材料之后,原型承袭就相比适用了,并且那样轻便处理今后,使用起来还不会潜移默化instanceof 重临值的没有错,那是与别的模拟格局对待最大的好处。

<script>
Function.prototype.$extends = function(p){
this.$super = p;
var fn = function(){};
fn.prototype = p.prototype;
this.prototype = new fn();
//那句是本身要好加的,保障组织出子类实例的constructor照旧指向子类的结构器函数
this.prototype.constructor=this;
//-----------------------------
return this;
};
function Animal(){
}
function Cat(){
}
Cat.$extends(Animal);
var bb=new Cat();
alert(bb.constructor);
//不过(this.prototype.constructor=this)这种做法通过bb那几个目的不可能回朔到Animal的原型
//下边语句还是再次来到Cat这几个函数,并不是Animal
alert(bb.constructor.prototype.constructor)
</script>

模拟类承袭

最后,大家运用这个规律仍然是能够兑现比较健全的类承袭:

JavaScript

(function(global){"use strict" Function.prototype.extend = function(props){ var Super = this; //父类构造函数 //父类原型 var TmpCls = function(){ } TmpCls.prototype = Super.prototype; var superProto = new TmpCls(); //父类构造器wrapper var _super = function(){ return Super.apply(this, arguments); } var Cls = function(){ if(props.constructor){ //实施构造函数 props.constructor.apply(this, arguments); } //绑定 this._super 的方法 for(var i in Super.prototype){ _super[i] = Super.prototype[i].bind(this); } } Cls.prototype = superProto; Cls.prototype._super = _super; //复制属性 for(var i in props){ if(i !== "constructor"){ Cls.prototype[i] = props[i]; } } return Cls; } function Animal(name){ this.name = name; } Animal.prototype.sayName = function(){ console.log("My name is "+this.name); } var Programmer = Animal.extend({ constructor: function(name){ this._super(name); }, sayName: function(){ this._super.sayName(name); }, program: function(){ console.log("I"m coding..."); } }); //测量检验大家的类 var animal = new Animal("dummy"), akira = new Programmer("akira"); animal.sayName();//输出 ‘My name is dummy’ akira.sayName();//输出 ‘My name is akira’ akira.program();//输出 ‘I"m coding...’ })(this);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
(function(global){"use strict"
 
  Function.prototype.extend = function(props){
    var Super = this; //父类构造函数
 
    //父类原型
    var TmpCls = function(){
 
    }
    TmpCls.prototype = Super.prototype;
 
    var superProto = new TmpCls();
 
    //父类构造器wrapper
    var _super = function(){
      return Super.apply(this, arguments);
    }
 
    var Cls = function(){
      if(props.constructor){
        //执行构造函数
        props.constructor.apply(this, arguments);
      }
      //绑定 this._super 的方法
      for(var i in Super.prototype){
        _super[i] = Super.prototype[i].bind(this);
      }
    }
    Cls.prototype = superProto;
    Cls.prototype._super = _super;
 
    //复制属性
    for(var i in props){
      if(i !== "constructor"){
        Cls.prototype[i] = props[i];
      }
    }  
 
    return Cls;
  }
 
  function Animal(name){
    this.name = name;
  }
 
  Animal.prototype.sayName = function(){
    console.log("My name is "+this.name);
  }
 
  var Programmer = Animal.extend({
    constructor: function(name){
      this._super(name);
    },
    sayName: function(){
      this._super.sayName(name);
    },
    program: function(){
      console.log("I"m coding...");
    }
  });
  //测试我们的类
  var animal = new Animal("dummy"),
      akira = new Programmer("akira");
  animal.sayName();//输出 ‘My name is dummy’
  akira.sayName();//输出 ‘My name is akira’
  akira.program();//输出 ‘I"m coding...’
 
})(this);

能够相比较一下ES6的类承接:

JavaScript

(function(global){"use strict" //类的定义 class Animal { //ES6痴呆行组织器 constructor(name) { this.name = name; } //实例方法 sayName() { console.log("My name is "+this.name); } } //类的持续 class Programmer extends Animal { constructor(name) { //直接调用父类构造器进行最初化 super(name); } sayName(){ super.sayName(); } program() { console.log("I"m coding..."); } } //测量检验大家的类 var animal = new Animal("dummy"), akira = new Programmer("akira"); animal.sayName();//输出 ‘My name is dummy’ akira.sayName();//输出 ‘My name is akira’ akira.program();//输出 ‘I"m coding...’ })(this);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
(function(global){"use strict"
 
  //类的定义
  class Animal {
    //ES6中新型构造器
      constructor(name) {
          this.name = name;
      }
      //实例方法
      sayName() {
          console.log("My name is "+this.name);
      }
  }
 
  //类的继承
  class Programmer extends Animal {
      constructor(name) {
        //直接调用父类构造器进行初始化
          super(name);
      }
      sayName(){
          super.sayName();
      }
      program() {
          console.log("I"m coding...");
      }
  }
  //测试我们的类
  var animal = new Animal("dummy"),
      akira = new Programmer("akira");
  animal.sayName();//输出 ‘My name is dummy’
  akira.sayName();//输出 ‘My name is akira’
  akira.program();//输出 ‘I"m coding...’
 
})(this);

还大概有地点那句代码,笔者本人加了1句,考订了子类构造器照旧指向子类函数,可是对象的原型链的回朔不能达到父类原型,消除办法是
去掉this.prototype.constructor=this;既不给原型设置constructor属性,而是给实例设置二个constructor属性,如下代码

总结

原型承袭和类继承是二种分歧的认识格局,原型承接在指标不是得步进步的简要利用模型里比类承接越来越灵活方便。可是JavaScript的原型承袭在语法上有三个协会器额向外调拨运输用的难点,大家若是通过 createObjWithoutConstructor 来延迟构造器的调用,就能够缓和这一个标题。

3 赞 8 收藏 1 评论

图片 3

复制代码 代码如下:

<script>
Function.prototype.$extends = function(p){
this.$super = p;
var fn = function(){};
fn.prototype = p.prototype;
this.prototype = new fn();
return this;
};
function Animal(){
}
function Cat(){
this.constructor= arguments.callee;
}
Cat.$extends(Animal);
var bb=new Cat();
alert(bb.constructor);
//这种做法得以经过bb那么些指标回朔到Animal的原型
alert(bb.constructor.prototype.constructor)
</script>

末尾深入分析下constructor的实在成效

复制代码 代码如下:

<script>
//定义函数
var f=function(){
}
//这里显得true,因为f的构造器是Funtion,f内部的原型属性_proto_被赋值为构造器的prototype也等于Function的prototype
//instanceof检查f内部的_proto_是否与Function.prototype有协同的结点,要是有则赶回true
alert(f instanceof Function)
//obj是f的实例
var obj=new f;
//obj内部的原型属性_proto_在new f时被赋值为f.prototype,显明f.prototype与Function.prototype未有联手的结点,因而显示false
alert(obj instanceof Function)
//为了让obj成为Function的实例也正是(obj instanceof Function)突显true
//只需要f.prototype=Function.prototype
f.prototype=Function.prototype;
//不过小编不推荐方面这种做法,因为对f.prototype的更动会破坏了Function.prototype,举个例子f.prototype.name="51js"会给Function的原型也丰裕1个name属性
//准确的做法应该是上边那样,那样诸如f.prototype.name的修改就不会破坏Function的原型了
f.prototype=new Function();
f.prototype.name="zhouyang";
/**驷马难追是此处,再次调治constructor属性为f,维护constructor这种做法是为了保障obj可以科学回朔原型链,
*倘使我们要获得obj内部的原型链,但只晓得obj,不驾驭obj是怎么实例化来的,由于obj内部的_proto_性子不可知,那么大家要收获obj内部原形只可以通过obj.constructor来获得构造器,然后再获取构造器的prototype
*1.假设大家加上面那句(f.prototype.constructor=f),回朔obj原型链
*唯其如此回朔1层原型链也便是obj.constructor.prototype(子类原型)-->obj.constructor.prototype.constructor.prototype(还是是子类原型),那样只可以回朔1层原型链
**/
f.prototype.constructor=f;
obj=new f;
alert("找到子类了---"+obj.constructor+"n"
+"找到的依旧子类,不或然找到父类---"+obj.constructor.prototype.constructor)
alert(obj instanceof Function)
/**2.只要大家用上面的点子在f定义里设置f的实例的constructor,并不是f原型的constructor
*就足以回朔2层原型链也正是obj.constructor.prototype(子类原型)-->obj.constructor.prototype.constructor.prototype(父类原型)
*门到户说这种情状是适合对象原型承袭链的情景的
*/
f=function(){
this.constructor=arguments.callee;
}
f.prototype=new Function();
f.prototype.name="zhouyang";
obj=new f;
alert("找到子类了---"+obj.constructor+"n"
+"找到父类了---"+obj.constructor.prototype.constructor)
alert(obj instanceof Function)
</script>

复制代码 代码如下:

<script>
//定义函数
var f=function(){
}
//这里体现true,因为f的构造器是Funtion,f内部的原型属性_proto_被赋值为构造器的prototype约等于Function的prototype
//instanceof检查f内部的_proto_是或不是与Function.prototype有联手的结点,倘诺有则赶回true
alert(f instanceof Function)
//obj是f的实例
var obj=new f;
//obj内部的原型属性_proto_在new f时被赋值为f.prototype,明显f.prototype与Function.prototype未有同步的结点,由此显示false
alert(obj instanceof Function)
//为了让obj成为Function的实例也正是(obj instanceof Function)展现true
//只需要f.prototype=Function.prototype
f.prototype=Function.prototype;
//不过自己不引入方面这种做法,因为对f.prototype的修改会毁掉了Function.prototype,比如f.prototype.name="51js"会给Function的原型也增加1个name属性
//精确的做法应该是下面那样,那样诸如f.prototype.name的修改就不会破坏Function的原型了
f.prototype=new Function();
f.prototype.name="zhouyang";
/**根本是此处,再度调治constructor属性为f,维护constructor这种做法是为了保证obj能够科学回朔原型链,
*假如大家要获得obj内部的原型链,但只略知一二obj,不了然obj是怎么实例化来的,由于obj内部的_proto_属性不可以知道,那么大家要拿走obj内部原形只好通过obj.constructor来收获构造器,然后再获取构造器的prototype
*1.假若大家加上边那句(f.prototype.constructor=f),回朔obj原型链
*只可以回朔1层原型链也正是obj.constructor.prototype(子类原型)-->obj.constructor.prototype.constructor.prototype(依旧是子类原型),那样只好回朔1层原型链
**/
f.prototype.constructor=f;
obj=new f;
alert("找到子类了---"+obj.constructor+"n"
+"找到的要么子类,不可能找到父类---"+obj.constructor.prototype.constructor)
alert(obj instanceof Function)
/**2.若是大家用上边包车型客车艺术在f定义里设置f的实例的constructor,并非f原型的constructor
*就足以回朔2层原型链也正是obj.constructor.prototype(子类原型)-->obj.constructor.prototype.constructor.prototype(父类原型)
*刚烈这种景况是切合对象原型承袭链的事态的
*/
f=function(){
this.constructor=arguments.callee;
}
f.prototype=new Function();
f.prototype.name="zhouyang";
obj=new f;
alert("找到子类了---"+obj.constructor+"n"
+"找到父类了---"+obj.constructor.prototype.constructor)
alert(obj instanceof Function)
</script>结论constructor的成效正是保证对象的原型链

向果果和winter赐教一下,不知驾驭的是否正确哈,另外笔者看大家常说的原型的传染到底指的是何许??
职能的话上面这么些或者能够作证

复制代码 代码如下:

<script>
var f = function(x){}
f.prototype={};
alert((new f).constructor);
f.prototype.constructor=f;
alert((new f).constructor);
</script>

您可能感兴趣的小说:

  • Javascript的构造函数和constructor属性
  • 理解Javascript_11_constructor完毕原理
  • JavaScript constructor和instanceof,JSOO中的一对快乐仇人
  • 深远深入分析js中的constructor和prototype
  • JavaScript类和后续 constructor属性
  • JavaScript中多少个首要的习性(this、constructor、prototype)介绍
  • JavaScript中的prototype和constructor简明总括
  • javascript new后的constructor属性
  • 不用构造函数(Constructor)new关键字也能促成JavaScript的面向对象
  • javascript设计格局Constructor(构造器)方式

本文由金莎娱乐场官方网站发布于金莎娱乐官方网站,转载请注明出处:复制代码 代码如下,JavaScript未有类承袭的定义

TAG标签:
Ctrl+D 将本页面保存为书签,全面了解最新资讯,方便快捷。