最近在看《JavaScript设计模式》,然后开篇复习了JavaScript中的几种继承方式,自己似乎也没有怎么仔细探究过,目前自己没怎么碰到过应用的场景(噗),所以借这次机会好好来屡屡思路。

方式1 类式继承

例子

function Person() {this.telephone = ['000-0000-0000'];
}function Student(className) {this.className = className;
}Student.prototype = new Person();
var Haha = new Student(1);
var Xixi = new Student(2);

创建好父类和子类。联系他们的方式是把学生的prototype指向一个人的实例。

问:prototype是什么?
几乎任何对象有一个[[prototype]]属性,在标准中,[[prototype]]一个隐藏属性,指向的是这个对象的原型。而它的指向是由构造该对象的方法决定的:
1.对象字面量构造:其[[prototype]]指向Object.prototype。

var person = {};

2.构造函数构造:new操作符调用的函数就是构造函数。其[[prototype]]和其构造函数的prototype指向相同。而构造函数prototype属性指向的对象带有constructor属性,指向函数自身。

function Person(){}
var person = new Person();

此图为Person的prototype内容,可以看到constructor属性实际指向的就是Person()函数。(小绿色框框内和外面绿色框框其实是同一个内容)。

3.Object.create构造的。

var person = {};
var Haha = Object.create(person);

这里对象Haha的[[prototype]]指向对象person。也可以写null,此时对象Haha就没有原型。

首先要分清楚类和实例,在控制台显示中,只有类才会有prototype属性,而实例是拥有一个名为_proto_的属性,它会指向构造它函数的原型,两者本质都是一个指针。

function Person() {this.telephone = ['000-0000-0000'];
}
var Hehe = new Person();
console.log(Person.prototype);
console.log(Hehe);

以上代码运行结果:

可以瞧见,这里Hehe的_proto_是指向了Person.prototype

问:new关键字的作用是什么?
new关键字运作的过程如下,引用自《JavaScript》高级程序设计:

1、创建一个空对象,并且 this 变量引用该对象,同时还继承了该函数的原型。 2、属性和方法被加入到 this 引用的对象中。
3、新创建的对象由 this 所引用,并且最后隐式的返回 this。

简单来说,它创建了一个空对象,指定了原型,把属性方法进行拷贝,并把this指向进行了改变。假如我们把上面的代码改成:

function Person() {this.telephone = ['000-0000-0000'];
}var Hehe = Person();
console.log(Hehe.telephone);

去掉new关键词赋予Person(),会报错,而输出window.telphone得到的就是['000-0000-0000']。因为函数的返回值(没有返回值所以是undefined)赋予给了Hehe,尝试去读取undefined的属性,报错了。而此时函数运行中的this是全局变量window。

So,回归类式继承,仔细看看诞生的嘻嘻和哈哈两位同学

会发现,各自都有自己的班级名属性,但是原型指向的是同一个Person实例,所以如果嘻嘻有两个号码,或者他要更改自己的号码,那哈哈的电话号码也会发生变化,他们只能共享这个电话号码。

方式2 构造函数继承

例子

function Person(name) {this.name = name;
}Person.prototype.showName = function() {console.log(this.name);
}function Student(name, className) {this.className = className;Person.call(this, name);
}
var Haha = new Student('Haha', 1);
var Xixi = new Student('Xixi', 2);

问:call函数的运作过程?
call函数和apply函数的作用相同,不同之处就是apply函数只能传入2个参数,而call函数可以有多个。F.call(thisArg,[arg1……]) 函数的运作过程如下(来源网络):

1.先判断F是否为一个函数,如果不是一个函数,那么将抛出TypeError异常。
2.创建一个内部类型空列表list
3.然后如果参数除去thisArg外还有其他参数的话,就将这些值添加到list中
4.thisArg和list作为F内部属性[[Call]]的参数传入调用进行函数的执行操作

