JavaScript原型与继承,从本质认识JavaScript的原型继承和类继承

从实质认识JavaScript的原型继承和类继承

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

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

JavaScript发展到今日,和别的语言区别等的贰个风味是,有丰硕多采的“继承格局”,只怕有个别准确一点的说法,叫做有二种四种的遵照prototype的模拟类继承达成格局。

在ES六以前,JavaScript未有类继承的概念,由此使用者为了代码复用的指标,只可以参考其余语言的“继承”,然后用prototype来模拟出对应的完毕,于是有了种种继承格局,比如《JavaScript高级程序设计》上说的 原型链,借用构造函数,组合继承,原型式继承,寄生式继承,寄生组合式继承 等等

这正是说多延续格局,让第2次接触那壹块的同伴们心里有个别崩溃。但是,之所以有那么多接二连三方式,其实依旧因为“模拟”二字,因为咱们在说后续的时候不是在斟酌prototype本人,而是在用prototype和JS天性来模拟其他语言的类继承。

我们今日撇下那些项目见惯司空的接轨格局,来看一下prototype的本质和大家为何要模拟类继承。

小编:拾年踪迹
文章源自:https://www.h5jun.com/post/inherits.html

Object.prototype
JavaScript是依据原型继承的,任何对象都有贰个prototype属性。Object.prototype是全数指标的根,并且不可更改。

本身曾品尝精晓关于prototype的连带概念,最初知道起来晦涩难懂,加上当时用的地点又少。前面逐步精晓,当你须求领悟1个东西的时候,刻意的去了然是从没有过精神的效益的,不过能在您的脑际里留下一丝印象,当你真正会师包车型大巴时候,会纪念已经看到过,时机成熟的时候再去驾驭,会有不计其数取得,轮番看个一回,拿上实例分析,会发觉峰回路转。

原型继承

“原型”
这一个词自个儿源自心境学,指逸事、教派、梦境、幻想、农学中不止重复现身的意象,它源自由民主族记念和原来经验的公共无意识。

据此,原型是1种浮泛,代表事物表象之下的牵连,用简短的话来说,就是原型描述事物与事物之间的一般性.

想象2个幼儿如何认知这几个世界:

当儿童没见过老虎的时候,大人大概会教他,老虎呀,就像叁只大猫。固然那些孩子刚刚平时和近邻家的猫猫玩耍,那么他不用去动物园见到真实的大虫,就能设想出老虎大约是长什么样子。

图片 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(){ } 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 "嗷";
}

为此,原型能够经过讲述八个东西之间的貌似关系来复用代码,大家能够把那种复用代码的情势称为原型继承。

JavaScript发展到今日,和其余语言不均等的二个风味是,有各式各个的“继承方式”,恐怕某个准确一点的布道,叫做有丰裕多采的依据prototype的模拟类继承兑现方式。

复制代码 代码如下:

正文解说的相关内容:

类继承

几年过后,当时的小家伙长大了,随着她的文化结构不断丰硕,她认识世界的方式也产生了有的变化,她学会了太多的动物,有喵喵叫的猫,百兽之王狮子,优雅的丛林之王老虎,还有豺狼、大象之类。

此刻,单纯的相似性的认知格局已经很少被运用在这么充足的学问内容里,越发严峻的体味方式——分类,起初被更频繁使用。

图片 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 "汪"}
}

在ES陆从前,JavaScript未有类继承的定义,由此使用者为了代码复用,只可以参考其余语言的“继承”,然后用prototype来模拟出对应的贯彻,于是有了各样继承形式,比如《JavaScript高级程序设计》上说的
原型链,借用构造函数,组合继承,原型式继承,寄生式继承,寄生组合式继承
等等。

Object.prototype=null;
alert(Object.prototype);//[object Object]

  • 创建对象的两种情势以及开创的经过
  • 原型链prototype的理解,以及prototype
    __proto__[[Prototype]])的关系
  • 持续的两种达成

原型继承和类继承

为此,原型继承和类继承是二种认知形式,本质上都是为着架空(复用代码)。相对于类,原型更初级且越来越灵敏。因而当一个种类内未有太多涉及的事物的时候,用原型分明比用类更加灵敏轻便。

原型继承的便捷性表今后系统中目标较少的时候,原型继承不需求协会额外的抽象类和接口就能够落成复用。(如系统里只有猫和狗二种动物来说,没须要再为它们组织二个抽象的“动物类”)

原型继承的八面后珑还突显在复用形式更灵敏。由于原型和类的格局分化,所以对复用的衡量尺度也就不雷同,例如把三个橄榄棕皮球当做二个阳光的原型,当然是能够的(反过来也行),但家弦户诵不能够将“恒星类”当做太阳和红球的国有父类(倒是能够用“球体”那么些类作为它们的共用父类)。

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

那正是说多一而再方式,让第一遍接触那1块的小伙伴们心里有个别崩溃。不过,之所以有那么多一而再方式,其实依然因为“模拟”贰字。因为大家在说继承的时候,不是在商量prototype本身,而是在用prototype和JS性子来JavaScript原型与继承,从本质认识JavaScript的原型继承和类继承。模拟别的语言的类继承

