JS怎么着创制对象和继承对象,从精神认识JavaScript的原型继承和类继承

by admin on 2019年2月26日

从精神认识JavaScript的原型继承和类继承

2016/04/06 · JavaScript
· 1 评论JS怎么着创制对象和继承对象,从精神认识JavaScript的原型继承和类继承。 ·
继承

初稿出处:
十年踪迹(@十年踪迹)   

JavaScript发展到前几日,和别的语言差异的三个表征是,有各样各种的“继承方式”,只怕稍微准确一点的传教,叫做有各式各个的遵照prototype的模拟类继承达成格局。

在ES6以前,JavaScript没有类继承的概念,因而使用者为了代码复用的目的,只好参考其余语言的“继承”,然后用prototype来模拟出对应的贯彻,于是有了各个继承格局,比如《JavaScript高级程序设计》上说的 原型链,借用构造函数,组合继承,原型式继承,寄生式继承,寄生组合式继承 等等

那正是说多三番五次格局,让第3次接触这一块的伴儿们心中有个别崩溃。然则,之所以有那么多一连格局,其实如故因为“模拟”二字,因为大家在说继续的时候不是在研商prototype自身,而是在用prototype和JS性子来效仿其他语言的类继承。

我们未来吐弃这个连串司空见惯的接续格局,来看一下prototype的真相和我们怎么要模拟类继承。

JS作为面向对象的弱类型语言,继承也是其充足强大的特点之一。那么怎么着在JS中落到实处持续呢?让我们拭目以俟。

JS创设对象的二种办法:工厂情势,构造函数情势,原型形式,混合格局,动态原型格局

JS继承的兑现格局

原型继承

“原型”
那个词本身源自心思学,指遗闻、教派、梦境、幻想、军事学中不停重复出现的意境,它源自由民主族纪念和原来经验的公物无意识。

故而,原型是一种浮泛,代表事物表象之下的牵连,用不难的话来说,就是原型描述事物与事物之间的形似性.

设想一个小朋友怎么样认知这一个世界:

当小孩子没见过老虎的时候,大人恐怕会教他,老虎呀,就像三头大猫。如果那些孩子刚刚日常和街坊家的猫咪玩耍,那么她不用去动物园见到真实的大虫,就能设想出老虎大致是长什么样子。

必发88 1

以此典故有个更简明的发挥,叫做“里丑捧心”。假如大家用JavaScript的原型来叙述它,就是:

JavaScript

function 泰格(){ //… } Tiger.prototype = new Cat();
//老虎的原型是3只猫

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

很显明,“东施东施效颦”(只怕反过来“照虎画猫”,也得以,取决孩子于先认识老虎照旧先认识猫)是一种认知形式,它令人类小孩子不要求在脑际里再一次完全创设3只猛虎的全部信息,而能够透过她熟知的猫咪的“复用”获得老虎的绝大部分音讯,接下去她只供给去到动物园,去考察老虎和猫咪的不一致部分,就能够正确认知什么是老虎了。那段话用JavaScript能够描述如下:

JavaScript

function Cat(){ } //小猫喵喵叫 Cat.prototype.say = function(){ return
“喵”; } //小猫会爬树 Cat.prototype.climb = function(){ return
“笔者会爬树”; } function Tiger(){ } 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 "嗷";
}

因此,原型可以经过讲述七个东西之间的貌似关系来复用代码,大家能够把那种复用代码的格局称为原型继承。

JS继承的兑现格局

 

既然如此要贯彻一而再,那么首先我们得有贰个父类,代码如下:

类继承

几年过后,当时的女孩儿长大了,随着她的学识结构不断丰裕,她认识世界的法门也时有产生了部分变迁,她学会了太多的动物,有喵喵叫的猫,百兽之王狮子,优雅的丛林之王老虎,还有豺狼、大象之类。

此时,单纯的相似性的回味情势已经很少被运用在这么丰硕的学问内容里,越发严峻的体会形式——分类,开始被更频仍利用。

必发88 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 "汪"}
}

