一、原型继承父类的实例

  1. //父类及其原型属性/方法
  2. function SuperType () {
  3. this.name = ['zc','ls','ww'];
  4. }
  5. SuperType.prototype.getSuperName = function() {
  6. return this.name;
  7. };
  8.  
  9. //子类及其原型属性/方法
  10. function SubType() {
  11. this.test = ['a','b','c','d'];
  12. }
  13. //子类型的原型指向父类型的实例(即子类的原型复制了父类的构造器以及父类原型属性/方法)
  14. SubType.prototype = new SuperType();
  15. //为子类原型添加原型拓展属性/方法
  16. SubType.prototype.getSubTest = function() {
  17. return this.test;
  18. }
  19.  
  20. var instance1 = new SubType();
  21. instance1.name.push('yzy');//name属性是原型继承自父类实例
  22. instance1.test.push('e');//test属性是源于子类本身的构造器
  23. console.log(instance1.name,instance1.test)
  24.  
  25. var instance2 = new SubType();
  26. console.log(instance2.name,instance2.test)

控制台输出:

标注:

注意这里的子类原型指向一个父类的实例(引用传递),那么这块的父类实例就是内存中的一块地址,以后所有的子类实例都会有一个原型属性指向这块地址,并且子类A对这块地址中数据更改也会影响到子类B。

图示:

所以你可以看到,instance1.name是从父类实例来的,这个属性实际存在于这个单例,访问的时候都是引用传递,由于这个单例是共享的,instance1 push了一个数据,那么就算instance2没有任何动作,instance2读的时候数据也会是变化后的数据;

而对于test属性,是子类自身的,所以这个属性值存在于子类实例自身,相互之间互不影响,所以虽然instance1.test push了一个数据,但是instance2访问的时候丝毫不受影响。

缺点:继承自父类实例的原型属性会被所有实例所共享。

二、构造函数伪装(call()、apply())

  1. //父类及其原型属性/方法
  2. function SuperType(name) {
  3. this.name = name;
  4. this.color = ['green','red'];
  5. }
  6. SuperType.prototype.getSuperName = function() {
  7. return this.name;
  8. }
  9.  
  10. //子类及其原型属性/方法
  11. function SubType(name) {
  12. SuperType.call(this, name);
  13. this.test = ['a','b','c','d'];
  14. }
  15. SubType.prototype.getSubTest = function() {
  16. return this.test;
  17. }
  18.  
  19. var instance1 = new SubType('Jack');
  20. console.log(instance1.name,instance1.getSubTest());
  21. console.log('------------------------');
  22. console.log(instance1.getSuperName())

控制台输出:

标注:

call()方法实际上就是在当前作用域拷贝了一下函数执行者的构造函数/方法,所以上述call()方法实际上做了如下的事情

  1. //子类及其原型属性/方法
  2. function SubType(name) {
  3. //SuperType.call(this, name);
  4. this.name = name;
  5. this.color = ['green','red'];
  6.  
  7. this.test = ['a','b','c','d'];
  8. }

注意的是,call()函数伪装并不会在当前作用域执行 SuperType 原型下的方法/属性

所以,因为 getSuperName() 是父类原型下的方法,所以call() 方法自然不会复制该方法给 SubType 构造器,因此控制台报错也就是理所当然的咯

缺点:函数伪装不会继承父类原型下的属性/方法。

三、组合继承(函数伪装 + 原型继承)

  1. //父类及其原型属性/方法
  2. function SuperType(name) {
  3. this.name = name;
  4. }
  5. SuperType.prototype.getSuperName = function () {
  6. return this.name;
  7. }
  8.  
  9. // 子类1及其原型属性/方法
  10. function SubType1(name) {
  11. SuperType.call(this, name);
  12. this.test = ['h1', 'h2', 'h3', 'h4'];
  13. }
  14. SubType1.prototype = SuperType.prototype;
  15. SubType1.prototype.getSubTest = function () {
  16. return this.test;
  17. }
  18.  
  19. // 子类2及其原型属性/方法
  20. function SubType2(name) {
  21. SuperType.call(this, name);
  22. this.age = ;
  23. }
  24. SubType2.prototype = SuperType.prototype;
  25. SubType2.prototype.getSubAge = function () {
  26. return this.age;
  27. }
  28.  
  29. var instance1 = new SubType1('Jack');
  30. var instance2 = new SubType2('Tom');
  31. console.log(instance1,instance2);

控制台输出:

标注:

①这里子类原型继承自父类原型,然后子类为原型添加了原型拓展,这里的原型继承是引用传递,所以添加拓展的操作都是基于同一块内存地址的。

图示:

所以,无论是父类的原型属性还是子类继承的原型(父类原型),实际上都是引用传递,都指向内存中的同一块地址,因此,上述的代码,虽然子类2虽然没有原型方法 getSubTest,但是实际上子类1已经在他们指向的共同内存地址添加了该方法,同理子类1也是。

缺点:子类型的原型属性共享。

四、寄生组合式继承

  1. function object(o) {
  2. function F() { };
  3. F.prototype = o;
  4. return new F();
  5. }
  6. //寄生组合式继承
  7. function inheritPrototype(subType, superType) {
  8. var prototype = object(superType.prototype);
  9. subType.prototype = prototype;
  10. }
  11.  
  12. //父类及其原型属性/方法
  13. function SuperType(name) {
  14. this.name = name;
  15. }
  16. SuperType.prototype.getSuerperName = function () {
  17. return this.name;
  18. }
  19.  
  20. //子类1及其原型属性/方法
  21. function SubType(name) {
  22. SuperType.call(this, name);
  23. this.test = ['h1', 'h2', 'h3', 'h4'];
  24. }
  25. inheritPrototype(SubType, SuperType);
  26. SubType.prototype.getSubTest = function () {
  27. return this.test;
  28. };
  29.  
  30. //子类2及其原型属性/方法
  31. function SubType2(name) {
  32. SuperType.call(this, name);
  33. this.test2 = ['s1', 's2', 's3', 's4'];
  34. }
  35. inheritPrototype(SubType2, SuperType);
  36. SubType2.prototype.getSubTest2 = function () {
  37. return this.test2;
  38. };
  39.  
  40. /* 以下为测试代码示例 */
  41. var instance1 = new SubType(['yyy', 'Jack', 'Nick']);
  42. var instance2 = new SubType2(['yyy2', 'Jack2', 'Nick2']);
  43. console.log(instance1,instance2)

控制台输出:

标注:

①我们看这个寄生组合式继承的处理方式,传进来一个子类和父类,子类的原型 = 新对象(新对象的原型 = 父类的原型),所以就是子类原型下的原型 = 父类的原型

这就是我们所看到的上面控制台输出的结果了,父类的原型挂在子类原型下的原型下,这样为各个子类添加原型的时候就不会影响挂在上面的父类原型了。

但是,由于依旧是引用传递,所以这个子类原型下原型(继承自父类的原型)依旧是共享的

图示:

为达上述目的,我这边直接将父类实例挂在子类原型上,也是可以的:

  1. //寄生组合式继承
  2. function inheritPrototype(subType, superType) {
  3. subType.prototype =new superType();
  4. }
  5.  
  6. //父类及其原型属性/方法
  7. function SuperType(name) {
  8. if(name){
  9. this.name = name;
  10. }
  11. }
  12. SuperType.prototype.getSuerperName = function () {
  13. return this.name;
  14. }
  15.  
  16. //子类1及其原型属性/方法
  17. function SubType(name) {
  18. SuperType.call(this, name);
  19. this.test = ['h1', 'h2', 'h3', 'h4'];
  20. }
  21. inheritPrototype(SubType, SuperType);
  22. SubType.prototype.getSubTest = function () {
  23. return this.test;
  24. };
  25.  
  26. //子类2及其原型属性/方法
  27. function SubType2(name) {
  28. SuperType.call(this, name);
  29. this.test2 = ['s1', 's2', 's3', 's4'];
  30. }
  31. inheritPrototype(SubType2, SuperType);
  32. SubType2.prototype.getSubTest2 = function () {
  33. return this.test2;
  34. };
  35.  
  36. /* 以下为测试代码示例 */
  37. var instance1 = new SubType(['yyy', 'Jack', 'Nick']);
  38. var instance2 = new SubType2(['yyy2', 'Jack2', 'Nick2']);
  39. console.log(instance1,instance2)

标注:

②这里挂载在子类原型下的原型的是一个父类的实例,值得注意的是,实例化一个父类实例是会自动调用父类构造器的,所以会将父类构造器以及父类原型一同挂载到子类原型下的原型下,不妨让我们把上述例子中的父类构造器if判断去掉看看控制台输出结果:

讲到这里你是不是觉得已经结束了???当然~~~没有!

