资讯专栏INFORMATION COLUMN

浅谈JavaScript继承

raoyi / 2450人阅读

摘要:注意这里跟原型链继承有个比较明显的区别是并没有使用继承而是在子类里面执行父类的构造函数相当于把父类的代码复制到子类里面执行一遍这样做的另一个好处就是可以给父类传参。

Javascript继承

学过后端语言的同学对继承并不陌生,但是对JS继承少许还是有些困惑,不要试图问我是如果知道的,其实javascript继承主要是基于原型prototype实现的。

其实当你真正了解了原型链时候,再看js继承,其实比OOP语言更灵活、更简单一些。接下来我们来看看原型链继承吧:

    //父类
    function Animal(){}
    //子类
    function Dog(){}
    //继承
    Dog.prototype = new Animal();

其实,就是把子类的prototype指向父类的实例,继承就完成了,很简单吧。这就是原型链继承
上面只是一个简单的继承结果,并无实际意义,继承的目的就是要共享父类的属性和方法,接下来我们一步一步来揭开这神秘的面纱

    /**
     *
     * 父类,带属性
     * @constructor
     * @param name 名字
     * @param type  动物分类
     * @constructor
     */
    function Animal(name,type) {
      this.name = name || "your name";
      this.type= type || 0;
      this.coatColor= ["white","block","yellow","brown"]; //引用类型
      //函数也是引用类型
      this.speak = function () {
          console.log(this.name+" speaking .");
      }
}
    }
    
    /**
     * 为父类新增一个方法
     * @returns {boolean}
     */
    Animal.prototype.say= function () {
         console.log("my name  is "+this.name);
    };
    
    /**
     * 子类
     * @constructor
     */
    function Dog(name) {
         this.name = name;
         this.foot= 4;
    }
    
    //实现继承-原型链继承 => (子类 -> 子类原型->父类) ;继承 注意,继承必须要写在子类方法定义的前面
    Dog.prototype = new Animal();
    
    /**
     * 子类方法
     *  为子类新增一个方法(在继承之后,否则会被覆盖/异常)  dog.run is not a function
     */
    Dog.prototype.run = function () {
        console.log("The "+ this.name +" was runing.");
    };
    var dog = new Dog("taiSen");
    console.log(dog.name); //dog    --子类覆盖父类的属性
    console.log(dog.type); // 0     --父类的属性
    console.log(dog.foot); //4      --子类自己的属性
    dog.say(); //my name  is taiSen    --继承自父类的方法
    dog.run(); //The taiSen was runing. --子类自己的方法

以上,看起来我们好像已经完成了一个完整的继承了。但是,原型链继承有一个缺点,就是属性如果是引用类型的话,会共享引用类型 ,接下来我个Animal增加引用类型属性this.coatColor,测试下

    //测试下
    var dog1= new Dog();
    var dog2 = new Dog();
    dog1.coatColor.push("blue");
    console.log(dog1.coatColor); // [ "white", "block", "yellow", "brown", "blue" ]
    console.log(dog2.coatColor); // [ "white", "block", "yellow", "brown", "blue" ]
dog1,dog2 输出的coatColor一样,说明引用类型属性会被所有实例共享--- 这就是原型链继承的缺点,那么我们如果解决这个问题呢? 接下来我们就要借用————构造函数继承
    //子类
    function Cat() {
        Animal.call(this)  // 构造函数继承(继承属性)
    }
    
    //测试下
    var cat1= new Cat();
    var cat2 = new Cat();
    cat1.coatColor.push("red");
    console.log(cat1.coatColor); // [ "white", "block", "yellow", "brown", "red" ]
    console.log(cat2.coatColor); // [ "white", "block", "yellow", "brown"]

从结果看,我们就解决了引用类型被所有实例共享的问题了。

注意:这里跟原型链继承有个比较明显的区别是并没有使用prototype继承,而是在子类里面执行父类的构造函数, 相当于把父类的代码复制到子类里面执行一遍,这样做的另一个好处就是可以给父类传参。

测试代码:

    /比如:
    function Pig(name) {
        Animal.call(this,name);
    }
    var pig1= new Animal("big Pig");
    var pig2 = new Animal("small Pig");
    console.log(pig1.name); // big Pig
    console.log(pig2.name); //small Pig

看起来是不是很像java,C#语言啊,以上构造函数解决了引用类型被所有实例共享的问题。

注意: 正因为构造函数解决了解决了引用类型被所有实例共享的问题,导致了一个相对很矛盾的问题出现了,——————函数也是引用类型,函数也没办法共享了.也就是说,每个实例里面的函数,虽然功能一样,但是却不是同一个函数,就相当于我们每实例化一个子类,就复制了一遍的函数代码。
//父类新增this.speak函数
function Animal(name,type) {
  this.name = name || "your name";
  this.type= type || 0;
  this.coatColor= ["white","block","yellow","brown"]; //引用类型
    //函数也是引用类型
  this.speak = function () {
      console.log(this.name+" speaking .");
  }
}