既然如此要达成持续,那么首先我们得有1个父类,代码如下:

1
在工厂方式中,在构造函数内部制造多个新对象,最后回到那些目的。当实例化时,大家不供给用new关键字,就像调用方法同样就足以实例化。

// 定义一个动物类

原型继承和类继承

就此,原型继承和类继承是二种认知方式,本质上都是为了架空(复用代码)。相对于类,原型更初级且更灵活。因而当一个连串内没有太多涉及的东西的时候,用原型显明比用类更灵活轻便。

原型继承的便捷性表今后系统中指标较少的时候,原型继承不要求结构额外的抽象类和接口就足以兑现复用。(如系统里只有猫和狗二种动物来说,没须要再为它们组织几个抽象的“动物类”)

原型继承的油滑还呈以往复用格局尤其灵活。由于原型和类的形式不雷同,所以对复用的度量圭臬也就差异,例如把三个革命皮球当做一个太阳的原型,当然是能够的(反过来也行),但强烈不能将“恒星类”当做太阳和红球的公共父类(倒是能够用“球体”这一个类作为它们的集体父类)。

既是原型本质上是一种认知格局能够用来复用代码,那大家怎么还要模仿“类继承”呢?在那当中我们就得看看原型继承有啥样难点——

// 定义二个动物类

工厂格局的通病是简单和一般函数混淆,只好通过命名来确认它是一个构造函数。不推荐应用那种方式。

function Animal (name) {

原型继承的题材

是因为大家刚刚前边举例的猫和老虎的构造器没有参数,因而大家很恐怕没察觉标题,今后我们试验一个有参数构造器的原型继承:

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();

大家实在无参数地调用了3回 Vector2D 的构造器!

那3遍调用是不须求的,而且,因为大家的 Vector2D
的构造器丰硕不难并且没有副成效,所以咱们这一次无谓的调用除了稍稍消耗了质量之外,并不会带来太严重的难题。

但在其实项目中,大家有个别组件的构造器比较复杂,大概操作DOM,那么那种景色下无谓多调用2回构造器,显明是有或许导致严重难点的。

于是乎,大家得想办法制伏那三次剩余的构造器调用,而明显,大家发现大家得以不要求那2遍剩余的调用:

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 再次来到值的正确,那是与其他模拟格局对待最大的好处。

function Animal (name) {

//factory pattern

// 属性

模拟类继承

末段,我们使用这几个规律还足以兑现比较完美的类继承:

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);

// 属性

function createPerson(name, age, job){
    var o = {};
    o.name = name;
    o.age = age;
    o.job = job;
    o.friends = [“Mike”, “Sun”];
    o.sayName = function(){
        alert(“factory pattern: ” + this.name);
    }
    return o;
}

this.name = name || ‘Animal’;

总结

原型继承和类继承是三种区其余体会情势,原型继承在对象不是不少的简约利用模型里比类继承特别灵活方便。但是JavaScript的原型继承在语法上有叁个结构器额向外调拨运输用的题材,大家只要经过
createObjWithoutConstructor 来延迟构造器的调用,就能消除那几个难点。

3 赞 8 收藏 1
评论

必发88 3

this.name = name || ‘Animal’;

var Abby = createPerson(“Abby”, “22”, “Softwarre Engineer”);
Abby.sayName();

// 实例方法

// 实例方法

 

this.sleep = function(){

this.sleep = function(){

2
构造函数情势,用new关键字来实例化对象。与工厂格局比较,使用构造函数形式成立对象,无需在函数内部重新创立对象,而利用this指代,并且函数无需显著return。不推荐使用那种情势。

console.log(this.name + ‘正在睡觉!’);

console.log(this.name + ‘正在睡觉!’);

构造函数的缺点是时时刻刻的正片,每new三遍就造出贰个副本,各个方法都要在每一种实例上再次成立3回,显著那样是相当的,大家想要的是一种多少措施共享全数,有此方法私有,于是埃里克发明了原型链。

}

}

//constructor pattern
function Person(name, age, job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function(){
        alert(“constructor pattern: ” + this.name);
    }
}

}

}

