javascript 构造函数 原型 实例

以下我将通过几个小试验来了解一个对象和它的构造函数、原型、它构造函数的prototype属性间的关系,以及一个通过new和构造函数创建的对象,从被创建到被构造函数初始话的过程。

最简单的情况一:

首先,创建构造函数Person:

function Person () {
}

然后,给这个Person函数的prototype上添加属性:

Person.prototype.sayHi = function(){
     return 'hello';
}

最后,我们可以创建这个构造函数的实例:

var p = new Person();

这时,我们调用:

p.sayHi();      // hello

我们在p上可以调用p的构造函数Person的prototype上定义的方法。

情况二:

首先,创建构造函数Person:

function Person () {
}

然后,我们就创建Person的实例:

var p = new Person();

这时,再给这个Person函数的prototype上添加属性:

Person.prototype.sayHi = function(){
     return 'hello';
}

最后,我们调用:

p.sayHi();   //hello

结果还是一样的。

以上两种情况说明了,当用Person构造函数创建实例p的时候,js解释器,已经自动把p的原型的引用『指向』了Person的prototype属性所引用的对象;当我们为Person的prototype属性添加了sayHi()方法之后,因为p的原型对象和Person的prototype属性是一个对象,所以,p也自然可以调用这个从其原型继承的sayHi()方法。

情况三,再复杂一点:

首先,创建构造函数Person:

function Person () {
}

然后,我们就创建Person的实例:

var p = new Person();

之后,注意!现在做的会与上面有所不同:

Person.prototype = {
     sayHi: function(){
             return 'hello';
     }
}

最后,我们调用:

p.sayHi();
//TypeError: Object Person has no method 'sayHi' 

p上没有sayHi这个方法。

接着:我们这样:

var p2 = new Person();
p2.sayHi(); // hello

这是为什么呢?会不会有这样的疑问:p的原型不是和Person的prototype属性指向的是同一个对象啊。注意!!!比较上边对Person的prototype属性的具体操作。发现了吧,对,情况三中,我们在创建p的时候,Person的prototype是js解释器自动创建的默认的原型对象。之后,的代码Person.prototype = {},使Person的prototype指向了一个新创建的对象。这时p的原型还是指向那个默认的原型对象,而Person的prototype已经是另外一个对象了。所以,我们给这个全新的Person的prototype对象,添加的sayHi()方法是p继承不到的。

情况四,再复杂一点

我们直接在Person的构造函数里给Person创建一个全新的prototype对象。

function Person() {
      Person.prototype = {
             sayHi: function(){
                     return 'hello';
             }
      }
}

然后,创建实例:

var p = new Person();

调用:

p.sayHi(); 
//TypeError: Object Person has no method 'sayHi' 

为什么?不要着急,接着看,再创建一个实例

var p2 = new Person();

调用:

p2.sayHi(); // hello

结论:

用new和构造函数创建对象时,过程是这样的:首先,js解释器,会创建一个实例对象,接着就会把这个原形对象指向其构造函数的prototype属性指向的对象,然后,才会调用其构造函数。如上例,当p被创建的时候,js解释器,把p的原型指向了Person的默认prototype对象,这基本上是一个空对象,所以p上没有sayHi方法。然后Person被调用了,其内部的代码被运行,Person的prototype属性被指向了一个全新的对象,这个对象上有一个sayHi()属性。接着我们又用Person函数创建了另外一个对象p2,这时,Person的prototype属性,已经在初始化p时被指向了一个包含sayHi()方法的对象。所以可以成功调用p2.sayHi().这种构造方法的写法是不建议的,因为每创建一个新的对象,其构造函数的prototype属性即被重新指定一个全新的对象,破坏了对象的继承。