Object与Object.prototype
Object继承于Object.prototype,扩充3个性情给Object.prototype上,同时也会反射到Object上。如:


原型继承的难点

是因为大家刚刚前面举例的猫和老虎的构造器未有参数,由此大家很或许没发现标题,今后大家试验2个有参数构造器的原型继承:

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

地点那段代码里面我们看来大家用 Vector贰D 的实例作为 Vector3D 的原型,在
Vector3D 的构造器里面大家还是可以调用 Vector二D 的结构器来伊始化 x、y。

只是,即便认真斟酌方面包车型客车代码,会发现二个小标题,在中游描述原型继承的时候:

JavaScript

Vector3D.prototype = new Vector2D();

1
Vector3D.prototype = new Vector2D();

咱俩实际上无参数地调用了一次 Vector贰D 的构造器!

这一回调用是不供给的,而且,因为我们的 Vector二D
的构造器丰富简单并且未有副成效,所以大家此番无谓的调用除了稍稍消耗了质量之外,并不会带来太严重的标题。

但在实际上项目中,大家有个别组件的构造器相比复杂,或然操作DOM,那么那种场地下无谓多调用2遍构造器,明显是有希望引致惨重难点的。

于是,大家得想艺术克服这3次剩余的构造器调用,而综上可得,大家发现大家能够不须求这一次剩余的调用:

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 再次来到值的不利,那是与其余模拟格局相比较最大的补益。

咱俩明日撇下这么些种类繁多的继承格局,来看一下prototype的真面目和咱们怎么要模拟类继承。

复制代码 代码如下:

一.广阔情势与原型链的敞亮

a.构造函数创制

function Test() {
    // 
}

流程

  • 创造函数的时候会暗许为Test创立贰个prototype属性,Test.prototype涵盖四个指南针指向的是Object.prototype
  • prototype暗中同意会有叁个constructor,且Test.prototype.constructor = Test
  • prototype里的其余措施都以从Object继承而来

图片 3

示例

// 调用构造函数创建实例
var instance = new Test()

此处的instance包含了1个指针指向构造函数的原型,(此处的指针在chrome里叫__proto__,也等于[[Prototype]]

图片 4

示例

b.原型情势
由上大家能够清楚,暗中同意创设的prototype属性只拥有constructor和后续至Object的习性,原型形式就是为prototype添加属性和方法

Test.prototype.getName = ()=> {
    alert('name')
}

此时的instance实例就所有了getName方法,因为实例的指针是指向Test.prototype的

instance.__proto__ === Test.prototype

正如图所示
![897RVF]E5@IX$)`IVJ3BOSY.png](http://upload-images.jianshu.io/upload\_images/3637499-2c25e10269d8bbbd.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

此处我们可得知:实例instance与构造函数之间是由此原型prototype来相关联的。

c.组合形式
那种方式大家用的最多,其实也是原型情势的另1种写法,只可是有某个小差别而已

function Test() {}

Test.prototype = {
    getName() {
        alert('name')
    }
}

大家平常会这么向来重写prototype方法,由上大家可以,prototype会暗许自带constructor属性指向构造函数本人,那么重写今后吧?

Test.prototype.constructor === Object 
// 而并不等于Test了
// 因为重写以后相当于利用字面量方式创建一个实例对象,这个实例的构造函数是指向Object本身的

理所当然大家也得以手动赋值constructor

Test.prototype = {
    constructor: Test,
    getName() {
        alert('name')
    }
}

那正是说又会有疑问了constructor要不要有啥意义?笔者认为constructor意义仅仅是为着来识别原型所属的构造函数吧。

当要求获得有些属性的时候,会先从实例中查找,未有就依照指针所针对的原型去寻找,依次向上,直到实例的指针__proto__本着为null时停下查找,例如:

// 1 读取name
instance.name 

// 2 instance.__proto__ === Test.prototype
Test.prototype.name

// 3 Test.prototype.__proto__ === Object.prototype
Object.prototype.name

// 4
Object.prototype.__proto__ === null

当找到了那性格子就会一向回到,而不会三番五次查找,固然这几个属性值为null,想要继续寻找,我们得以由此delete操作符来促成。

由那里大家自然能够想到Array, Date, Function, String,都以3个构造函数,他们的原型的指针都以指向Object.prototype,它们就如自身那里定义的Test一律,只可是是原生自带而已

d.多少个有效的措施

  • Object.getPrototypeOf() 获取某些实例的指针所针对的原型

Object.getPrototypeOf(instance) === Test.prototype
  • hasOwnProperty
    判断多特性能是存在于实例中或然存在于原型中,如图所示:

    图片 5

    NY~N}CNR`}8W%4QA$M8LFE4.png

  • in操作符,无论该属性是或不是可枚举

'name' in instance  // true
'getName' in instance // true

任凭属性是在实例中,依旧在原型中都再次回到true,所以当大家供给判定一个性质存在与实例中,依旧原型中有二种办法