var Abby = new Person(“Abby”, “22”, “Software Engineer”);
Abby.sayName();

// 原型方法

// 原型方法

 

Animal.prototype.eat = function(food) {

Animal.prototype.eat = function(food) {

 

console.log(this.name + ‘正在吃:’ + food);

console.log(this.name + ‘正在吃:’ + food);

3 原型情势,那里就要说到prototype。大家成立的每一个函数都有有3个prototype(原型)属性,它也是叁个目的,它的用途是富含有一定类型的享有实例的品质和艺术。不引进应用这种方式。

};

};

上面例子,我们把拥有办法2个个充裕到prototype上,但由于prototype上艺术属于一种共享,那么些点子有个别旁人用的到,有个别别人根本用不到,有个别旁人想用的远非的主意还要再一次往prototype上足够。那样就倒霉了,所以最常用的情势其实是混合型的。

① 、原型链继承

壹 、原型链继承

//prototype pattern
function Abby(){}

核心:将父类的实例作为子类的原型

核心:将父类的实例作为子类的原型

Abby.prototype.name = “Abby”;
Abby.prototype.age = “22”;
Abby.prototype.sayName = function(){
    alert(“prototype pattern: ” + this.name);
}

function Cat(){

function Cat(){

var person1 = new Abby();
person1.sayName();

}

}

 

Cat.prototype = new Animal();

Cat.prototype = new Animal();

 

Cat.prototype.name = ‘cat’;

Cat.prototype.name = ‘cat’;

4
构造函数方式和原型方式的犬牙交错类型。将装有属性不是措施的个性定义在函数中(构造函数情势),将拥有属性值为艺术的品质利用prototype在函数之外定义(原型方式)。
推荐应用那种方法创造对象。

// Test Code

// Test Code

JS怎么着创制对象和继承对象,从精神认识JavaScript的原型继承和类继承。//hybrid constructor & prototype pattern
function Student(name, sno){
  this.name = name;
  this.sno = sno;
  this.sayName = function(){
    alert(“hybrid constructor & prototype pattern: ” + this.name);
  }
}

var cat = new Cat();

var cat = new Cat();

Student.prototype = {
  constructor : Student,
  teacher : [“mike”, “abby”],
  sayTeacher : function(){
    alert(“hybrid constructor & prototype pattern(teacher): ” +
this.teacher);
  }
}

console.log(cat.name);

console.log(cat.name);

var zhangsan = new Student(“zhangsan”, “22”);
var lisi = new Student(“lisi”, “23”);
zhangsan.sayName();
lisi.sayName();
zhangsan.sayTeacher();
lisi.sayTeacher();

console.log(cat.eat(‘fish’));

console.log(cat.eat(‘fish’));

 

console.log(cat.sleep());

console.log(cat.sleep());

 

console.log(cat instanceof Animal); //true

console.log(cat instanceof Animal); //true

5 动态原型格局

console.log(cat instanceof Cat); //true

console.log(cat instanceof Cat); //true

动态原型方式得以清楚为混合形式的多少个特例。该形式中,属性为艺术
的质量直接在函数中进行了概念,不过因为if从句从而保险开创该对象的实例时,属性的点子不会被重复创建。推荐使用那种方式。

特点:

特点:

//dynamic prototype pattern
function Person(){
  this.name = “Mike”;
  this.age = 22;
}
if (typeof Person._lev == “undefined”){
   Person.prototype.lev = function(){
     return this.name;
   }
   Person._lev = true;
}

尤其纯粹的三番五次关系,实例是子类的实例,也是父类的实例

充足纯粹的持续关系,实例是子类的实例,也是父类的实例

var x = new Person();
alert(x.lev());

父类新增原型方法/原型属性,子类都能访问到

父类新增原型方法/原型属性,子类都能访问到

 

大约,易于落到实处

简单易行,易于落实

 

缺点:

缺点:

  1. 寄生构造函数方式

