• JavaScript面向对象轻松入门之继承(demo by ES5、ES6)


      继承是面向对象很重要的一个概念,分为接口继承和实现继承,接口继承即为继承某个对象的方法,实现继承即为继承某个对象的属性。JavvaScript通过原型链来实现接口继承、call()apply()来实现实现继承。

      接口继承的实现在ES5中是比较麻烦,在其它OOP语言中一个extends关键字就可以实现,但在ES5中要通过原型链去模拟,非常难理解,对初学者很不友好,并且有好几种接口继承的方式。本文为了对初学者更友好,并不打算让读者了解接口继承的原理,而是直接把接口继承实现方法封装成一个函数,大家只要把这个函数拿过去用就可以了。

      相关概念:父类(超类)即为被继承者,子类(派生类)即继承者

    接口继承函数extend():

     1 function extend(subClass, superClass) {
     2     function o() {
     3         this.constructor = subClass;
     4     }
     5     o.prototype = superClass.prototype;
     6     subClass.prototype = new o();
     7     return subClass.prototype;
     8 }
     9 /*
    10 subClass是子类,superClass是父类,extend函数让subClass继承superClass的原型方法,并返回subClass的原型;
    11 这种继承方式也是用的最多的,并且ES6的extends也是通过这种方式实现的,可以说是比较权威的用法;
    12 */

    ES5继承DEMO:

     1 function Animal(shoutVoice,speed){
     2     this._shoutVoice = shoutVoice;//string
     3     this._speed = speed;//string
     4 }
     5 Animal.prototype.getSpeed = function(){
     6     return this._speed;
     7 };
     8 Animal.prototype.shout = function(){
     9     console.log(this._shoutVoice);
    10 };
    11 Animal.prototype.run = function(){
    12     console.log('嘿嘿,吃我灰吧!我的速度可是有'+this._speed);
    13 };
    14 
    15 function Dog(){
    16     Animal.call(this,'汪汪汪!','10m/s');
    17     //实现继承:调用Animal的构造函数,继承Animal类的属性,第一个参数必须是this;
    18 }
    19 //接口继承:extends函数让Dog类继承Animal类的的原型方法并返回Dog的新的原型prototype;
    20 var DogP = extend(Dog,Animal);
    21 /*可以继续给的Dog类的prototype添加方法*/
    22 DogP.gnawBone = function() {
    23     console.log('这是本狗最幸福的时候');
    24 }
    25 /*也可以把父类的方法覆盖掉*/
    26 DogP.run = function(){
    27     console.log('这是Dog类上的run方法,不是Animal类的');
    28     /*虽然覆盖掉了,但实际上Animal类的run方法还在,也可以通过这种方式访问父类的方法,
    29     对原理有兴趣的同学可以了解一下原型链*/
    30     Animal.prototype.run.call(this);
    31 }
    32 var dog = new Dog();
    33 console.log(dog.getSpeed());//log: '10m/s'
    34 dog.shout();//log: '汪汪汪!'
    35 dog.run();
    36 /*log:
    37 '这是Dog类上的run方法,不是Animal类的'
    38 '嘿嘿,吃我灰吧!我的速度可是有10m/s'
    39 */
    40 dog.gnawBone();//log: '这是本狗最幸福的时候'42 /*其它类继承Animal类*/
    43 function Snake(){
    44     Animal.call(this,'嘶!嘶!嘶!','5m/s');
    45 }
    46 var SnakeP = extend(Snake,Animal);
    47 /*Dog类也可以继续被继承*/
    48 function PoodleDog(){
    49     Dog.call(this);
    50     this._breed = 'poodle';
    51 }
    52 var PoodleDogP = extend(PoodleDog,Dog);
    53 /*理论上讲可以无限继承下去,如浏览器DOM对象就继承了很多个对象,组成了一个长长的原型链
    54 如一个div标签对象的类继承顺序:
    55 HTMLDivElement<HTMLElement<Element<Node<EventTarget<Object
    56 但我们的项目中最好别超过3次,否则就不太好控制了;*/

    注意事项:

      *继承的次数不应过多,否则子类一不小心就把父类的属性方法给覆盖了;
      *我们可以把继承的对象作为成员属性,即组合,尽量少用继承,多用组合;
      *父类的属性和方法最好别太多,过多也容易被子类覆盖,可以抽象成一个对象来管理过多的属性和方法。
      *继承增加了耦合,所以父类封装性一定要好,尽量降低与子类的耦合,
      *父类的设计要有前瞻性,具备一定的扩展能力,你也不希望今后修改父类的时候,再去修改所有的子类吧?

      *父类尽量只定义方法,不定义属性,即构造函数最好是空函数;

    ES6继承DEMO:

      ES6实现继承就方便很多了,由于TypeScript实现继承和ES6差不多,所以这章就不贴出TypeScript的Demo了

     1 class Animal{
     2     constructor(shoutVoice,speed){
     3         this._shoutVoice = shoutVoice;//string
     4         this._speed = speed;//string
     5     }
     6     get speed(){
     7         return this._speed;
     8     }
     9     shout(){
    10         console.log(this._shoutVoice);
    11     }
    12     run(){
    13         console.log('嘿嘿,吃我灰吧!我的速度可是有'+this._speed);
    14     }
    15 }
    16 class Dog extends Animal{
    17     constructor(){
    18         super('汪汪汪!','10m/s');//相当于Animal.call(this,'汪汪汪!','10m/s');
    19     }
    20     gnawBone() {
    21         console.log('这是本狗最幸福的时候');
    22     }
    23     run(){
    24         console.log('这是Dog类上的run方法,不是Animal类的');
    25         super.run();//相当于Animal.prototype.run.call(this);
    26     }
    27 }
    28 class PoodleDog extends Dog{
    29     constructor(){
    30         super();
    31         this._breed = 'poodle';
    32     }
    33     get breed(){
    34         return this._breed;
    35     }
    36 }
    37 let poodleDog = new PoodleDog();
    38 console.log(poodleDog.breed);//log: 'poodle'
    39 console.log(poodleDog.speed);//log: '10m/s'
    40 poodleDog.shout();//log: '汪汪汪!'
    41 poodleDog.run();
    42 /*log:
    43 '这是Dog类上的run方法,不是Animal类的'
    44 '嘿嘿,吃我灰吧!我的速度可是有10m/s'
    45 */
    46 poodleDog.gnawBone();//log: '这是本狗最幸福的时候'

    后话

      js的继承与其它OOP语言有一些不同的地方,所以最终还是要深刻的理解原型、原型链才能灵活运用,希望大家有时间一定要把这部分知识补上;

      如果你喜欢作者的文章,记得收藏,你的点赞是对作者最大的鼓励;

      作者会尽量每周更新一章,下一章是讲多态;

      大家有什么疑问可以留言或私信作者,作者尽量第一时间回复大家;

      如果老司机们觉得那里可以有不恰当的,或可以表达的更好的,欢迎指出来,我会尽快修正、完善。

  • 相关阅读:
    About IL, ILGenerator
    何谓系统架构师(转)
    C# weakreference
    新蛋,您能告诉我,这是怎么了吗?
    C#线程同步的常用关键字简介
    [转]百万级访问网站前期的技术准备
    Gridview控件的使用要点
    Asp.net三种页面传值方法
    浅谈 Vue css scoped & module
    Mac iTerm2 zsh: command not found: node/npm/nvm...
  • 原文地址:https://www.cnblogs.com/elonhu/p/7077372.html
Copyright © 2020-2023  润新知