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

创建对象

  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,由此实现继承

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

js--创建对象与对象继承_第1张图片
111 (1).png

你可能感兴趣的:(js--创建对象与对象继承)