要想为子类新增属性和方法,须求求在new
Animal()那样的口舌之后执行,无法松开构造器中

要想为子类新增属性和格局,必要求在new
Animal()那样的话语之后执行,无法松开构造器中

//parasitic constructor pattern (hybird factory)
function Person1(name, age, job){
  var o = new Object();
  o.name = name;
  o.age = age;
  o.job = job;
  o.sayName = function(){
    alert(this.name);
  }
  return o;
}

不能够兑现多延续

心中无数落到实处多一连

var mike = new Person1(“Mike”, 22, “Software Engineer”);
mike.sayName();

源于原型对象的引用属性是持有实例共享的(详细请看附录代码:示例1;))

发源原型对象的引用属性是兼备实例共享的(详细请看附录代码:示例1;))

 

创造子类实例时,不能向父类构造函数字传送参

开创子类实例时,不恐怕向父类构造函数字传送参

 

推荐介绍指数:★★(③ 、4两大约命缺点)

推荐介绍指数:★★(三 、4两大致命缺点)

7
就绪构造函数形式,这种方式并非this,不用new,指标是平安,那是一种形式,不是主流

二 、构造继承

贰 、构造继承

//durable constructor pattern
function Person2(name, age, job){
  var o = new Object;
  o.name = name;
  o.age = age;
  o.job = job;
  o.sayName = function(){
    alert(name);
  }
  return o;
}

核心:应用父类的构造函数来拉长子类实例,等于是复制父类的实例属性给子类(没用到原型)

核心:行使父类的构造函数来拉长子类实例,等于是复制父类的实例属性给子类(没用到原型)

var mike = Person2(“Mike”, 22, “Software Engineer”);
mike.sayName();

function Cat(name){

function Cat(name){

 

Animal.call(this);

Animal.call(this);


this.name = name || ‘Tom’;

this.name = name || ‘Tom’;

 

}

}

JS中的继承首要借助原型链。

// Test Code

// Test Code

 

var cat = new Cat();

var cat = new Cat();

各样构造函数都具备三个原型对象,原型对象都含有贰个针对构造函数的指针(constructor),实例都蕴涵1个对准原型对象的个中指针(_proto_)。

console.log(cat.name);

console.log(cat.name);

倘诺对原型进行数1三回赋值,那么前面包车型地铁赋值会覆盖前边的,也正是经过原型链只好继续离实例化近年来的一个原型对象。

console.log(cat.sleep());

console.log(cat.sleep());

原型链继承的实质正是一个单链表的吃水搜索。例如,原型对象(Son.prototype)等于另贰个原型(Person)的实例(person1),那么此时的原型对象(Son.prototype)将包括三个针对另贰个原型(Person.prototype)的指针,相应的,另有二个原型(Person.prototype)中也饱含着1个针对性另二个构造函数(Person())的指针。

console.log(cat instanceof Animal); // false

console.log(cat instanceof Animal); // false

再如,另三个原型(Person.prototype)又是另二个连串(Person)的实例(person1),那么上述提到仍旧创建,如此罕见递进,就构成了实例与原型的链子。这正是所谓的原型链。

console.log(cat instanceof Cat); // true

console.log(cat instanceof Cat); // true

负有引用类型暗中同意继承了Object类型,全数函数的暗中认可原型都以Object的实例,因而默许原型都会蕴藏一个里边指针,指向Object.prototype.那也多亏自定义类型能经过行使toString()等私下认可方法的缘故。

特点:

特点:

在经过原型链完成连续时,无法动用对象字面量成立原型对象,那样会重写原型链。

化解了第11中学,子类实例共享父类引用属性的标题

消除了第11中学,子类实例共享父类引用属性的问题

 

创设子类实例时,能够向父类传递参数

创制子类实例时,能够向父类传递参数

 

能够完成多一而再(call四个父类对象)

能够兑现多一而再(call八个父类对象)

call函数的用法(可用于实现一连)

缺点:

缺点:

call([thisObj[必发88,,arg1[, arg2[,  
[,.argN]]]]]),调用3个对象的八个措施,以另一个目的替换当前目的。

