继承是所有面向对象的语言最让人津津乐道的概念

许多面向对象的语言都支持两种实现继承的方式:

1、接口继承

2、实现继承

由于ECMAScript中没有函数签名,所以自然也是不支持接口继承

所以JS中能实现的也只能是实现继承,而实现继承主要是依靠原型链

至于原型链的构成在昨天的文章中也大概讲了一下

无非就是每个对象的实例都有一个[[Prototype]]的属性(在游览器中以__proto__来显式地支持此属性)指向了其构造函数的原型对象

每个对象都有一个这样的引用,就构成了原型链

原型链的顶端是Object.prototype

而实现继承很大程度上都需要依靠,原型对象,除了少数情况之外

原型链的基本实现

  1. function SuperType(){
  2. this.property = true;
  3. }
  4.  
  5. SuperType.prototype.getSuperValue = function(){
  6. return this.property;
  7. };
  8.  
  9. // 创建一个新的构造函数
  10. function SubType(){
  11. this.subprototype = false;
  12. }
  13.  
  14. // 重写这个构造函数的原型对象让其指向SuperType的实例
  15. SubType.prototype = new SuperType();
  16.  
  17. // 添加这个新构造函数的自身的方法
  18. SubtType.prototype.getSubValue = funtion(){
  19. return this.subproperty;
  20. }
  21.  
  22. var instance = new SubType();
  23. alert(instance.getSuperValue()); // true 可以访问到原型对象上的方法和属性

关于上述原型链的实现其实并不完整

因为昨天我们已经说过,重写原型对象的方法会存在一些问题

上方代码中 SubType.prototype.constructor 并不会正确地指向 SubType 这一构造函数

而是会指向 SuperType, 因为 SuperType 的实例并没有 constructor 属性,所以JS会访问SuperType实例所指的原型对象的 constructor 属性也就是 SuperType这一构造函数

PS. constructor属性会在构造函数创建原型对象时,让这个对象指向构造函数,但当我们重写构造函数的prototype属性时(也就是让prototype指向另一个对象),JS并不会自动帮我们完成这一过程

确定原型和实例的关系

一般来说有两种方法

1、instanceof

2、isPrototypeOf()

两种方式从原理上来说并没有什么太大的区别

因为只要是在实例的原型链上出现过的原型对象都会被判为 true

所以当我们需要具体的判断某个实例是否是由某个特定的构造函数构造的时候,就不能使用上述的方法

但我们可以借助原型链的特点来判断

  1. 实例对象.__proto__.constructor === 要判断的构造函数

原型链继承

在这么长的铺垫下终于进入主题了

第一种实现继承的方式,在刚在的展示中基本上已经展示了

还是在给个完整的原型链继承的代码吧

  1. function SuperType(){
  2. this.property = true;
  3. }
  4.  
  5. SuperType.prototype.getSuperValue = function(){
  6. return this.property;
  7. };
  8.  
  9. // 创建一个新的构造函数
  10. function SubType(){
  11. this.subprototype = false;
  12. }
  13.  
  14. // 重写这个构造函数的原型对象让其指向SuperType的实例
  15. SubType.prototype = new SuperType();
  16. // 修改 constructor 让其指向正确地构造函数
  17. SubType.prototype.constructor = SubType;
  18.  
  19. // 添加这个新构造函数的自身的方法
  20. SubtType.prototype.getSubValue = funtion(){
  21. return this.subproperty;
  22. }
  23.  
  24. var instance = new SubType();
  25. alert(instance.getSuperValue()); // true 可以访问到原型对象上的方法和属性

PS. 在实现继承的过程中有几个地方需要注意

1. 给原型对象添加方法的语句一定要在替换原型之后

2.一旦替换了原型就要小心不要在定义原型上的新方法时错误地替换了原型

这种继承方式存在以下问题

1、对于引用类型的值会被所有实例所共享

2、没有办法在不影响所有对象实例的情况下,向父类的构造函数传递参数

所以在实践当中我们很少对单独使用这种方式来实现继承

经典继承(借用构造函数)

这种继承方式的核心思想就是在子类的构造函数中调用父类的构造函数

这样就可以获得父类的所有属性

  1. function SuperType(){
  2. this.color = 'color';
  3. }
  4.  
  5. function SubType (){
  6. SuperType.applay(this,arguments);
  7. this.name = 'lhy';
  8. }

通过改变this指向来让父类构造函数中创建的属性创建到子类构建的对象上

和构造函数模式样

这样的方法没法解决函数复用的问题,也没法继承到父类的原型上的属性

但是这种方法可以在创建子类时给父类的构造函数传递参数并且不会影响其实例对象

虽然这种方法有独特的优势,但是它的问题也不少

所以我们在实践中也很少单独使用这种继承方式