上面说过:这个子类原型下原型(继承自父类的原型)依旧是共享的!

那么我后来做了个实验:

Ⅰ.父类原型属性值是基本数据类型

  1. //寄生组合式继承
  2. function inheritPrototype(subType, superType) {
  3. subType.prototype =new superType();
  4. }
  5.  
  6. //父类及其原型属性/方法
  7. function SuperType(name) {
  8. if(name){
  9. this.name = name;
  10. }
  11. }
  12. SuperType.prototype.getSuerperName = function () {
  13. return this.name;
  14. }
  1.  

  1. SuperType.prototype.age =
  2. SuperType.prototype.console = function(){
  3. this.age += ;
  4. console.log(this.age)
  5. };

  1. //子类1及其原型属性/方法
  2. function SubType(name) {
  3. SuperType.call(this, name);
  4. this.test = ['h1', 'h2', 'h3', 'h4'];
  5. }
  6. inheritPrototype(SubType, SuperType);
  7. SubType.prototype.getSubTest = function () {
  8. return this.test;
  9. };
  10.  
  11. //子类2及其原型属性/方法
  12. function SubType2(name) {
  13. SuperType.call(this, name);
  14. this.test2 = ['s1', 's2', 's3', 's4'];
  15. }
  16. inheritPrototype(SubType2, SuperType);
  17. SubType2.prototype.getSubTest2 = function () {
  18. return this.test2;
  19. };
  20.  
  21. /* 以下为测试代码示例 */
  22. var instance1 = new SubType(['yyy', 'Jack', 'Nick']);
  23. var instance2 = new SubType2(['yyy2', 'Jack2', 'Nick2']);
  24. instance1.console();
  25. instance1.console();
  26. instance1.console();
  27. instance1.console();
  28. instance1.console();
  29. instance2.console();

控制台输出:

结果说明:父类原型下的age属性没有共享!

Ⅱ.父类原型属性值是非基本数据类型(例如:对象):

  1. //寄生组合式继承
  2. function inheritPrototype(subType, superType) {
  3. subType.prototype =new superType();
  4. }
  5.  
  6. //父类及其原型属性/方法
  7. function SuperType(name) {
  8. if(name){
  9. this.name = name;
  10. }
  11. }
  12. SuperType.prototype.getSuerperName = function () {
  13. return this.name;
  14. }


  1. SuperType.prototype.age = {
  2. age:
  3. }
  4. SuperType.prototype.console = function(){
  5. this.age.age += ;
  6. console.log(this.age.age)
  7. };

  1. //子类1及其原型属性/方法
  2. function SubType(name) {
  3. SuperType.call(this, name);
  4. this.test = ['h1', 'h2', 'h3', 'h4'];
  5. }
  6. inheritPrototype(SubType, SuperType);
  7. SubType.prototype.getSubTest = function () {
  8. return this.test;
  9. };
  10.  
  11. //子类2及其原型属性/方法
  12. function SubType2(name) {
  13. SuperType.call(this, name);
  14. this.test2 = ['s1', 's2', 's3', 's4'];
  15. }
  16. inheritPrototype(SubType2, SuperType);
  17. SubType2.prototype.getSubTest2 = function () {
  18. return this.test2;
  19. };
  20.  
  21. /* 以下为测试代码示例 */
  22. var instance1 = new SubType(['yyy', 'Jack', 'Nick']);
  23. var instance2 = new SubType2(['yyy2', 'Jack2', 'Nick2']);
  24. instance1.console();
  25. instance1.console();
  26. instance1.console();
  27. instance1.console();
  28. instance1.console();
  29. instance2.console();

控制台输出:

结果说明:父类原型下的age属性共享!

综上所述:

  原型上的基本数据类型属性是值传递(内存地址不共享);

  原型上的非基本数据类型属性是引用传递(内存地址共享)。

