hackftz

bits change world

导航

Javascript 继承和多态

Posted on 2019-04-13 15:29  hackftz  阅读(149)  评论(0编辑  收藏  举报

  近期通过一些巧合 或者说 思想转变吧 。。。

  想通过blog && 公众号 (个人公众号: KeepinJS)去记录自己的Javascript深度学习的内容,从而达到 进一步的自我提升。

  js面向对象设计 很多都会用到  原型继承和多态 再加以封装 我通过一些大佬视频 和 学习博客 来分享一波自己的见解   如果有描述不当之处 还请及时评论,我看到的话,有时间会给予回复和沟通,谢谢!

 

  基础知识不多赘述,来看以下几种继承方式:

 

原型继承

  

  这里可以通过构造函数创建父类实例,父类实例可以根据不同的参数定义不同的实例属性name,当我们使用原型继承的时候,将子类构造函数原型指向父类实例的时候,再创建子类实例的时候,并不能定义子类的属性,也就是说,虽然父类原型上的方法可以通过这种方式继承下来,但是父类的构造方法(即定义实例属性name)并没有继承下来。

  并且,如果没有在创建父类实例的时候定义父类实例的名字,那么,子类是不知道自己继承的父类实例的名字是什么。

  所以这种继承是最简单,它只是实现了父类定义的属性和方法。

 

子类构造函数内调用父类构造函数

   

 

  通过在子类构造函数内调用父类构造函数,实现this指向的改变,可以实现创建子类实例的时候,它们都会有自己私有的属性值

 ,每个子类实例都是独一无二的。但是这里是不是看出问题了,子类又不能调用父类原型上的方法,只是继承了父类构造函数定义实例属性的方法,因此,这也是不合适的。

 

 

 组合继承

    

  这样,不仅可以继承父类构造函数初始化实例属性的方法,也可以调用父类原型上的方法,一举两得。

  但是,这里会存在一个问题,new Person()的时候调用一次父类构造函数,子类创建实例的时候又调用了一次父类的构造函数,是不是调用了两次,并且这样的继承必须要创建一个父类实例,有没有办法去减少这样多次调用构造函数和强制调用呢??

 

寄生组合式继承

 

  这里解析一波Object.create方法,官方定义如下:

  Object.create(obj)方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。 (请打开浏览器控制台以查看运行结果。)

  

  就是说参数对象obj是新创建对象obj1的构造函数的原型对象。

  这里寄生组合式方法中调用方法使得子类构造函数的原型对象指向了父类构造函数的原型对象。可以理解为其中建立了__proto__这么一层关系。

  

  注意一点,此时子类构造函数的原型对象的constructor属性是指向Parent的。

 

  所以需要将子类构造函数原型对象的constructor重新指向子类构造函数。

 

ES5和ES6继承的区别

  

  这里使用浏览器环境提供的Array方法,在Fun构造函数上使用寄生组合式继承。但是并没有实现原生Array创建数组实例对象的属性和方法。

  这是为什么呢?

  这是因为原生内部构造函数具有内部属性,而通过Array.apply()方法或者分配原型对象都不能在子类实例的创建过程中将this指向Array。

  ES5通过构造函数创建实例的过程,是先调用子类构造函数,然后将父类属性添加到子类实例上。并且,Array对象存在不可枚举属性和不可继承的静态方法。

  ES6是新建父类实例对象调用父类构造函数,再调用子类构造函数修饰this,这个this始终没有改变,并且可以相比于ES5,可以继承父类的所有行为!!

  (这块细节很多,以后会单独研究后做一篇讨论)

 

ES6继承

 

  ES6只有静态方法和静态属性,ES7有静态属性的提案。

  这里只提一点,和Java很像,静态方法在外部通过类名调用,比不过内部是通过this调用,而且静态方法是会被继承的。

  

 

  ES6存在两条继承链:一是子类构造函数的__proto__属性总是指向父类构造函数,二是子类prototype属性也指向父类的prototype属性。

  ES6类的继承使用了Object.setPrototypeOf(Children.prototype, Parent.prototype),具体实现方式百度。

 

多态

  下面说一下多态,从几个方面阐述和分析:

   首先来说多态的定义:在面向对象语言中,接口的多种不同的实现方式即为多态。

   

  一种是子类和父类可以有不同实现的”虚函数“,子类构造函数定义的函数属性或者是原型上的函数属性会覆盖父类构造函数定义的重名函数属性或者是原型上的重名函数,调用的时候根据原型链来调用。

   

  对于比较运算符,如果是一个对象和一个是数字进行比较,则会调用对象的valueOf方法,如果valueOf方法没有指定,则会调用toString方法;如果是一个对象和一个字符串进行比较,会先尝试toString方法,然后是valueOf方法。

  另外相同类的实例对象之间进行比较,会先尝试转化,再比较大小;而对非同类实例对象比较,则会直接返回false。

  注:valueOf方法只能返回数字!