组合继承

有小伙伴可能发现之前的两种方式在某种程度上是互补的

所以第三种继承的实现方式当然就是由前面两种方式组合而来的组合继承

  1. function SuperType(){
  2. this.property = true;
  3. }
  4.  
  5. SuperType.prototype.getSuperValue = function(){
  6. return this.property;
  7. };
  8.  
  9. // 创建一个新的构造函数
  10. function SubType(){
  11. // 调用父类的构造函数
  12. SuperType.applay(this,arguments);
  13. this.subprototype = false;
  14. }
  15.  
  16. // 重写这个构造函数的原型对象让其指向SuperType的实例
  17. SubType.prototype = new SuperType();
  18. // 修改 constructor 让其指向正确地构造函数
  19. SubType.prototype.constructor = SubType;
  20.  
  21. // 添加这个新构造函数的自身的方法
  22. SubtType.prototype.getSubValue = funtion(){
  23. return this.subproperty;
  24. }
  25.  
  26. var instance = new SubType();
  27. alert(instance.getSuperValue()); // true 可以访问到原型对象上的方法和属性

这种组合方式也是在实践中我们最常用的方法(ES5)

融合了前两种方式优点,除此而外 instanceof 和 isPrototypeOf() 也能正确识别

原型式继承

有些时候我们要实现继承,很可能只是希望新的这个类型在已有类型的基础上多出一些属性

没有必要大费周章地创建新的原型函数,那么原型式继承给了我们新的选择

  1. function object(o){// o是参照的对象
  2. function F(){};
  3. F.prototype = o;
  4. return new F();
  5. }

这个简短的函数就是原型式继承的核心思想

虽然都是将新对象以旧对象为原型创建,但是和原型链继承的方式的区别也很显著

那就是这种继承方式不需要构造函数,这也是它存在的意义

为了规范这种继承,ES5规范了Object.create() 方法

传入两个参数,1、原型对象 2、为新对象定义额外属性的对象(可选)

所以我们可以直接使用create 方法

但是这种方式存在的问题也很明显,那就是引用类型的值在实例中会共享(在以同一个对象为参照对象的情况下)

寄生式继承

这种继承方式是原型式继承的进阶版

其思路与寄生构造函数类似

  1. function object(o){// o是参照的对象
  2. function F(){};
  3. F.prototype = o;
  4. return new F();
  5. }
  6.  
  7. function createAnother(o){
  8. var clone = object(o);
  9. clone.say = function(){
  10. alert('lhy');
  11. }
  12. return clone;
  13. }

这种方法相当于,在原型式继承的基础上把给新对象添加新属性的工作,以工厂模式的思路实现了

当然,这种方式的缺点就是,函数没法复用(构造函数模式的通病)

寄生组合继承

看到这里,大家可能以为组合式继承,就是最好的选择

其实细心的小伙伴可能已经发现了,在我们创建子类实例的时候父类的构造函数调用了两次

我们调用两次的原因无非就是需要一个父类的原型副本而已

所以寄生组合继承的思路就是通过原形式继承来获取父类原型的副本,从而避免调用两次父类的构造函数

  1. function object(o){// o是参照的对象
  2. function F(){};
  3. F.prototype = o;
  4. return new F();
  5. }
  6.  
  7. // 定义继承的方法
  8. function inheritPrototype(SubType,SuperType){
  9. var prototype = object(SuperType);
  10. prototype.constructor = SubType;
  11. SubType.prototype = prototype;
  12. }
  13.  
  14. function SuperType(name){
  15. this.name = name;
  16. this.color = ['blue','red','green'];
  17. }
  18.  
  19. function SubType(name,age){
  20. SuperType.call(this,name);// 调用父类的构造函数
  21. this.age = age;
  22. }
  23.  
  24. inheritPrototype(SubType,SuperType);
  25. SubType.prototype.sayAge = function(){
  26. alert(this.age);
  27. }

这里为了大家更好理解,所以没有用 Object.create()

在实践中大家最好用 Object.create() 代替上面我定义的 object 函数

以上就是JS对象继承的主要内容了

