天天看点

怎么在javascript中创建对象

前几天,有总结过javascript中对象的一些知识——【理解javascript中的对象属性】和【javascript原型对象、构造函数和实例对象】,今天在这个基础上,学习了javascript中,创建对象的几种方法。

其实,在【javascript引用类型之object类型】中有提到过两种。用字面量跟构造函数。今天所说的方法,有封装的概念,将属性和方法封装成一个对象,再由此创建实例。

工厂模式

所谓的工厂模式,就是利用函数来定义一个创建对象的接口。然后每次都利用这个函数来创建对象。

function createPerson(name,age){
       	  var o=new Object();
       	  o.name=name;
       	  o.age=age;
       	  return o;
       }

       var p1=createPerson("Tom",24);
       alert(p1.name); //Tom
       alert(p1.age);  //24
           

在这个例子中,createPerson函数内部,用Object类创建了一个对象,该对象的属性值由函数参数获得,最后返回这个对象。接下来实例化的过程就是调用这个函数来实现的。

这种创建对象的方法能够满足创建多个相似对象的需求,但是,不能知道一个对象是属于什么类型(这里继续沿用前面的规则,对js引入一个不规范的类的概念。)

构造函数模式

javascript中可以由构造函数创建特定类型的对象。如Array、Date等。这边的Array、Date就是javascript中原生的构造函数。我们也可以自定义构造函数,由此来创建自定义类型的对象。利用构造函数来创建对象,就是构造函数模式。

function Person(name,age){
             this.name=name;
             this.age=age;
             this.sayName=function(){
                   return this.name;
             }
       }

       var p1=new Person("Tom",24);
       alert(p1.name); //Tom
       alert(p1.age);  //24
       alert(p1.sayName()); //Tom
           

注意,使用构造函数来创建实例,必须使用new操作符。

构造函数的优点是,它可以将它的实例标识为特定的类型。

在【javascript原型对象、构造函数和实例对象】中有提过,使用构造函数实例化的属性是实例属性,即该构造函数的每个实例中都拥有一份属性的副本。对于实现同样功能的方法来说,这实在是没有必要,而且造成内存的浪费。

原型模式

所谓的原型模式也就是通过原型来定义属性和方法。换句话说,不必在构造函数中定义对象的信息,而全部将这些信息添加到对象的原型中。

function Person(){}

       Person.prototype.name="Tom";
       Person.prototype.age="24";
       Person.prototype.friends=["Sam","Lily"];

       var p1=new Person();
       var p2=new Person();

       alert(p1.friends);  //Sam,Lily
       alert(p2.friends);  //Sam,Lily

       p1.friends.push("Bob");

       alert(p1.friends); //Sam,Lily,Bob
       alert(p2.friends); //Sam,Lily,Bob
           

这个例子就是利用原型模式来创建Person类。所有属性都有原型来定义。实例化了两个实例分别为p1和p2。在代码中,我们为p1添加了一个新的朋友,但是p2也一样增加了这个朋友。这其实并不是我们想要的。为什么会这样呢?在【 javascript原型对象、构造函数和实例对象】中也有说到,在原型对象中定义的属性和方法是所有对象实例所共享的,其中一个修改了值,会影响到其他实例。这就是单独使用原型模式的弊端。

但是有点需要注意的,假如是实例重写了属性,那则不会修改原型中的值,并且重写的值会覆盖掉原型中的定义的值:

function Person(){}

       Person.prototype.name="Tom";
       Person.prototype.age="24";
       Person.prototype.friends=["Sam","Lily"];

       var p1=new Person();
       var p2=new Person();

       p1.friends=["Bob"];

       alert(p1.friends);  //Bob
       alert(p2.friends);  //Sam,Lily
           

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

看了前面构造函数模式和原型模式的优缺点,那么其实就可以综合二者的优点来创建对象,这也是创建自定义类型时,最常见的方法。

function Person(name,age){
       	   this.name=name;
       	   this.age=age;
       	   this.friends=["Sam","Bob"];
       }

       Person.prototype={
       	   constructor:Person,
       	   sayName:function(){
       	   	  return this.name;
       	   }
       };

       var p1=new Person("Lily",29);
       var p2=new Person("Tom",24);

       alert(p1.age);  //29
       alert(p2.age);  //24

       p1.friends.push("Susan");

       alert(p1.friends);  //Sam,Bob,Susan
       alert(p2.friends);  //Sam,Bob

       alert(p1.sayName()); //Lily
       alert(p2.sayName()); //Tom
           

动态原型模式

所谓动态原型模式就是指,将对象信息都封装在构造函数中,但是在构造函数内部初始化原型(仅在需要的情况下)。

function Person(name,age){
       	   this.name=name;
       	   this.age=age;
            
           if(typeof this.sayName!="function"){
           	   Person.prototype.sayName=function(){
           	   	   return this.name;
           	   }
           }

       }

       var p1=new Person("Tom",29);
       var p2=new Person("Lily",24);

       alert(p1.age); //29
       alert(p2.age); //24

       alert(p1.sayName()); //Tom
       alert(p2.sayName()); //Lily
           

这个例子,只在sayName()方法不存在的情况下,才会添加到原型中。即,只在初次调用构造函数时才会执行。在这之后,原型已经完成初始化,就不需要再做修改了。

这种方法也同样结合了构造函数模式和原型模式的有点。并且,结构更为简洁些。

注意:使用动态原型模式时,不能使用对象字面量重写原型。

以上就是javascript中创建对象中比较常用的方法。并没有说哪个方法绝对的好,或者绝对的不好。主要是根据实际情况,选择最适合的方式,才是最好的。

继续阅读