实例并不是父类的实例,只是子类的实例

实例并不是父类的实例,只是子类的实例

call方法能够用来顶替另一个对象调用贰个格局。call方法可将三个函数的对象上下文从起先的上下文字改善变为由htisObj钦定的新目的。要是没有提供thisObj参数,那么Global对象被看作thisObj。

只能继续父类的实例属性和方法,不可能一而再原型属性/方法

唯其如此三番五次父类的实例属性和艺术,不能够继续原型属性/方法

function Animal(name){
  this.name = name;
  this.showName = function(){
    alert(this.name);
  }
}

没辙兑现函数复用,每种子类都有父类实例函数的副本,影响属性

不知所可落到实处函数复用,各种子类都有父类实例函数的副本,影响属性

function Cat(name){
  Animal.call(this, name);
}

推荐指数:★★(缺点3)

推荐介绍指数:★★(缺点3)

var cat = new Cat(“Black Cat”);
cat.showName();

三 、实例继承

叁 、实例继承

Animal.call(this) 的情致就是行使 Animal对象代替this对象,那么
Cat中不就有Animal的保有属性和章程了吧,Cat对象就能够直接调用Animal的法子以及品质了.

核心:为父类实例添加新特色,作为子类实例返回

核心:为父类实例添加新特性,作为子类实例重返

相同,假若应用八个call就能够实现多重继承。

function Cat(name){

function Cat(name){

 

var instance = new Animal();

var instance = new Animal();

 

instance.name = name || ‘Tom’;

instance.name = name || ‘Tom’;

new操作符成立实例的长河:创制贰个新目的->将构造函数的职能域赋给新对象(由此this就本着了那一个新指标)->执行构造函数的代码(为这一个新对象添加属性)->重回新指标

return instance;

return instance;

}

}

// Test Code

// Test Code

var cat = new Cat();

var cat = new Cat();

console.log(cat.name);

console.log(cat.name);

console.log(cat.sleep());

console.log(cat.sleep());

console.log(cat instanceof Animal); // true

console.log(cat instanceof Animal); // true

console.log(cat instanceof Cat); // false

console.log(cat instanceof Cat); // false

特点:

特点:

不限定调用格局,不管是new 子类()依然子类(),重临的目的拥有同样的效益

不限量调用方式,不管是new 子类()依然子类(),重临的靶子具备相同的功能

缺点:

缺点:

实例是父类的实例,不是子类的实例

实例是父类的实例,不是子类的实例

不辅助多一而再

不扶助多再三再四

引进指数:★★

推荐指数:★★

四 、拷贝继承

肆 、拷贝继承

function Cat(name){

function Cat(name){

var animal = new Animal();

var animal = new Animal();

for(var p in animal){

for(var p in animal){

Cat.prototype[p] = animal[p];

Cat.prototype[p] = animal[p];

}

}

Cat.prototype.name = name || ‘Tom’;

Cat.prototype.name = name || ‘Tom’;

}

}

// Test Code

// Test Code

var cat = new Cat();

var cat = new Cat();

console.log(cat.name);

console.log(cat.name);

console.log(cat.sleep());

console.log(cat.sleep());

console.log(cat instanceof Animal); // false

console.log(cat instanceof Animal); // false

console.log(cat instanceof Cat); // true

console.log(cat instanceof Cat); // true

特点:

特点:

支撑多一而再

支撑多再三再四

缺点:

缺点:

频率较低,内部存款和储蓄器占用高(因为要拷贝父类的品质)

频率较低,内部存款和储蓄器占用高(因为要拷贝父类的脾性)

无所适从取得父类数不胜数的点子(举不胜举方法,不能够选择for in 访问到)

没辙获取父类不可胜举的措施(千千万万方法,无法使用for in 访问到)

引进指数:★(缺点1)

推荐指数:★(缺点1)

⑤ 、组合继承

⑤ 、组合继承

核心:由此调用父类构造,继承父类的习性并保存传参的长处,然后经过将父类实例作为子类原型,完成函数复用