简而言之就是它把一个函数的对象上下文改成了由 thisArg指定的新对象。

So,回归构造函数继承,仔细看看诞生的嘻嘻和哈哈两位同学

可以看到两个实例都拥有了className和name两个属性,因为call方法的运行类似于执行了Haha.name='Haha'Xixi.name='Xixi'
但是因为没有与父类的原型相联系,所以父类原型中的方法,不能得到继承。运行Haha.showName()会得到报错。

方式3 组合继承

例子

function Person(name) {this.name = name;
}Person.prototype.showName = function() {console.log(this.name);
}function Student(name, className) {this.className = className;Person.call(this, name);
}
Student.prototype = new Person();
Student.prototype.showClassName = function() {console.log(this.className);
}var Haha = new Student('Haha', 1);
var Xixi = new Student('Xixi', 2);

组合继承综合了类式继承和构造函数继承,在把父类的属性继承后,把子类的原型指向了父类实例,这样就可以继承父类原型的方法了。
但是这里相当于使用了两次父类函数,并且子类不是父类的实例,子类的原型是父类的实例,所以还会有更好的方法。

方式4 原型继承

function inheritObject(o) {function F() {}F.prototype = o;return new F();
}var person = {name: "unknown",telephone: ["000-0000-0000"]
}var Xixi = inheritObject(person);
Xixi.name = "Xixi";
Xixi.telephone.push("111-1111-1111");var Haha = inheritObject(person);
Haha.name = "Haha";

仔细看看诞生的嘻嘻和哈哈两位同学

这里.name给Xixi实例添加了一个自己的name属性,而push操作是直接影响原型中引用变量,所以改进之后又有了下面这种方式。
在这里我产生了一个疑问,为什么name属性是自己添加新的,而telephone是采用原来的。于是添加了一个age属性,执行Xixi.age++操作。

这里可以看到实例重新添加了一个age属性,所以我们可以说只要是改变原型属性的值,就会把新的属性加在实例上,引用不改变是因为引用的地址还没有改变。

方式5 寄生式继承

寄生式继承是在原型继承的基础之上,我们需要再添加一下代码:

function createPerson(obj) {var o = inheritObject(obj);o.getName = function(){console.log(name);}return o;
}

这样就给得到的对象添加了公共方法。

方式6 寄生组合式继承

寄生组合式继承是为了弥补组合式继承的缺点,是在寄生式继承+构造函数继承组合而成的:

function inheritObject(o) {function F() {}F.prototype = o;return new F();
}
function inheritPrototype(subClass, superClass) {//复制一份父类原型var p = inheritObject(superClass.prototype);//修正重写子类原型导致constructor属性被修改p.constructor = subClass;//设置子类原型subClass.prototype = p;
}function Person(name) {this.name = name;
}Person.prototype.showName = function() {console.log(this.name);
}function Student(name, className) {this.className = className;Person.call(this, name);
}inheritPrototype(Student, Person);Student.prototype.showClassName = function() {console.log(this.className);
}var Xixi = new Student('Xixi',2);
var Haha = new Student('Haha',1);

以下为嘻嘻和哈哈的内容:

可以对比一下组合式继承的结果:

不同的地方在于把子类原型的构造函数改成了实例对应的构造函数,在组合继承中子类原型直属并没有constructor属性。