// 一种就是使用hasOwnProperty判断在实例中
// 另一种判断在原型中
instance.hasOwnProperty('getName') === false && 'getName' in instance === true
  • for ... in操作符也是相同的,但只会列出可枚举的习性,ie八版本的bug是随便该属性是还是不是可枚举,都会列出

    图片 6

    D(%S__GN8404{H9X6PW$DVK.png

name是在实例中定义的,getName是在原型中定义的
  • Object.keys()则不雷同,它回到三个目标上全部可枚举的习性,仅仅是该实例中的

Object.keys(instance)
// ["name"]

e.总结
上述商讨了构造函数,原型和实例的关联:

  • 各类构造函数都有原型对象
  • 各样原型对象都有2个constructor指南针指向构造函数
  • 各种实例都有多少个__proto__指南针指向原型

模拟类继承

末尾,大家选用那么些规律还可以完结比较完善的类继承:

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

能够相比一下ES陆的类继承:

JavaScript

(function(global){“use strict” //类的概念 class Animal {
//ES陆中最新协会器 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);

原型继承

“原型”
那么些词本人源自心绪学,指传说、宗教、梦境、幻想、艺术学中连连重复出现的意象,它源自民族记念和原有经验的国有无意识。

据此,原型是一种浮泛,代表事物表象之下的联系。用简短的话来说,正是原型叙述事物与事物之间的相似性

想像3个小孩子怎么着认知那几个世界:

当孩子没见过老虎的时候,大人恐怕会教他,老虎呀,就如2只大猫。假诺这一个孩子刚刚平常和街坊家的喵咪玩耍,那么她不用去动物园见到真实的大虫,就能设想出老虎差不多是长什么样样子。

图片 7

本条逸事有个更简明的表达,叫做“里丑捧心”。尽管大家用JavaScript的原型来描述它,正是:

function Tiger(){
    //...
}

Tiger.prototype = new Cat(); //老虎的原型是一只猫

很鲜明,“画虎不成反类犬”(只怕反过来“照虎画猫”,也能够,取决孩子于先认识老虎照旧先认识猫)是1种认知情势,它令人类小孩子不须要在脑际里再度完全营造2只猛虎的任何音信,而能够通过他熟识的猫猫“复用”得到老虎的大部新闻,接下去她只需求去到动物园,去调查老虎和喵咪的分裂部分,就能够正确认知什么是老虎了。那段话用JavaScript可以描述如下:

function Cat(){

}
//小猫喵喵叫
Cat.prototype.say = function(){    
  return "喵";
}
//小猫会爬树
Cat.prototype.climb = function(){
  return "我会爬树";
}

function Tiger(){

}
Tiger.prototype = new Cat();

//老虎的叫声和小猫不同,但老虎也会爬树
Tiger.prototype.say = function(){
  return "嗷";
}

于是,原型能够通过讲述几个东西之间的形似关系来复用代码,大家能够把那种复用代码的形式称为原型继承

Object.prototype.nameStr=”Object Prototype”;
Object.prototype.getName=function(){return this.nameStr};
alert(Object.getName());//Object Prototype

2.继承

接轨的精神是行使构造函数的原型 =
某些构造函数的实例,以此来形成原型链。例如

// 定义父类
function Parent() {}
Parent.prototype.getName = ()=> {
    console.log('parent')
}
// 实例化父类
let parent = new Parent()

// 定义子类
function Child() {}
Child.prototype = parent 
// 实例化子类
let child = new Child()

child.getName() // parent
// 此时
child.constructor === parent.constructor === Parent

a.最经典的继续情势

function Parent(name) {
    this.name = name
    this.colors = ['red']
}
Parent.prototype.getName = function() {
    console.log(this.name)
}
// 实例化父类
let parent = new Parent()

function Child(age, name) {
    Parent.call(this, name)
    this.age = age
}
Child.prototype = parent 
// 实例化子类
let child = new Child(1, 'aaa')
child.getName() // parent

那边会让自身想到ES陆中的class继承

class Parent {
    constructor(name) {
        this.name = name
        this.colors = ['red']
    }
    getName() {
        console.log(this.name)
    }
}

class Child extends Parent {
    constructor(age, name) {
        super(name)
    }
}

let child = new Child(1, 'aaa')
child.getName() // parent

事实上是三个道理,那里大家简单想到,将Child.prototype针对parent实例,正是运用原型达成的存在延续,而为了各类实例都兼备各自的colors和name,也正是基础属性,在Child的构造函数中call调用了Parent的构造函数,也就是每便实例化的时候都开头化一遍colors和name,而不是富有实例共享原型链中的colors和name


上述也是团结单方面学习壹边整理的,逻辑有点杂乱,见谅,还望有误之处提出,不胜谢谢!

参考:
红宝书第6章
MDN
继承与原型链
明亮JavaScript的原型链和接二连三

相关

  • JavaScript原型与继承(一)
  • JavaScript原型与后续(2)
  • JavaScript原型与持续(三)

发表评论

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

网站地图xml地图