JavaScript原型对象、构造函数和实例对象功能与用法详解

作者:代码拾荒时间:2026-10-22 16:32:05浏览:452

本文详细解析JavaScript中构造函数、原型对象与实例对象的核心关系。通过对比实例属性与原型共享属性的差异,阐述如何利用prototype和constructor建立继承机制,并介绍Object.getPrototypeOf等ES5新增方法,帮助开发者掌握JS面向对象编程的关键概念。

JavaScript原型对象、构造函数和实例对象功能与用法详解

本文通过具体示例,深入解析JavaScript中原型对象、构造函数以及实例对象的功能与使用方式,供读者参考学习。

尽管JavaScript语言本身并未直接定义“类”这一概念,但开发者可以通过结合使用构造函数与原型对象来模拟类的行为。为了便于阐述,此处暂且不严谨地借用“类”这一术语。

以下整理关于JavaScript构造函数、原型对象及实例对象的核心笔记,若内容存在疏漏或错误,欢迎指正。

首先通过一张关系图直观展示三者之间的联系,随后进行详细拆解。

JavaScript原型对象、构造函数和实例对象功能与用法详解

构造函数与实例对象的关系

构造函数与实例对象的关系

构造函数可视作类的外部表现形式,其函数名通常直接作为类的名称使用。

本质上,构造函数仍是一个函数,但与普通函数存在以下显著区别:

  • 不会显式地创建新对象;
  • 直接将属性和方法赋值给this;
  • 不包含return语句;

构造函数的主要用途是创建新对象。此前笔记曾提及,可以使用new关键字调用构造函数,从而生成特定类型的新对象。例如,创建一个Object类型的实例:


var o=new Object();

为区分构造函数与普通函数,通常约定构造函数名称首字母大写,而普通函数首字母小写。这虽非强制要求,但属于良好的编码习惯。

通过构造函数创建并初始化的属性称为实例属性。所谓实例属性,是指由该构造函数生成的每个对象都拥有该属性的独立副本。这些属性通过实例进行访问,其值取决于每个实例的具体定义;若实例中未重新定义,则采用构造函数初始化时的默认值。示例如下:


function Person(name,age){
  this.name=name;
  this.age=age;
  this.friends=["Tom","Boo"];
}
var p1=new Person("Lily",20);
var p2=new Person("Sam",30);
alert(p1.name); //Lily
alert(p2.name); //Sam
p1.friends.push("Susan");
alert(p1.friends); //Tom,Boo,Susan
alert(p2.friends); //Tom,Boo

上述示例定义了一个Person构造函数,并初始化了name、age和friends三个属性。随后创建了两个实例对象p1和p2。观察可知,每个属性均为各自独立拥有,互不影响。这是因为每个实例对象都持有一份属性的副本。

每个实例对象均包含一个指向其构造函数的属性,该属性名为constructor:


function Person(name,age){
  this.name=name;
  this.age=age;
}
var p1=new Person("Lily",20);
var p2=new Person("Sam",30);
alert(p1.constructor==Person); //true
alert(p2.constructor==Person); //true

构造函数拥有一个prototype属性,该属性指向原型对象。

原型对象与实例对象的关系

原型对象与实例对象的关系

在JavaScript中,每个对象都关联着一个原型对象。类的所有实例对象均从该原型对象上继承属性。

原型对象是类的唯一标识:仅当两个对象继承自同一个原型对象时,它们才被视为同一类的实例。

前文提及构造函数拥有指向原型的prototype属性。换言之,一个对象的原型即为其构造函数的prototype属性值。当函数被定义时,系统会自动创建并初始化prototype值,该值是一个对象,且初始时仅包含一个constructor属性,该属性指回与原型相关联的构造函数。示例如下:


function Person(name,age){
  this.name=name;
  this.age=age;
}
alert(Person.prototype); //[object Object]
alert(Person.prototype.constructor==Person); //true

也可以通过原型来定义属性和方法。通过原型创建的属性和方法被所有实例共享。这意味着,若在一个实例中修改了该属性或方法的值,其他所有实例的对应属性或方法值也会随之改变:


function Person(name,age){
  this.name=name;
  this.age=age;
}
Person.prototype.friends=["Tom","Sam"];
var p1=new Person("Lily",24);
var p2=new Person("Susan",20);
alert(p1.friends); //Tom,Sam
alert(p2.friends); //Tom,Sam
p1.friends.push("Bill");
alert(p1.friends); //Tom,Sam,Bill
alert(p2.friends); //Tom,Sam,Bill

由上述示例可见,通过原型定义的属性被所有实例共享。为p1添加朋友后,p2也自动获得了该朋友。

然而,在许多场景下,这种共享现象并非预期结果。那么,何时应使用构造函数初始化属性和方法,何时又该由原型对象定义呢?

通常建议在构造函数内定义一般成员,即那些在每个实例中值均不同的属性,尤其是对象或数组类型的值;而在原型对象中则定义所有实例共享的属性,即其值在所有实例中保持一致的属性。

当使用构造函数创建实例时,实例内部包含一个指向构造函数原型对象的指针。部分浏览器支持通过__proto__属性来表示这一内部指针:


function Person(name,age){
  this.name=name;
  this.age=age;
}
Person.prototype.sayName=function(){
  alert(this.name);
}
var p1=new Person("Lily",24);
alert(p1.__proto__.sayName); //function (){alert(this.name);}
alert(p1.__proto__.constructor==Person); //true

ECMAscript5新增了一个方法Object.getPrototypeOf(),用于返回前述实例对象内部指向其原型的指针值:


function Person(name,age){
 this.name=name;
 this.age=age;
}
var p1=new Person("Lily",24);
alert(Object.getPrototypeOf(p1)==Person.prototype); //true

isPrototypeOf()方法也可用于确定实例对象与原型之间的关系:


function Person(name,age){
  this.name=name;
  this.age=age;
}
var p1=new Person("Lily",24);
alert(Person.prototype.isPrototypeOf(p1)); //true

原型语法

原型语法

基于前文对原型对象、实例对象及构造函数关系的介绍,我们已知给原型对象添加属性和方法的定义方式为:Person.prototype=name。

那么,是否每次定义Person的属性都要重复书写Person.prototype呢?答案是否定的。我们也可以采用对象字面量的方式来创建原型对象:


function Person(){
}
Person.prototype={
  name:"Tom",
  age:29
}
var p1=new Person();
alert(p1.name); //Tom
alert(p1.age); //29

需注意,此方法相当于重写了整个原型对象,从而切断了其与构造函数的联系,此时Person.prototype.constructor不再指向Person:


function Person(){
}
Person.prototype={
  name:"Tom",
  age:29
}
var p1=new Person();
alert(Person.prototype.constructor==Person); //false
alert(Person.prototype.constructor==Object); //true

因此,若希望其重新指向Person,需显式进行赋值:


function Person(){
}
Person.prototype={
 constructor:Person,
 name:"Tom",
 age:29
}
var p1=new Person();
alert(Person.prototype.constructor==Person); //true
alert(Person.prototype.constructor==Object); //false

总结

总结

最后,通过一个综合示例再次梳理构造函数、原型对象及实例对象之间的关系:


function Person(name,age){
  this.name=name;
  this.age=age;
}
Person.prototype.sayName=function(){
  alert(this.name);
}
var p1=new Person("Tom",20);
alert(Person.prototype); //object
alert(Person.prototype.constructor==Person); //true
alert(p1.constructor==Person); //true
alert(p1.__proto__==Person.prototype); //true
alert(p1.__proto__.__proto__==Object.prototype); //true
alert(p1.__proto__.__proto__.constructor==Object); //true
alert(Person.constructor==Function); //true
alert(Object.prototype.constructor==Object);

JavaScript原型对象、构造函数和实例对象功能与用法详解

上图展示了该示例中原型、构造函数和实例属性之间的关系。

更多关于JavaScript相关内容感兴趣的读者可查看本站专题:《javascript面向对象入门教程》、《JavaScript常用函数技巧汇总》、《JavaScript错误与调试技巧总结》、《JavaScript数据结构与算法技巧总结》、《JavaScript遍历算法与技巧总结》及《JavaScript数学运算用法总结》

希望本文所述内容对大家学习JavaScript程序设计有所帮助。

作者最新文章