美文网首页
js--创建对象与对象继承

js--创建对象与对象继承

作者: 栗子酥小小 | 来源:发表于2017-03-20 21:00 被阅读0次

    创建对象

    1. 工厂模式:
      function createPerson(name, age, job){
      var o = new Object();
      o.name = name;
      o.age = age;
      o.job = job;
      o.sayName = function(){
      alert(this.name);
      };
      return o;
      }
      var person2 = createPerson("Greg", 27, "Doctor");
    • 解析:虽然能创造对象,但是无法从constructor和instanceOf获取更多关于构造函数的信息,因为这里生成的对象的constructor都指向Object,而instanceOf只有Object为true.
    1. 正常构造函数模式:
      function Person(name, age, job){
      this.name = name;
      this.age = age;
      this.job = job;
      this.sayName = function(){
      alert(this.name);
      };
      }
      var person2 = new Person("Greg", 27, "Doctor");
    • 解析:函数里面没有return语句,必须要用new才能创建对象,如果直接调用Person,则里面的this会指向window,造成的结果是会在window上生成全局变量。
    • 使用new可以生成新对象,具体步骤如下:
      1. 开辟内存,创建新对象
      2. 将构造函数的作用域赋给新对象(因此this指向这个新对象)
      3. 将新对象的proto指向构造函数默认的prototype指针指向的对象
      4. 执行构造函数中的代码(为这个新对象添加属性)
      5. 返回新对象。
    1. 特殊比较——寄生构造函数模式:
      function Person(name, age, job){
      var o = new Object();
      o.name = name;
      o.age = age;
      o.job = job;
      o.sayName = function(){
      alert(this.name);
      };
      return o;
      }

       var friend = new Person("Nicholas", 29, "Software Engineer");
       friend.sayName();  //"Nicholas"
      
    • 解析:这里想要强调的是,当使用new操作符来对一个函数进行操作时,系统默认会创建新对象,此时还要根据函数中是否存在return值,来判断。如果没有返回值,则返回系统后台新创建的对象,如果有返回值而且是一个对象,则程序猿指定的返回值会代替新创建对象返回。
    • 所以,上面new之后返回给friend的是o,与工厂模式中一样,不能依赖instanceof和constructor来确定对象的具体类型。
    1. 原型模式:
      function Person(){
      }

       Person.prototype.name = "Nicholas";
       Person.prototype.age = 29;
       Person.prototype.job = "Software Engineer";
       Person.prototype.sayName = function(){
           alert(this.name);
       };
       
       var person1 = new Person();
       var person2 = new Person();
       
       person1.name = "Greg";
       alert(person1.name);   //"Greg" – from instance
       alert(person2.name);   //"Nicholas" – from prototype
      

    对象继承

    1. 原型继承:
      function SuperType(){
      this.property = true;
      }

       SuperType.prototype.getSuperValue = function(){
           return this.property;
       };
       
       function SubType(){
           this.subproperty = false;
       }
       
       //inherit from SuperType
       SubType.prototype = new SuperType();
       
       SubType.prototype.getSubValue = function (){
           return this.subproperty;
       };
       
       var instance = new SubType();
       alert(instance.getSuperValue());   //true
      
       alert(instance instanceof Object);      //true
       alert(instance instanceof SuperType);   //true
       alert(instance instanceof SubType);     //true
      
       alert(Object.prototype.isPrototypeOf(instance));    //true
       alert(SuperType.prototype.isPrototypeOf(instance)); //true
       alert(SubType.prototype.isPrototypeOf(instance));   //true
      
    • 缺点:对象实例共享所有继承的属性和方法。
    1. 借用构造函数:
      function SuperType(){
      this.colors = ["red", "blue", "green"];
      }

       function SubType(){  
           //inherit from SuperType
           SuperType.call(this);
       }
      
       var instance1 = new SubType();
       instance1.colors.push("black");
       alert(instance1.colors);    //"red,blue,green,black"
       
       var instance2 = new SubType();
       alert(instance2.colors);    //"red,blue,green"
      
    • 解析:这里实际上是在(未来将要)新创建的SubType实例的环境下调用了SuperType构造函数,这样就会在新SubType对象上执行SuperType()函数中定义的所有对象初始化代码,这样每个SubType实例都会具有自己的colors属性。
    • 解决了上一方法中所有对象共享同一实例的缺点,每个对象都会调用SubType(),因此也都有自己的环境,会形成自己的实例属性。
    1. 组合继承(原型链+借用构造函数)

       function SuperType(name){
           this.name = name;
           this.colors = ["red", "blue", "green"];
       }
       
       SuperType.prototype.sayName = function(){
           alert(this.name);
       };
      
       function SubType(name, age){  
           SuperType.call(this, name);  //第二次调用超类构造函数
           
           this.age = age;
       }
      
       SubType.prototype = new SuperType();    //第一次调用超类构造函数
        SubType.prototype.constructor = SubType;
       SubType.prototype.sayAge = function(){
           alert(this.age);
       };
       
       var instance1 = new SubType("Nicholas", 29);
       instance1.colors.push("black");
       alert(instance1.colors);  //"red,blue,green,black"
       instance1.sayName();      //"Nicholas";
       instance1.sayAge();       //29
      
       var instance2 = new SubType("Greg", 27);
       alert(instance2.colors);  //"red,blue,green"
       instance2.sayName();      //"Greg";
       instance2.sayAge();       //27
      
    • 解析:这种方法通过使用原型链实现对原型属性和方法的继承(需共享的数据),通过借用构造函数来实现对实例属性的继承(需要每个实例独立的数据)。
    • 最终,子类会包含超类的全部实例属性。子类的原型与超类的原型会分开,是不同的对象,更改不会相互影响,但通过子类原型的proto可以取得超类的原型。
    • 组合继承最大的问题:无论什么情况下都会两次调用超类构造函数,一次是在创建子类原型的时候,第二次是在子类构造函数内部call。
    • instanceof和isPrototypeOf()也能够用于识别基于组合继承创建的对象。
    1. 原型式继承:
      function object(o){
      function F(){};
      F.prototype = o;
      return new F();
      }
    • 这意味着被传入的对象o成为了新对象的原型,新对象的proto直接指向o,并且当新对象对实例中不存在但原型上存在的属性操作时,对象o也会被随之改变。
    • 原型式继承还可以使用ES5中的Object.create(),如下:
      Square.prototype = Object.create(Rect.prototype,{
      constructor:{
      configure:true,
      enumerable:true,
      writable:true,
      value:Square
      }
      })
    • 解析:这样就实现了Square继承Rectangle的操作,还免去了生成中间构造函数F()的操作。
    1. 原型式继承进阶版——寄生式继承
      function createAnother(original){
      var clone = object(original); //这里沿用上面的object()函数
      clone.sayHi = function(){
      alert("hi");
      }
      return clone;
      }
      var person = {
      name: "json",
      age: 23
      }
      var newPerson = createAnother(person);
      newPerson.sayHi(); //"hi"
    • 解析:其实就是在上面基础上,增加了可以对继承后的对象添加实例方法或实例属性,然后这里继承以后,newPerson的proto指向person,两者修改其一,另一都会受到影响。
    1. 寄生组合式继承:
    • 完整版代码:
      function object(o){
      function F(){}
      F.prototype = o;
      return new F();
      }

              function inheritPrototype(subType, superType){
                  var prototype = object(superType.prototype);   //返回一个新对象,新对象的__proto__指向superType.prototype            
                  subType.prototype = prototype;                 //修改子类的原型,之后,subType的实例的__proto__会指向这个原型,而这个原型的__proto__会指向父类的原型superType.prototype,由此实现继承
                  subType.prototype.constructor = subType;   //修正constructor
              }
                                      
              function SuperType(name){
                  this.name = name;
                  this.colors = ["red", "blue", "green"];
              }
              
              SuperType.prototype.sayName = function(){
                  alert(this.name);
              };
      
              function SubType(name, age){  
                  SuperType.call(this, name);
                  this.age = age;
              }
      
              inheritPrototype(SubType, SuperType);
              
              SubType.prototype.sayAge = function(){
                  alert(this.age);
              };
              
              var instance1 = new SubType("Nicholas", 29);
              instance1.colors.push("black");
              alert(instance1.colors);  //"red,blue,green,black"
              instance1.sayName();      //"Nicholas";
              instance1.sayAge();       //29
             
              var instance2 = new SubType("Greg", 27);
              alert(instance2.colors);  //"red,blue,green"
              instance2.sayName();      //"Greg";
              instance2.sayAge();       //27
      
    • 解析: 与组合继承相比的高效率在于,只调用一次SuperType构造函数,并且避免了在SuperType.prototype上面创建不必要的多余属性。与此同时,原型链保持不变,能够用instanceof和isPrototypeOf()来判断。是引用类型最理想的继承范式。

    • 这里说只调用一次SuperType构造函数,被省略掉那那一次是new SuperType(),因为这里如果直接new一个出来的话,虽然可以得到一个proto指向SuperType.prototype对象的对象,但是会存在冗余的SuperType实例属性,而本模式中的做法是,另用一个空函数,使它的prototype也指向SuperType.prototype,这样除了实例属性为空以外,其他都与SuperType()一样,此时new一个新函数的实例对象,就能取得一个proto指向SuperType.prototype对象的对象,最后将subType的prototype指向这个新new的实例对象,此时subType.prototype.proto = SuperType.prototype, 就实现了subType.prototype到SuperType.prototype的链接,形成了继承链,而subType.prototype里也没有冗余的值为空的SubperType的实例属性,Perfect!!!

    • 代码注释:

      • var prototype = object(superType.prototype); //返回一个新对象,新对象的proto指向superType.prototype
      • subType.prototype = prototype; //修改子类的原型,之后,subType的实例的proto会指向这个原型,而这个原型的proto会指向父类的原型superType.prototype,由此实现继承

    补充:寄生组合式继承的图解:

    111 (1).png

    相关文章

      网友评论

          本文标题:js--创建对象与对象继承

          本文链接:https://www.haomeiwen.com/subject/giuinttx.html