一、面向对象编程(继承)

这篇博客是面向对象编程的第三篇,JS继承。继承顾名思义,就是获取父辈的各种"财产"(属性和方法)。

怎么实现继承?

我们的JavaScript比较特别了,主要通过原型链实现继承的。

下面介绍各种实现继承的方式:原型链继承,借用构造函数,组合继承,原型式继承,寄生式继承,寄生组合式继承。

二、实现继承方式

1.原型链方式

原型我们都知道,每个构造函数都有一个原型对象(prototype),用于存放共享的属性方法。

原型链继承原理:我们要继承一个父类,那就把这个子类的原型对象指向父类的实例就行了。

a.代码:

  1. //创建父类构造函数
  2. function Father(){
  3. this.fristName = "Father";
  4. }
  5. //给父类的原型对象添加方法
  6. Father.prototype.getFatherName = function(){
  7. return this.fristName ;
  8. }
  9. //创建子类构造函数
  10. function Son(){
  11. this.name = 'jack';
  12. }
  13. //重点这行:把子类的原型对象指向父类实例实现继承
  14. Son.prototype = new Father();
  15. //我们还可以为子类添加原型方法
  16. Son.prototype.getName = function(){
  17. return this.name;
  18. }
  19. //测试一下,创建一个子类的实例
  20. var s1 = Son();
  21. s1.getFaterName(); //Father 继承了爸爸的名字了!

b.一些特别注意的地方:

1.上面我们看到为子类原型添加方法getName,它在子类原型对象指向父类实例后,也一定要在这句话后面,如果在前面的话,这方法会被覆盖的。因为子类原型对象指向父类实例时相当于重写子类原型对象。

2.为子类原型添加方法式不能用字面量的方式,这样会重写已经继承类父类的原型对象。

c.原型链继承的问题:

很明显的,继承的属性方法都在子类的原型对象里面,那么里面的属性方法都是共享的,这明显不是符合我们平常要求。

2.借用构造函数

借?是的,借一下父类的构造函数,怎么借?那就使用call或者apply方法吧。

借用构造函数原理: 就在子类创建构造函数时内部调用父类的构造函数。

代码演示:

  1. //父类(父类是相对来说的,大家注意一下噢)
  2. function Father(){
  3. this.colors = ['red','green'];
  4. }
  5. //子类
  6. function Son(){
  7. //重点这行:内部调用父类构造函数,this是指向未来创建Son的实例
  8. Father.call(this);
  9. }
  10. //测试一下,继承了父类的属性了,并且属性不会共享噢
  11. var s1 = new Son();
  12. s1.colors.push('blue');
  13. console.log(s1.colors); ['red','green','blue']
  14. //s2实例的colors不会被影响
  15. var s2 = new Son();
  16. console.log(s2.colors); ['red','green']

这里的借用构造函数可以实现属性和方法不会被共享,因为属性在构造函数中,而不是在原型对象中。但是这也说明这种方式没办法共享方法的。

3.组合继承(最常用)

组合继承=原型链继承+借用构造函数继承,没错就结合两种方法就好了。

组合继承原理:实例共享的属性方法:我就原型链继承;实例私用的属性方法:我就借用构造函数(结合两者的优点)

代码演示:

  1. //父类
  2. function Father(name){
  3. this.name = name;
  4. this.colors = ['red','green'];
  5. }
  6. //父类原型对象
  7. Father.pototype.sayName = function(){
  8. console.log(this.name);
  9. }
  10. //子类
  11. function Son(name,age){
  12. //借用父类构造函数
  13. Father.call(this,name);
  14. this.age = age;
  15. }
  16. //子类原型指向父类实例,原型链继承
  17. Son.prototype = new Father();
  18. Son.prototype.constructor = Son; //让子类的原型只向子类构造函数
  19. Son.prototype.sayAge = function(){
  20. console.log(this.age);
  21. }
  22. //创建实例对象
  23. var s1 = new Son('Mc ry',22);
  24. var s2 = new Son('yy',20);
  25. //继承了父类方法,sayName是共享的
  26. s1.sayName(); //mc ry
  27. s2.sayName(); //yy
  28. //继承colors,每个实例私有
  29. s1.colors.push('blue');
  30. console.log(s1.colors); ['red','green','blue']
  31. console.log(s2.colors); ['red','green']

可能有的疑惑:在原型链继承那里,子类原型对象指向类父类的实例,应该继承了所有的属性和方法啊,那应该都是共享的,但为什么colors,name属性不会共享呢?

原因:在调用借用构造函数时,属性在子类新实例中创建了,也就是在子类实例中已经有的父类属性就不用继续到原型对象中查找了,也就是屏蔽了,所以不会共享了。

4.原型式继承

这是道格拉斯·克罗克福提出的一个方式,他提出一个函数object() 来简单实现不用构造函数的继承