//测试
console.log(pig1.speak===pig2.speak); // false 

以上证明,父类的函数,在子类的实例下是不共享的。

怎么办呢?,以上可以看出原型链继承构造函数继承 这两种继承方式的优缺点刚好是互相矛盾的,那么我们有没有办法鱼和熊掌兼得呢? 答案是肯定的————组合继承

    // 父类
    function Animal() {
        this.name = name || "your name";
        this.type= type || 0;
        this.coatColor= ["white","block","yellow","brown"]; //引用类型
    }
    
    // 父类函数
    Animal.prototype.speak =function () {
          console.log(this.name+" speaking .");
      }
    
    // 子类
    function Chicken(){
        Animal.call(this)             // 构造函数继承(继承属性)
    }
    // 继承
    Chicken.prototype = new Animal()  // 原型链继承(继承方法)
总结
继承方式 核心代码 优缺点 用法
原型链继承 Dog.prototype = new Animal() 实例的引用类型共享 继承属性
构造函数继承 在子类Cat)里执行 Animal.call(this) 会独享所有属性,包括引用属性(重点是函数) 继承方法
组合继承 利用原型链继承要共享的属性,利用构造函数继承要独享的属性 实现相对完美的继承 结合上两位

本文中的代码见demo coding ,如果觉得对您有用,帮加个star,万分感谢!

今天就写到这,讲述了3种继承方式,其实J继承还有很多继承方式。其他留在下期再见咯。
感觉各位的收看,欢迎提问。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/95135.html

相关文章

  • 浅谈JavaScript继承

    摘要:组合方式实现继承原型链构造函数喵喵喵汪汪汪与的唯一区别是多了这一句组合方式实现了对构造函数内和原型上所有属性和方法的继承,并且的实例对象之间也不会相互干扰。 前言 关于JavaScript继承相关的定义和方法网上已经有很多解释啦,本菜鸟就不抄抄写写惹人嫌了,本文主要探讨三种基本的继承方式并且给出优化方案。 正文 借助构造函数实现继承 function Parent1() { ...

    Jingbin_ 评论0 收藏0
  • 浅谈 OOP JavaScript [完结章] -- 继承

    摘要:构造函数通过原型继承了构造函数和原型,这就形成了一个链条,通俗的讲就是原型链继承。而且方法只能冒充构造函数里面的属性和方法而无法冒充原型对象里面的属性和方法还有最大的问题就是重复使用。 前言: 写到这里,差不多就把OOP完结了,写了几篇OOP的文章,但是只是略懂皮毛,可能深入的OOP还有很多,但是我感觉写到这里也算是差不多完结了。 继承 继承是面向对象比较核心的概念,其他语言可能实现...

    张利勇 评论0 收藏0
  • 浅谈JavaScript中的面向对象

    摘要:面向对象面向对象编程的全称是,简称,面向对象编程是用抽象方式创建基于现实世界模型的一种编程模式。面向对象编程的三个主要特征是封装继承多态。 面向对象 面向对象编程的全称是Object Oriented Programming,简称OOP,面向对象编程是用抽象方式创建基于现实世界模型的一种编程模式。面向对象编程可以看做是使用一系列对象相互协作的软件设计,面向对象程序设计的目的是在编程中促...

    Magicer 评论0 收藏0
  • 浅谈JavaScript原型链

    摘要:在这其中我们就逃不开要讨论继承原型对象构造函数实例了。想要获得某一类型的构造函数可以用来获得,也可以对该属性进行赋值操作。三上面就提到一点是指构造函数的原型对象,它是一个对象,它是构造函数的属性。 原型链这一个话题,需要和很多概念一起讲,才能串成一个比较系统的知识点。在这其中我们就逃不开要讨论继承、原型对象、构造函数、实例了。 一、构造函数 构造函数是一类特殊的函数,它的作用是用来生成...

    xumenger 评论0 收藏0
  • 浅谈面向对象的javascript几个特性

    摘要:中的和是一门很灵活的语言,尤其是。即然是面向对象的编程语言,那也是不可或缺的。在中,永远指向的是他的调用者。定义是存在于实例化后对象的一个属性,并且指向原对象的属性。我们在扩展的时候,同时父类也会有对应的方法,这很显然是一个很严重的问题。 javascript中的this和new javascript是一门很灵活的语言,尤其是function。他即可以以面向过程的方式来用,比如: f...

    JayChen 评论0 收藏0

发表评论

0条评论

raoyi

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<