zoukankan      html  css  js  c++  java
  • javascript的原型与继承(2)

    这是上一篇的后续。

    Javascript是一种基于对象的语言,遇到的所有东西几乎都是对象。如果我们想要把属性和方法封装成一个对象,应该怎么做呢:

     假设我们把猫看成一个对象:

    var Cat = {
    
        name : ‘’,
    
        color : ‘’
    }

    现在,我们需要根据这个原型对象来生成两个具体的实例对象。

    var cat1 = {};//创建一个空对象
    
    cat1.name = “大毛”;
    
    cat1.color = “yellow”;
    
    var cat2 = {};//创建一个空对象
    
    cat2.name = “二毛”;
    
    cat2.color = “white”;

     这样也算是一种封装吧。但这样的写法显然是有些缺点。一个是,如果需要多生成实例,会很麻烦。第二就是,实例和原型之间,看不出有什么联系。

     

    原始模式的改进

    我们可以写一个代码,来改变代码重复的问题。

    function Cat(name,color){
      return {
            name : name,
            color : color
        }
    }        

    然后生成实例对象,就等于在调用函数。

    var cat1 = Cat(“大毛”,’yellow’);
    
    var cat2 = Cat(“二毛”,”white”);

    这种方法减少了代码量,但依然看不出原型和实例的关系。

     

    构造函数模式

    为了解决原型对象生成实例的问题。javascript提供了一个构造函数。

    所谓构造函数,其实是一个普通函数。但是内部使用了this变量。对构造函数使用new运算符,就能生成实例。并且this会绑定到实例对象上。

    function Cat(name,color){
    
      this.name = name;
    
      this.color = color;
    
    }
    
    var cat1 = new Cat(“大毛”,’yellow’);
    
    var cat2 = Cat(“二毛”,”white”);

    构造函数的实例和原型之间如何联系呢,实例会含有一个constructor属性,该属性指向实例的构造函数。

    alert(cat1.constructor == Cat); //true
    
    alert(cat2.constructor == Cat); //true

    构造函数的问题:

    资源浪费,前面已经说过,这里就不叙述了。

    引入prototype属性

    前面已经介绍

     

    构造函数的继承

    比如,现在有一个 动物 对象的构造函数。

    function Animal(){
    
        this.species = “animals”;
    
    }

    还有一个 猫 对象的构造函数

    function Cat(name,color){
    
      this.name = name;
    
      this.color = color;
    
    }

    那怎样使“猫”继承“动物”呢?

     1. 构造函数绑定:

    将父对象的构造函数绑定在子对象上。

    function Cat(name,color){
    
      Animal.apply(this,arguments);
    
      this.name = name;
    
      this.color = color;
    
    }
    
    var cat1 = new Cat(“kitty”,”white”);
    
    console.log(cat1.species); //animals

    原型继承

    这种也是比较常用的方法,采用prototype属性。

    如果“猫”的prototype对象,指向一个Animal的实例。那么所有“猫”的实例,就能继承Animal了。

    //将Cat的prototype对象指向一个Animal的实例。
    
    Cat.prototype = new Animal(); 
    
    Cat.prototype.constructor = Cat;
    
     
    
    var cat1 = new Cat(“大毛”,”white”);
    
    console.log(cat1.species);

    Catprototype对象指向一个Animal的实例,将完全删除prototype对象原先的值,重新赋予了一个新值。

    Cat1constructor本来是应该指向Cat这个构造函数的。但是因为:

    Cat.prototype = new Animal();

    Catconstructor被指向了Animal这个构造函数。这会导致继承链的错乱。因此我们需要手动的进行纠正。

    Cat.prototype.constructor = Cat;

    直接继承prototype

    这种方法是对前面方法的改进。由于在Animal对象中,不变的属性都直接写入了Animalprototype中。因此,也可以让Cat跳过Animal,直接继承Animal.prototype

    Cat.prototype = Animal.Prototype;
    
    Cat.prototype.constructor = Cat;

    与前一种相比,这样可以不用建立Animal实例,相对来说可以节约资源。缺点也很明显:Cat.prototype现在和Animalprototype都指向了同一个对象。那么任何对Cat.prototype的操作,都会影响到Animal.prototype。所以这个时候,Animal.prototypeconstructor也被指向了Cat,这显然不是我们想要的。

    利用空对象作为中介

    var F = function(){}
    
    F.prototype = Animal.prototype;
    
    Cat.prototype = new F();
    
    Cat.prototype.constructor = Cat;

    F是空对象,几乎不占内存。这时,修改Cat.prototype.constructor,也不会影响到Animalprototype对象。可以将上面的方法,封装成一个函数。

     

    function extend(child,parent){
    
      var F = function(){};
    
      F.prototype = parent.prototype;
    
      child.prototype = new F();
    
      child.prototype.constructor = child;
    
    }
    
    extend(Cat,Animal);
    
    var cat1 = new Cat(“大毛”,”white”);

    拷贝继承

    这种逻辑是比较容易理解的,就是将父类的属性一一的拷贝到子类的属性中.

     

    function Animal(){};
    
    Animal.prototype.species = “animal”;
    
    function extend2(child,parent){
    
      var p = parent.prototype;
    
      var c = child.prototype;
    
        for(var k in p){
    
        c[k] = p[k];
    
       }
    }

    然而这种方法的缺点也是很明显的,将父类和子类的prototype指向了同一个存储单元。对于那些引用值类型的属性来说。修改其中的任意一方,都会对另一方的属性造成影响。

     

    非构造函数的继承

    道爷曾经提出过一个object()函数,可以做到这一点。

    function object(o){
    
      function F(){}
    
      F.prototype = o;
    
      return new F();
    
    }

    这种方法 其实和空对象中介方法,是一个道理的。

  • 相关阅读:
    node体验
    JS练习--prototype的一道题目
    JS的OOP--继承之prototype
    JS的OOP--new一个function背后的实际操作
    JS中new运算符的运算顺序
    thymeleaf 拼接字符串与变量
    spring jpa exists
    LocalDateTime json格式化
    格式化java8 LocalDateTime
    springboot定时任务
  • 原文地址:https://www.cnblogs.com/lxin/p/3593809.html
Copyright © 2011-2022 走看看