JavaScript面向对象编程(对象创建)

前言

在学面向对象(objetct-oriented,oo)编程之前,首先需要知道什么是对象,ECMA-262,将对象定义为无序属性的集合,其属性可以包含基本值、对象或者是函数JavaScript中有多种方式来创建对象,比如:工厂模式、构造函数模式、原型模式、组合构造函数模式和原型模式、寄生构造函数模式等等。

一、工厂模式

工厂模式是软件工程领域一种广为人知的设计模式,这种模式抽象了创建具体对象的过程。考虑到在ECMAScript 中无法创建类,开发人员就发明了一种函数,用函数来封装以特定接口创建对象的细节。

function createStudent(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 student1= createStudent("xiaoming", 9, "student");
var student2 = createStudent("xiaowagn", 15, "student");



函数createStudent()能够根据接受的参数来构建一个包含所有必要信息的Student对象。可以无数次地调用这个函,而每次它都会返回一个包含三个属性一个方法的对象。工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)。随着JavaScript的发展,又一个新模式出现了。

二、构造函数模式

首先我们将前面的代码修改如下:

function Student(name,age){
      this.name=name;
      this.age=age;
      this.showname=function(){
      alert(this.name);
      }
}
var xiaoming =new Student("xiaoming",6);
var xiaogang =new Student("xiaogang",7);


与工厂模式区别:

 1.没有显示创建对象

  2.直接将属性赋值给了this对象

  3.没有Return语句。

要创建一个实例,需要使用new操作符。

创建对象步骤:

1.创建一个新的对象

2.将构造函数的作用域赋给新的对象(this就指向这个新的对象)

3.执行构造函数的代码,为这个对象添加属性

4.返回这个对象

 

alert(xiaoming.constructor==Student) //true;

对象的constructor属性最初是用来标识一个对象的类型的。检查一个对象类型,还是使用instanceof

alert(xiaoming instanceof Student) //true

alert(xiaoming instanceof Object) //true是因为所有对象均继承自Object

 

创建自定义的构造函数意味着将来可以将它的实例标识为一种特定的类型;而这正是构造函数模式胜过工厂模式的地方。使用构造函数也是有缺点的。就是每个方法都要在每个实例上重新创建一遍。比如,xiaomingxiaogang都有一个showname()方法。但是那两个方法不是同一个Function的实例。ECMAScript中的函数是对象,因此,每定义一个函数,就是实例化了一个对象。从逻辑角度讲,此时构造函数也可以这样定义。

function student(name,age){

this.name=name;

this.showName=new Function(“alert(this.name)”);

}

可以看出,每个Student实例都包含一个不同的Function 实例。不同的实例上的同名函数是不相等的。以下代码可以证明这一点。

alert(xiaoming.showName==xiaogang.showname);//false

二、构造函数模式

我们创建的每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。如果按照字面意思来理解,那么prototype 就是通过调用构造函数而创建的那个对象实例的原型对象。使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法。换句话说,不必在构造函数中定义对象实例的信息,而是可以将这些信息直接添加到原型对象中。

function Student(){

 

}

Student.property.name=”default name”;

Student.property.age=0;

Student.property.showName=funciton(){

alert(this.name);

}

Var xiaoming=new Student();

Var xiaogang=new Student();

与构造函数模式的不同时,新对象的这些属性和方法是由所有的实例共享的。换句话说xiaoming和xiaogang访问的都是同一组属性和同一个showName()函数。要理解原型模式的工作原理,必须先理解ECMAScript 中原型对象的性质。

1. 理解原型对象

无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype

属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个constructor

(构造函数)属性,这个属性包含一个指向prototype 属性所在函数的指针。就拿前面的例子来说,

Student.prototype. constructor 指向Student。而通过这个构造函数,我们还可继续为原型对象添加其他属性和方法.

 

JS中我们通过isPropertyOf()来判断,某个实例是否指向某个函数的的原型。

Eg

Student.property.isPropertyOf(xiaoming);//true

ECMAScript5中含增加了Object.getPropertyOf()方法来获取一个实例的原型

Eg

alert(Object.getPropertyOf(xiaoming)==Student.property);//true

alert(Object.getPropertyOf(xiaoming).name);//default name

此外,我们还可以通过hasOwnProperty()方法来检测一个属性是存在实例中还是存在原型中。alert(xiaoming.hasOwnProperty(“name”));//false

alert(xiaoming.property.hasOwnProperty(“name”));//true

原型模式也不是没有缺点。首先,它省略了为构造函数传递初始化参数这一环节,结果所有实例在默认情况下都将取得相同的属性值。虽然这会在某种程度上带来一些不方便,但还不是原型的最大问题。原型模式的最大问题是由其共享的本性所导致的。原型中所有属性是被很多实例共享的,这种共享对于函数非常合适。对于那些包含基本值的属性倒

也说得过去,毕竟(如前面的例子所示),通过在实例上添加一个同名属性,可以隐藏原型中的对应属性。然而,对于包含引用类型值的属性来说,问题就比较突出.

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

创建自定义类型的最常见方式,就是组合使用构造函数模式与原型模式。构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。结果,每个实例都会有自己的一份实例属性的副本,但同时又共享着对方法的引用,最大限度地节省了内存。另外,这种混成模式还支持向构造函数传递参数;可谓是集两种模式之长。

function Student(name,age){

this.name=name;

this.age=age;

}

Student.property.showName=funciton(){alert(this.name);}

这种构造函数与原型混成的模式,是目前在ECMAScript 中使用最广泛、认同度最高的一种创建自定义类型的方法。可以说,这是用来定义引用类型的一种默认模式。

五、寄生构造函数模式

通常,在前述的几种模式都不适用的情况下,可以使用寄生(parasitic)构造函数模式。这种模式的基本思想是创建一个函数,该函数的作用仅仅是封装创建对象的代码,然后再返回新创建的对象;但从表面上看,这个函数又很像是典型的构造函数。

funciton Student(name,age){

Var o=new Object();

o.name=name;

o.age=age;

o.showName=function(){alert(this.name);}

Reurn o;

关于寄生构造函数模式,有一点需要说明:首先,返回的对象与构造函数或者与构造函数的原型属性之间没有关系;也就是说,构造函数返回的对象与在构造函数外部创建的对象没有什么不同。为此,不能依赖instanceof 操作符来确定对象类型。由于存在上述问题,我们建议在可以使用其他模式的情况下,不要使用这种模式。个人感觉这模式还是有点模仿工厂模式。

 


你可能感兴趣的:(JavaScript面向对象编程(对象创建))