JavaScript原型对象、构造函数和实例对象功能与用法详解
本文详细解析JavaScript中构造函数、原型对象与实例对象的核心关系。通过对比实例属性与原型共享属性的差异,阐述如何利用prototype和constructor建立继承机制,并介绍Object.getPrototypeOf等ES5新增方法,帮助开发者掌握JS面向对象编程的关键概念。
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异步发送HTTP请求时怎么优雅地处理错误
2026-11-05 17:10:44
5款主流JS在线运行工具对比:CodePen、JSFiddle与StackBlitz怎么选
2026-11-05 17:04:04
JavaScript入门书籍避坑:这些书不适合初学者
2026-11-05 16:31:15
JS使用Math.round实现保留两位小数的原理与代码
2026-11-05 15:35:55
JavaScript 原生 Date 对象怎么实现日期加减
2026-11-05 14:30:00