代码演示:

  1. //object函数,传入一个对象
  2. function object(o){
  3. //创建一个空的构造函数
  4. function F(){};
  5. //这个空构造函数的原型对象指向o这个对象
  6. F.prototype = o ;
  7. //返回F的实例对象
  8. return new F();
  9. }

认真读这段代码可以知道,这个函数最终返回一个对象,这个对象拥有传入对象o的全部属性和方法。从而实现了继承。

  1. //一个对象
  2. var person = {
  3. name : 'ry',
  4. sayName : function(){
  5. console.log(this.name);
  6. }
  7. }
  8. //children对象继承了person对象所有的属性方法
  9. var children = object(person);

原型式继承方式的出现,可以不用创建构造函数都能继承另一个对象的属性方法。但是很明显,所用属性都是共享的。

ES5中有个object.create()方法的作用和上面的object()一样的。

5.寄生式的继承

寄生式其实和利用了原型式,都要使用到object函数,但不同的是寄生式中新的对象能够添加自己的方法。

  1. function creatAnother(o){
  2. //调用object继承o
  3. var obj = object(o);
  4. //还可以添加自己想要的方法
  5. obj.sayHi = function (){
  6. console.log('hi');
  7. }
  8. //返回这个对象
  9. retrun obj;
  10. }
  11. //新的对象继承了person
  12. var anotherPerson = creatAnother(person);
  13. //继承后能使用person的方法
  14. anotherPerson.sayHi(); //"hi"

6.寄生组合式继承(最理想)

上面组合式方式中虽然是最常用的,但有追求的还是会继续优化它。因为组合方式中也有不够好的地方:

一方面:我们可以看到调用了两次父类的构造函数。(一次是原型链继承中子类原型对象指向父类实例时,一次是借用构造函数中)

另一方面:就是上面疑惑,子类的原型中拥有父类已经有全部属性,但我们又要在调用子类构造函数时重写部分属性。

所以寄生组合方式就解决了上面,通过一个函数来代替组合方式中的原型链继承。

代码演示:

  1. //主要是这个函数,实现子类原型对象只继承父类原型对象中的属性
  2. function inheritPrototype(subType,superType){
  3. //利用上面的Object函数,将父类的原型赋予prototype变量
  4. var prototype = object(superType.prototype);
  5. //将prototype的构造函数重新指向子类
  6. prototype.constructor = subType;
  7. //将prototype给sub的原型对象
  8. subType.prototype = prototype;
  9. }
  10. //将前面的组合继承改写
  11. //父类
  12. function Father(name){
  13. this.name = name;
  14. this.colors = ['red','green'];
  15. }
  16. //父类原型对象
  17. Father.pototype.sayName = function(){
  18. console.log(this.name);
  19. }
  20. //子类
  21. function Son(name,age){
  22. //借用父类构造函数
  23. Father.call(this,name);
  24. this.age = age;
  25. }
  26. //(这行用inheritPrototype函数替换)子类原型指向父类实例,原型链继承
  27. inheritPrototype(Son, Father);
  28. Son.prototype.sayAge = function(){
  29. console.log(this.age);
  30. }

寄生组合式的继承方式是最理想的方式,它使得子类构造函数继承父类构造函数的属性,子类的原型对象继承父类原型对象的属性和方法。

三、小结

1.这次博客讲述了在js中是如何实现继承的,有很多中方式,但主要的是组合方式寄生组合方式。继承后我们能够使用父类的属性和方法,增加了代码的重用性。

2.了解js继承作用:有助于我们去阅读一些框架的源码,可能本次代码有点难以理解,我打上了大量的注释,供大家一起阅读,还是那句话,多看几遍,其义自见。如果觉得有收获,就点个赞吧,关注我吧。

  • 细心的朋友可能发现,我把文章的样式修改了,类似Markdown风格咯,看起来比较清爽,舒服。

同系列几篇:

第一篇:JavaScript--我发现,原来你是这样的JS(一)(初识)

JS--我发现,原来你是这样的JS:面向对象编程OOP[1]--(理解对象和对象属性类型)

JS--我发现,原来你是这样的JS:面向对象编程OOP[2]--(创建你的那个对象吧)

我发现,原来你是这样的JS全部文章汇总(点击此处)

本文出自博客园:http://www.cnblogs.com/Ry-yuan/

作者:Ry(渊源远愿)

欢迎转载,转载请标明出处,保留该字段。