js继承的实现(原型/链、函数伪装)的更多相关文章

  1. 【前端知识体系-JS相关】深入理解JavaScript原型(继承)和原型链

    1. Javascript继承 1.1 原型链继承 function Parent() { this.name = 'zhangsan'; this.children = ['A', 'B', 'C' ...

  2. js继承之组合继承(结合原型链继承 和 借用构造函数继承)

    在我的前两篇文章中,我们已经介绍了 js 中实现继承的两种模式:原型链继承和借用构造函数继承.这两种模式都存在各自的缺点,所以,我们考虑是否能将这二者结合到一起,从而发挥二者之长.即在继承过程中,既可 ...

  3. JS高阶---继承模式(原型链继承)

    [前言] 之前已经介绍了对象创建的五种模式,下面看下继承模式 本节介绍下<原型链继承> [主体] 验证如下: 关键点: .

  4. 第20篇 js高级知识---深入原型链

    前面把js作用域和词法分析都说了下,今天把原型链说下,写这个文章费了点时间,因为这个东西有点抽象,想用语言表达出来不是很容易,我想写的文章不是简单的是官方的API的copy,而是对自己的知识探索和总结 ...

  5. js继承中,原型属性的继承探究

    最近研究了js的继承,看了幻天芒的文章http://www.cnblogs.com/humin/p/4556820.html#3947420,明白了最好是使用apply或call方法来实现继承. 已知 ...

  6. 面试题常考&必考之--js中的难点!!!原型链,原型(__proto__),原型对象(prototype)结合例子更易懂

    1>首先,我们先将函数对象认识清楚: 补充snow的另一种写法: var snow =function(){}; 2>其次:就是原型对象 每当我们定义一个函数对象的时候,这个对象中就会包含 ...

  7. JS prototype chaining(原型链)整理中······

    初学原型链整理 构造器(constructor).原型(prototype).实例(instance); 每一个构造器都有一个prototype对象,这个prototype对象有一个指针指向该构造器: ...

  8. 原来JS是这样的 - 原型链

    上一篇提到属性描述符 [[Get]] 和 [[Put]] 以及提到了访问描述符 [[Prototype]],看它们的特性就会很容易的让人想到经典的面向对象风格体系中对类操作要做的事情,但带一些 int ...

  9. 关于JS面向对象中原型和原型链以及他们之间的关系及this的详解

    一:原型和原型对象: 1.函数的原型prototype:函数才有prototype,prototype是一个对象,指向了当前构造函数的引用地址. 2.函数的原型对象__proto__:所有对象都有__ ...

  10. JS高级. 03 混入式继承/原型继承/经典继承、拓展内置对象、原型链、创建函数的方式、arguments、eval、静态成员、实例成员、instanceof/是否在同一个原型链

    继承:当前对象没有的属性和方法,别人有,拿来给自己用,就是继承 1 混入式继承 var I={ }; var obj = { name: 'jack', age:18, sayGoodbye : fu ...

随机推荐

  1. Dijkstra 优先队列优化

    #include <iostream> #include <queue> #include <vector> using namespace std; ; stru ...

  2. python学习笔记-(十三)线程、进程、多线程&多进程

    为了方便大家理解下面的知识,可以先看一篇文章:http://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html 线程 1.什么是线程? ...

  3. gluOrtho2D与glViewport

    https://blog.csdn.net/HouraisanF/article/details/83444183 窗口与显示主要与三个量有关:世界坐标,窗口大小和视口大小.围绕这些量共有4个函数: ...

  4. 第七章 路由 82 名称案例-使用keyup事件实现

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8&quo ...

  5. Docuemnt 的 NamespaceURI为空问题

    创建doc的方式不同,需要增加 DocumentBuilderFactory.setNamespaceAware(true); 这样Element Node.getNamespaceURI 才不为空 ...

  6. Vue双向绑定的实现原理系列(四):补充指令解析器compile

    补充指令解析器compile github源码 补充下HTML节点类型的知识: 元素节点 Node.ELEMENT_NODE(1) 属性节点 Node.ATTRIBUTE_NODE(2) 文本节点 N ...

  7. veeValidate实战

    说在前面 vee-validate 版本2.0.4的学习github地址我的项目地址第一次认真的在git上写一个demo教程,喜欢的可以star一下~^o^~ (^-^) (^o^) 后续会有一个完整 ...

  8. Java-五种线程池,四种拒绝策略,三种阻塞队列(转)

    Java-五种线程池,四种拒绝策略,三种阻塞队列 三种阻塞队列:    BlockingQueue<Runnable> workQueue = null;    workQueue = n ...

  9. lombok使用及常用注解

    简介 大部分项目中都必不可少的包含数据库实体(Entity).数据载体(dto,dataObject),而这两部分都包含着大量的没有业务逻辑的setter.getter.空参构造,同时我们一般要复写类 ...

  10. gtid同步异常处理

    gtid同步异常处理 分析出现问题时候GTID值 通过分析法获取gtid值 通过查看mysql> show slave status \G;查看一下信息并记录下来:Retrieved_Gtid_ ...