摘要:组合式继承是最常用的继承模式,但组合继承使用过程中会被调用两次一次是创建子类型的时候,另一次是在子类型构造函数的内部。
首先需要了解原型链机制: 原型链作为实现继承的主要方法,其基本思想就是利用原型让一个引用类型继承另 一个引用类型的属性和方法.
构造函数、原型、实例之间的关系: 每个构造函数都有一个原型对象(prototype),原型对象都包含一个指向构造函数的指针(constructor),而实例都包含一个指向原型对象的内部指针(__propto__).
自我理解: 其实每个Function都是Object基类的一个实例,所以每个Function上都有一个__proto__指向了Object.prototype.当查找一个实例的属性时,会先从这个实例的自定义属性上找,如果没有的话通过__proto__去实例所属类的原型上去找,如果还没有的话再通过原型(原型也是对象,只要是对象就有__proto__属性)的__proto__到Object的原型上去找,一级一级的找,如果没有就undefined(Object的__proto__返回undefined).
(一) 原型链继承 :
function Parent(name) { this.name = name; } Parent.prototype.printName = function() { console.log("parent name:", this.name); } function Child(name) { this.name = name; } Child.prototype = new Parent("father"); Child.prototype.constructor = Child;//由于Child.prototype继承Parent,导致constructor丢失 Child.prototype.printName = function() { console.log("child name:", this.name); } var child = new Child("son"); child.sayName(); // child name: son
这种方法存在两个缺点:
1.子类型无法给超类型传递参数; 2.Child.prototype.sayName 必须写在 Child.prototype = new Parent("father"); 之后,不然就会被覆盖掉。
(二) 类式继承:
function Parent(name) { this.name = name; } Parent.prototype.printName = function() { console.log("parent name:", this.name); } Parent.prototype.doSomthing = function() { console.log("parent do something!"); } function Child(name, parentName) { Parent.call(this, parentName); this.name = name; } Child.prototype.printName = function() { console.log("child name:", this.name); } var child = new Child("son"); child.printName(); // child name: son child.doSomthing(); // TypeError: child.doSomthing is not a function
相当于 Parent 这个函数在 Child 函数中执行了一遍,并且将所有与 this 绑定的变量都切换到了 Child 上,这样就克服了第一种方式带来的问题。
缺点:没有原型,每次创建一个 Child 实例对象时候都需要执行一遍 Parent 函数,无法复用一些公用函数。
(三) 组合式继承:前两种方式的结合
function Parent(name) { this.name = name; } Parent.prototype.printName = function() { console.log("parent name:", this.name); } Parent.prototype.doSomething = function() { console.log("parent do something!"); } function Child(name, parentName) { Parent.call(this, parentName);// 第二次调用 this.name = name; } Child.prototype = new Parent();// 第一次调用 Child.prototype.constructor = Child; Child.prototype.printName = function() { console.log("child name:", this.name); } var child = new Child("son"); child.printName(); // child name: son child.doSomething(); // parent do something!
组合式继承是比较常用的一种继承方法,其背后的思路是使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。
这样,既通过在原型上定义方法实现了函数复用,又保证每个实例都有它自己的属性。
组合式继承是 JS 最常用的继承模式,但组合继承使用过程中会被调用两次:一次是创建子类型的时候,另一次是在子类型构造函数的内部。
第一次调用构造函数显然是没有必要的,因为第一次调用构造函数时候不需要函数内部的那些实例属性,这么写只是想获得其原型上的方法罢了,所以这时候你可能会这样写:
Child.prototype = Parent.prototype;
这样写显然是不对的:
1.首先,你这样写的话相当于是子类和父类都指向同一个对象,这时候如果你添加了新的方法给 Child 但实际上 Parent 并不需要,相当于强行给 Parent 添加了一个未知的方法。
2.其次,仔细想想,这样体现不出继承的多态性,比如此时子类想要重写父类的 getName 的方法,那么父类的方法也就会随之修改,这显然违背了多态性。
也就是说我们第一次调用构造函数的时候,其实是不管构造函数里面的内容,这是我们可以new一个空函数,将其prototype指向Parent.prototype,代码如下:
(四) 寄生组合式继承:
function Parent(name) { this.name = name; } Parent.prototype.printName = function() { console.log("parent name:", this.name); } function Child(name, parentName) { Parent.call(this, parentName); this.name = name; } function inheritPrototype(Parent, Child) { Child.prototype = Object.create(Parent.prototype); //修改 Child.prototype.constructor = Child; } inheritPrototype(Parent, Child); Child.prototype.printName = function() { console.log("child name:", this.name); } Child.prototype.constructor = Child; var parent = new Parent("father"); parent.printName(); // parent name: father var child = new Child("son", "father"); child.printName(); // child name: son
(五) ES 6 继承:
class Parent { constructor(name) { this.name = name; } doSomething() { console.log("parent do something!"); } printName() { console.log("parent name:", this.name); } } class Child extends Parent { constructor(name, parentName) { super(parentName); this.name = name; } printName() { console.log("child name:", this.name); } } const child = new Child("son", "father"); child.printName(); // child name: son child.doSomething(); // parent do something! const parent = new Parent("father"); parent.printName(); // parent name: father
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/97881.html
摘要:今天闲来无事,看见几行小字。又说所有对象,继承终是。强行押韵一波这首诗的意思就是说的我今天没有什么事情,然后无意中又在网上看到了任何对象都是从对象继承而来的这句话。一时兴起,便去验证这句话。 今天闲来无事,看见几行小字。又说所有对象,继承终是Obj。—— 强行押韵一波 这首诗的意思就是说的我今天没有什么事情,然后无意中又在网上看到了任何对象都是从Object对象继承而来的这句话。一时兴...
摘要:继承可以使得子类具有父类别的各种属性和方法。继承是类与类之间的关系。继承的实质就是两次的原型搜索,像是实例属性而不是继承,才是继承。更多用法见继承。 前言 面试中最常会问到的问题:什么是继承?如何分别用 ES5 和 ES6 实现?想要学习继承,必须先学好原型与原型链,如果此部分还不清楚,请先学习此部分再来阅读本文,可参考我的文章JS之原型与原型链或浏览其他相关的学习网站。 定义 继承...
摘要:前奏的构造函数就是函数的本身正文的继承是通过函数结合原型而实现的,继承是先实例化父类直接继承使用这个词来定义类的构造函数是函数的继承用与继承是实例化子类对象的时候继承父类然后继承 最开始接触的时候es5,js的类概念是 通过函数 实现的。 前奏:showImg(https://segmentfault.com/img/bVbuYHF?w=468&h=345);es5的构造函数就...
摘要:前言见解有限,如有描述不当之处,请帮忙及时指出,如有错误,会及时修正。倘若用的是中文搜索。所以最终的实例对象仍然能进行正常的原型链回溯,回溯到原本的所有原型方法这样通过一个巧妙的欺骗技巧,就实现了完美的继承。 前言 见解有限,如有描述不当之处,请帮忙及时指出,如有错误,会及时修正。 20180201更新: 修改用词描述,如组合寄生式改成寄生组合式,修改多处笔误(感谢@Yao Ding的...
摘要:但是,的本质仍然是函数,是构造函数的另外一种写法。报错原生构造函数的继承对于一些原生的构造函数,比如,,,等,在是无法通过方法实现原生函数的内部属性,原生函数内部的无法绑定,内部属性获得不了。 在没有学习 ES6 之前,学习 React,真的是一件非常痛苦的事情。即使之前你对 ES5 有着很好的基础,包括闭包、函数、原型链和继承,但是 React 中已经普遍使用 ES6 的语法,包括 ...
摘要:一般我们对这种构造函数命名都会采用,并把它称呼为类,这不仅是为了跟的理念保持一致,也是因为的内建类也是这种命名。由生成的对象,其是。这是标准的规定。本文的主题是原型系统的变迁,所以并没有涉及和对原型链的影响。 概述 JavaScript 的原型系统是最初就有的语言设计。但随着 ES 标准的进化和新特性的添加。它也一直在不停进化。这篇文章的目的就是梳理一下早期到 ES5 和现在 ES6,...
阅读 3193·2021-11-18 10:02
阅读 3417·2021-10-11 10:58
阅读 3324·2021-09-24 09:47
阅读 1095·2021-09-22 15:21
阅读 3858·2021-09-10 11:10
阅读 3254·2021-09-03 10:28
阅读 1723·2019-08-30 15:45
阅读 2096·2019-08-30 14:22