核心:通过调用父类构造,继承父类的习性并保留传参的独到之处,然后通过将父类实例作为子类原型,完毕函数复用

function Cat(name){

function Cat(name){

Animal.call(this);

Animal.call(this);

this.name = name || ‘Tom’;

this.name = name || ‘Tom’;

}

}

Cat.prototype = new Animal();

Cat.prototype = new Animal();

// Test Code

// Test Code

var cat = new Cat();

var cat = new Cat();

console.log(cat.name);

console.log(cat.name);

console.log(cat.sleep());

console.log(cat.sleep());

console.log(cat instanceof Animal); // true

console.log(cat instanceof Animal); // true

console.log(cat instanceof Cat); // true

console.log(cat instanceof Cat); // true

特点:

特点:

弥补了法子2的欠缺,能够延续实例属性/方法,也能够三番七回原型属性/方法

弥补了措施2的通病,能够接二连三实例属性/方法,也足以一连原型属性/方法

既是子类的实例,也是父类的实例

既是子类的实例,也是父类的实例

不存在引用属性共享难题

不存在引用属性共享难题

可传参

可传参

函数可复用

函数可复用

缺点:

缺点:

调用了一回父类构造函数,生成了两份实例(子类实例将子类原型上的那份屏蔽了)

调用了两回父类构造函数,生成了两份实例(子类实例将子类原型上的那份屏蔽了)

引进指数:★★★★(仅仅多消耗了一些内部存款和储蓄器)

引进指数:★★★★(仅仅多损耗了几许内部存款和储蓄器)

六 、寄生组合继承

⑥ 、寄生组合继承

核心:透过寄生方式,砍掉父类的实例属性,那样,在调用三回父类的布局的时候,就不会初阶化五遍实例方法/属性,制止的组成继承的毛病

核心:经过寄生方式,砍掉父类的实例属性,那样,在调用两次父类的布局的时候,就不会早先化四遍实例方法/属性,防止的组合继承的老毛病

function Cat(name){

function Cat(name){

Animal.call(this);

Animal.call(this);

this.name = name || ‘Tom’;

this.name = name || ‘Tom’;

}

}

(function(){

(function(){

// 创造八个从未有超过实际例方法的类

// 创制3个尚无实例方法的类

var Super = function(){};

var Super = function(){};

Super.prototype = Animal.prototype;

Super.prototype = Animal.prototype;

//将实例作为子类的原型

//将实例作为子类的原型

Cat.prototype = new Super();

Cat.prototype = new Super();

})();

})();

// Test Code

// Test Code

var cat = new Cat();

var cat = new Cat();

console.log(cat.name);

console.log(cat.name);

console.log(cat.sleep());

console.log(cat.sleep());

console.log(cat instanceof Animal); // true

console.log(cat instanceof Animal); // true

console.log(cat instanceof Cat); //true

console.log(cat instanceof Cat); //true

特点:

堪称完美

缺点:

福衢寿车相比复杂

引进指数:★★★★(实现复杂,扣掉一颗星)

附录代码:

示例一:

function Animal (name) {

// 属性

this.name = name || ‘Animal’;

// 实例方法

this.sleep = function(){

console.log(this.name + ‘正在睡觉!’);

}

//实例引用属性

this.features = [];

}

function Cat(name){

}

Cat.prototype = new Animal();

var tom = new Cat(‘Tom’);

var kissy = new Cat(‘Kissy’);

console.log(tom.name); // “Animal”

console.log(kissy.name); // “Animal”

console.log(tom.features); // []

console.log(kissy.features); // []

tom.name = ‘Tom-New Name’;

tom.features.push(‘eat’);

//针对父类实例值类型成员的变更,不影响

console.log(tom.name); // “Tom-New Name”

console.log(kissy.name); // “Animal”

//针对父类实例引用类型成员的改动,会经过影响其余子类实例

console.log(tom.features); // [‘eat’]

console.log(kissy.features); // [‘eat’]

发表评论

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

网站地图xml地图