【呆萌の研究】JavaScript常见的继承方式相关推荐

  1. JavaScript之各种继承方式和优缺点

    2019独角兽企业重金招聘Python工程师标准>>> JavaScript之各种继承方式和优缺点 原型链继承 function Parson(){this.name = 'hy' ...

  2. javascript中的继承方式

    javascript中的继承方式有好几种. 下面分别举例供大家参考学习: 1.function parent() { this.x=1; } function child() { var instan ...

  3. JavaScript中6种常见的继承方式

    为什么需要继承? 在实际编码的过程中,如果有很多类似的方法都存放于构造函数中,这样会导致内存的浪费(内存泄漏),在这种情况下我们就需要用到继承. 继承是什么? 所谓继承就是通过某种方式让一个对象可以访 ...

  4. “约见”面试官系列之常见面试题第三十八篇之js常见的继承方式(建议收藏)

    1.原型链继承 核心: 将父类的实例作为子类的原型 将构造函数的原型设置为另一个构造函数的实例对象,这样就可以继承另一个原型对象的所有属性和方法,可以继续往上,最终形成原型链 父类 // 定义一个动物 ...

  5. Javascript七种继承方式

    <!DOCTYPE html> <html><head><meta charset="utf-8"><title>< ...

  6. Js理解之路:Js常见的6中继承方式

    目录 一.JS 实现继承的几种方式 第一种:原型链继承 二.构造函数继承(借助call方法) 三.组合继承(原型链继承+构造函数继承) 第四种:原型式继承(借助Object.create) 第五种:寄 ...

  7. 探究JS常见的6种继承方式

    先看以下百科对(面向对象的继承)的解释! 通过以上精炼实用的解释,我们可以了解到继承的基本作用和功能!即可以使得子类具有父类的属性和方法或者重新定义.追加属性和方法等. 广告:帮忙点击>> ...

  8. js常见的的6种继承方式

    继承是面向对象的,继承可以帮助我们更好的复用以前的代码,缩短开发周期,提高开发效率:继承也常用在前端工程技术库的底层搭建上,在整个js的学习中尤为重要 常见的继承方式有以下的六种 一.原型链继承 原型 ...

  9. php中 继承中的概念,JavaScript_JavaScript中的继承方式详解,js继承的概念 js里常用的如下 - phpStudy...

    JavaScript中的继承方式详解 js继承的概念 js里常用的如下两种继承方式: 原型链继承(对象间的继承) 类式继承(构造函数间的继承) 由于js不像java那样是真正面向对象的语言,js是基于 ...

最新文章

  1. 鱼骨图分析法实际案例_【管理工具详解】鱼骨图分析法
  2. 一篇不错的讲解Java异常的文章(转载)
  3. VTK:点定位器用法实战
  4. Python多线程3:queue
  5. vue前期知识点笔记
  6. 【转】Linux的五个查找命令:find,locate,whereis,which,type
  7. pg 事务 存储过程_PgpoolII实现数据分区存储及性能分析
  8. android ant build.xml实例
  9. 62.不同的路径(力扣leetcode) 博主可答疑该问题
  10. serialVersionUID详解
  11. Java 上传附件后端接口大体流程和逻辑
  12. 正则表达式用法及实例
  13. 当系统中存在多个浏览器,如何设置IE为自己的默认浏览器
  14. html图片做成菱形,CSS秘密花园:菱形图片
  15. 制作自己的iconfont 图片转iconfont
  16. 打造自己的MVC框架
  17. 关于vuze(毒蛙)linux版本移植的问题
  18. linux ps 源代码,【linux】ps(示例代码)
  19. L1-7 谷歌的招聘
  20. ARM裸机——2.ARM体系结构(1)

热门文章

  1. 通过判断流的头 判断文件类型
  2. Kotlin教程 - 收藏集 - 掘金
  3. Apple 如何知道你使用了私有API
  4. 以色列政府网站遭史上规模最大的DDoS 攻击
  5. 思科修复NSA报告的Nexus 交换机DoS漏洞及其它
  6. 专访勒索组织“范本”:Thedarkoverlord 喜欢并享受辽阔的狩猎场
  7. 中秋逢国庆 | 盛世华诞 阖家团圆
  8. 前方两万字高能预警!SMBGhost SMBleed 漏洞深入研究
  9. 刚刚GitHub 收购 npm,旨在提升开源软件供应链安全
  10. curl 使用 ~/.netrc ( Windows 上是 _netrc ) 问题