创建对象

我们开始可以用Object构造函数或者对象字面量来快速创建对象,但使用这种方式创建多个对象时会产生大量重复代码,所以我们有了以下几种创建对象的方式。

(1)工厂模式

function createPerson(name, age,sex){ //传入的参数可以类比成送入工厂的原材料
        var o = new Object();
        o.name = name;
        o.age = age;
        o.sex = sex;
     o.sayName(){
       alert(this.name);  
     }
       
     return o;    //返回出的对象可以类比成工厂加工完成的产品
}
var person1 = createPerson("Jack",18,"男");
var person2 = createPerson("Rose",18,"女");

当我们使用工厂模式的函数时,并不知道自己所创建的是哪一类对象,也就是说对象之间的辨识度太低,在这基础上我们有了构造函数模式。

(2)构造函数模式

function Person(name, age,sex){
       this.name = name;  //这里的this在创建对象时会指向新创建的对象
       this.age = age;
       this.sex = sex;
       this.sayName = function{
               alert(this.name);
       }
}            
var person1 = new Person("Jack",18,"男");
var person2 = new Person("Rose",18,"女");

构造函数名可以作为对象的标识符,这样对象的辨识度便上升了。

因为在定义函数时会创建新的Function实例,每一个对象实例的同名方法不是同一个Function实例,每创建一个实例会新创建一个Function实例,这无疑会增加不必要的开销。针对这一点,

我们可以把函数定义在对象外部。

function Person(name, age,sex){
        this.name = name;
        this.age = age;
        this.sex = sex;
        this.sayName = sayName;      
}
function sayName(){
        alert(this.name);
}
var person1 = new Person("Jack",18,"男");
var person2 = new Person("Rose",18,"女");

 但是当对象有许多方法需要定义时,我们又会发现需要在全局范围定义许多全局方法,我们就离封装代码的目的越走越远了,所以我们又有了原型模式。

(3)原型模式

function Person(){
}
Person.prototype.name = "Jack";
Person.prototype.age = 18;
Person.prototype.sex = "男";
Person.prototype.sayName = function(){
         alert(this.name);
}
var person1 = new Person();
person1.sayName();//"Jack" 开始搜索实例的内部是否有该方法,没有的场合会开始搜索实例的原型对象
var person2 = new Person();
person2.sayName();//"Jack"
alert(person1.sayName == person2.sayName);//true

因为prototype出现了很多次我们也可以这样写

function Person(){
}
Person.prototype = {
      constructor:Person, //这里的constructor属性会变成可枚举的,默认情况下是不枚举的,可以用Object.defineProperty()
         name:"Jack",
         age:18,
         sex:"男",
         sayName:function(){
                alert(this.name);
         }
}

原型模式对于对象函数的共享非常友好,但因为每个对象的所有属性和方法都是公开的,当修改某个实例的属性时,其他实例的属性也会跟着一起改变,换句话说,实例彼此之间的相关性太大,所以我们可以组合使用构造函数模式和原型模式。用构造函数模式来定义实例属性,用原型模式来定义方法和共享的属性。

(4) 构造函数和原型模式组合使用

function Person(name, age,sex){
         this.name = name;
         this.age = age;
         this.sex = sex;
         this.friend = ["Jack","Rose"];
}
Person.prototype = {
         constructor : Person,
         sayName : function(){
                 alert(this.name);
         }
}

 当我们想把构造函数和原型写在一起时,可以使用动态原型模式。

(5)动态原型模式

function Person(name, age,sex){
         this.name = name;
         this.age = age;
         this.sex = sex;
         if(typeof this.sayName != 'function'){   //通过这种写法,我们不会重复定义函数
                 Person.prototype.sayName = function(){
                          alert(this.name);
                 };
         }
}

(6)寄生构造函数模式

function Person(name, age,sex){
         var o = new Object(); //在典型构造函数模式中是直接把属性保存在this,这里新创建了一个对象
         o.name = name;
         o.age = age;
         o.sex = sex;
         o.sayName = function(){
                 alert(this.name);
         }
         return o; //这里和工厂模式又有相似之处,区别在于实例的创建
}    

var person = new Person("Jack",18,"男"); //表面上是构造的Person对象,实际上是创建的Object类型的对象实例。
person.sayName();//"Jack"

寄生构造函数模式可以创建某个对象的加强版对象

function specialArray(){
         var values = new Array();
         values.push.apply(values,arguments)//将传来的参数装进数组
         values.toPipedString = function(){
                 values.join('|');
         } 
         return values
} 
var cities = new specialArray("Shanghai","Beijing","Guangzhou");
alert(cities.toPipedString());//"Shanghai|Beijing|Guangzhou"

(7)稳妥构造函数模式

稳妥对象指的是没有共有属性,并且方法中不出现this的对象

function Person(name, age,sex){
          var o = new Object();
          o.sayName = function(){    //只能通过此方法改变属性值
                   return name;
          }
          return o;
}