JS--我发现,原来你是这样的JS:面向对象编程OOP[3]--(JS继承)的更多相关文章

  1. JavaScript--我发现,原来你是这样的JS:面向对象编程OOP[2]--(创建你的那个对象吧)

    一.介绍 我们继续面向对象吧,这次是面向对象编程的第二篇,主要是讲创建对象的模式,希望大家能从博客中学到东西. 时间过得很快,还是不断的学习吧,为了自己的目标. 二.创建对象 1.前面的创建对象方式 ...

  2. JS--我发现,原来你是这样的JS:面向对象编程OOP[2]--(创建你的那个对象吧)

    一.介绍 我们继续面向对象吧,这次是面向对象编程的第二篇,主要是讲创建对象的模式,希望大家能从博客中学到东西. 时间过得很快,还是不断的学习吧,为了自己的目标. 二.创建对象 1.前面的创建对象方式 ...

  3. 如何把js的代码写的更加容易维护(一)--面向对象编程

    总是头疼javascript的代码写起来不可维护,那么看看下面的代码: (function (w, $) { var app = { init: function () { var me = this ...

  4. JavaScript--我发现,原来你是这样的JS:面向对象编程OOP[1]--(理解对象和对象属性类型)

    一.介绍 老铁们,这次是JS的面向对象的编程OOP(虽然我没有对象,心累啊,但是可以自己创建啊,哈哈). JS高程里第六章的内容,这章内容在我看来是JS中很难理解的一部分.所以分成三篇博客来逐个理清. ...

  5. JS--我发现,原来你是这样的JS:面向对象编程OOP[1]--(理解对象和对象属性类型)

    一.介绍 老铁们,这次是JS的面向对象的编程OOP(虽然我没有对象,心累啊,但是可以自己创建啊,哈哈). JS高程里第六章的内容,这章内容在我看来是JS中很难理解的一部分.所以分成三篇博客来逐个理清. ...

  6. JS 学习笔记 (七) 面向对象编程OOP

    1.前言 创建对象有很多种方法,最常见的是字面量创建和new Object()创建.但是在需要创建多个相同结构的对象时,这两种方法就不太方便了. 如:创建多个学生信息的对象 let tom = { n ...

  7. JS面向对象编程(进阶理解)

    JS 面向对象编程 如何创建JS对象 JSON语法声明对象(直接量声明对象) var obj = {}; 使用 Object 创建对象 var obj = new Object(); JS对象可以后期 ...

  8. JS - ES5与ES6面向对象编程

    1.面向对象 1.1 两大编程思想 1.2 面向过程编程 POP(Process-oriented programming) 1.3 面向对象编程 OOP (Object Oriented Progr ...

  9. js面向对象编程:if中可以使用那些作为判断条件呢?

    作者来源http://www.2cto.com/kf/201407/314978.html搬运 在所有编程语言中if是最长用的判断之一,但在js中到底哪些东西可以在if中式作为判断表达式呢? 例如如何 ...

随机推荐

  1. EasyUI Tree 树 ——实现多级别菜单的展示,以及与后台数据的交互

    一 要引入的js css库 <link type="text/css" href="css/base.css" rel="stylesheet& ...

  2. 【特效】体验很好的导航hover效果移出恢复当前位置

    很常见的一种导航的hover效果,鼠标放上后除了正常的hover,在移出整个导航后,会恢复当前栏目的特殊样式,分别有横向和纵向的导航.代码也比较简单,设置一个当前栏目的class,用index()找到 ...

  3. win10 uwp 获取按钮鼠标左键按下

    我们可以使用PointerPressed获得鼠标右键按下,但是我们如何获得左键? 其实UWP已经没有MouseLeftButtonDown,于是我们可以使用一个简单方法去获取鼠标左键按下. 我们在xa ...

  4. HTML之事件处理程序

    HTML事件 <body> <input type="button" value="按钮1" id="but1" oncl ...

  5. 【转】Linux设备驱动--块设备(一)之概念和框架

    原文地址:Linux设备驱动--块设备(一)之概念和框架 基本概念   块设备(blockdevice) --- 是一种具有一定结构的随机存取设备,对这种设备的读写是按块进行的,他使用缓冲区来存放暂时 ...

  6. 【转】C语言产生随机数

    原文地址:http://www.cnblogs.com/xianghang123/archive/2011/08/24/2152404.html 在C语言中,rand()函数可以用来产生随机数,但是这 ...

  7. (转)java内存泄漏的定位与分析

    转自:http://blog.csdn.net/x_i_y_u_e/article/details/51137492 1.为什么会发生内存泄漏 java 如何检测内在泄漏呢?我们需要一些工具进行检测, ...

  8. Python 3 使用venv创建虚拟环境

    Python 3.3以上使用venv来代替了原来Python2使用的virtualenv创建虚拟环境. 虚拟环境的作用是使得不同项目的Python包之间不会相互干扰,避免了由此产生的各种问题. 现在演 ...

  9. mybatis mysql 批量插入

    场景描述: 使用mybatis操作mysql数据库,进行批量插入数据,提高代码质量和执行效率. 环境: mybatis spring mysql java xml配置文件 <insert id ...

  10. Java基础总结--数组

    ---数组的定义---组织存储一组数据1.存放相同类型数据的集合--就是一种容器本质上变量也是一种容器--区别就是只存储了一个数据的容器--面对容器,而不是分散的数据eg.买一盘鸡蛋--蛋托其实就是容 ...