Javascript高级编程学习笔记(22)—— 对象继承的更多相关文章

  1. Javascript高级编程学习笔记(21)—— 对象原型

    JS中对象相关的最重要的恐怕就是原型链了 原型链也是JS中对象继承的实现的基础 接昨天的文章,我们使用构造函数创建对象的时候仍然存在一些问题 那就是所有的实例没法共用一个函数 这样无疑会造成极大的内存 ...

  2. Javascript高级编程学习笔记(20)—— 创建对象

    由于今天有点事,加上对象原型链的东西有点多,所以今天这篇就讲一个小的知识点吧 也算为明天的对象继承做铺垫 工厂模式 虽然使用对象字面量来创建一个对象十分地便捷,但是这个方法有一个显著的缺点 那就是如果 ...

  3. Javascript高级编程学习笔记(17)—— 引用类型(6)基本包装类

    基本包装类 基本包装类这个概念或许有的小伙伴没有听说过 但是小伙伴们有没有想过,为什么基本数据类型的实例也有方法呢? 其实这些方法都来自基本包装类型 这是JS为了方便操作基础数据类型而创建的特殊引用类 ...

  4. Javascript高级编程学习笔记(18)—— 引用类型(7)单体内置对象

    什么是内置对象呢? js高级程序设计中给出的定义为:由ES规定不依赖于宿主环境的对象,这些对象在JS执行前就已经存在 前面我们介绍的引用类型都是内置对象 除了这些对象外ECMA还规定了两个单体内置对象 ...

  5. Javascript高级编程学习笔记(59)—— 事件(3)事件对象

    事件对象 在触发DOM‘事件时,会产生一个事件对象 event 该对象包含着所有与事件有关的信息 所有浏览器都支持 event 对象但是支持的方式有所不同 DOM事件对象 兼容DOM的浏览器会将eve ...

  6. Javascript高级编程学习笔记(31)—— BOM(5)screen、history对象

    screen对象 screen对象应该是BOM对象中最不常用的对象了 其主要用于提供客户端的显示能力信息 包括浏览器外部显示的信息,和像素的宽高等 这个对象的主要用于检测客户端能力,一般不会影响功能 ...

  7. Javascript高级编程学习笔记(30)—— BOM(4)navigator对象

    window对象作为浏览器的全局对象.location对象保存了页面的url信息 那么navigator对象又有什么作用呢? navigator对象 该对象最早由 Netspace Navigator ...

  8. Javascript高级编程学习笔记(29)—— BOM(3)location对象

    在JS中location是一个神奇的对象 它既是window对象的属性,也是document对象的属性 它的作用主要在于保存当前文档页面的信息,以及将 url 解析为独立的片段 location对象属 ...

  9. Javascript高级编程学习笔记(28)—— BOM(2)window对象2

    今天讲一下window对象和浏览器导航,弹窗等有关的内容 导航和打开窗口 window.open() 用于导航到某个特定 url 该方法接收四个参数 1.url 2.窗口目标(当页面中有多个框架fra ...

随机推荐

  1. js/jquery遇到的坑总结

    1.“removeAttribute is not a function” error message 原因以及解决方法:removeAttribute is a JavaScript DOM fun ...

  2. Numpy 矩阵库(Matrix)

    Numpy 中包含了一个矩阵库 numpy.matlib, 该模块中的函数返回的是一个矩阵, 而不是 ndarray 对象. 一个 m * n de 矩阵是一个 有 m 行(row) n 列(colu ...

  3. [ES]elasticsearch章5 ES的分词(二)

    Elasticsearch 中文搜索时遇到几个问题: 当搜索关键词如:“人民币”时,如果分词将“人民币”分成“人”,“民”,“币”三个单字,那么搜索该关键词会匹配到很多包含该单字的无关内容,但是如果将 ...

  4. 洛谷 P1338 末日的传说

    题目链接:https://www.luogu.org/problemnew/show/P1338 题目描述 只要是参加jsoi活动的同学一定都听说过Hanoi塔的传说:三根柱子上的金片每天被移动一次, ...

  5. BUAAOO第二单元多线程电梯作业总结

    第二单元多线程作业需要保证线程安全

  6. 《修炼之道:.NET开发要点精讲》读书笔记(四)

    委托的作用:1)它允许把方法作为参数,传递给其它的模块:2)它允许我们同时调用多个具有相同签名的方法:3)它允许我们异步调用任何方法. “方法签名”指方法的参数个数.参数类型以及返回值等,具有相同签名 ...

  7. Filezilla server配置FTP服务器中的各种问题与解决方法

    转至;https://www.jb51.net/article/122171.htm 安装文件以及补丁下载 公司很多资料需要通过ftp上传,那么就需要配置一个FTP服务器,找了一台Windows服务器 ...

  8. 第二阶段第四次spring会议

    昨天我将对软件添加了初始页面. 今天我将对软件加上显示时间. try { SkinListBoxItem rt = new SkinListBoxItem(InputForm("请输入&qu ...

  9. spring boot 注解

    一级注解:(写在类名前面的)@RestController: 等价于在函数前面写@ResponseBody ,会直接返回要显示的内容 @ControllerString返回的是模板文件的名称. 二级注 ...

  10. tensorflow学习之(八)使用dropout解决overfitting(过拟合)问题

    #使用dropout解决overfitting(过拟合)问题 #如果有dropout,在feed_dict的参数中一定要加入dropout的值 import tensorflow as tf from ...