JavaScript 中定义类与对象的六种常用方式详解

作者:青柠

时间:2026-10-28

浏览:555

本文详细解析 JavaScript 中定义类和对象的六种方式:工厂模式、构造函数模式、原型模式、混合构造函数/原型模式、动态原型模式以及混合工厂模式。通过代码示例对比各模式的优缺点,重点分析内存共享、方法复用及属性初始化等核心问题,帮助开发者选择最适合的面向对象实现方案。

文章目录

JavaScript 类与对象定义概述

JavaScript 是一门面向对象的编程语言。在探讨面向对象编程时,类的概念是核心组成部分。与 C#、Java 等强类型语言拥有固定的类定义语法不同,JavaScript 提供了多种灵活的方式来实现类和对象。常见的实现方式主要包括以下几种。



1.工厂方式

工厂模式

工厂模式的核心是创建一个返回特定类型对象的工厂函数。具体实现代码如下。


代码示例如下。

function createCar(sColor,iDoors,iMpg)
{
var oTempCar=new Object;
oTempCar.color=sColor;
oTempCar.doors=iDoors;
oTempCar.mpg=iMpg;
oTempCar.showColor=function()
   {
alert(this.color);
   }
return oTempCar;
}
var oCar1=createCar("red",4,23);
var oCar2=createCar("blue",3,25);
oCar1.showColor();
oCar2.showColor();

这种模式在每次调用工厂函数时都会创建一个新的对象实例。然而,存在一个显著问题:每次生成新对象时,都会重新创建 showColor 函数。这导致每个对象都拥有自己独立的 showColor 版本,尽管从功能角度看,所有对象实际上应该共享同一个函数。为了解决这一效率问题,开发者可以将方法定义在工厂函数外部,然后让对象引用指向该外部函数,具体实现如下。


代码示例如下。

function showColor()
{
alert(this.color);
}
function createCar(sColor,iDoors,iMpg)
{
var oTempCar=new Object;
oTempCar.color=sColor;
oTempCar.doors=iDoors;
oTempCar.mpg=iMpg;
oTempCar.showColor=showColor;
return oTempCar;
}
var oCar1=createCar("red",4,23);
var oCar2=createCar("blue",3,25);
oCar1.showColor();
oCar2.showColor();

这种方式避免了为每个对象创建独立的 showColor 函数,而是让它们共享指向同一个函数的指针。虽然在功能上解决了重复创建的问题,但该函数在语义上并不属于对象的方法。因此,接下来介绍构造函数模式。



2.构造函数方式

构造函数模式

构造函数模式与工厂模式相似,具体代码示例如下。


代码示例如下。

function Car(sColor,iDoors,iMpg)
{
this.color=sColor;
this.doors=iDoors;
this.mpg=iMpg;
this.showColor=function()
   {
alert(this.color);
   }
}
var oCar1=new Car("red",4,23);
var oCar2=new Car("blue",3,25);

在构造函数内部,并没有显式创建对象,而是使用 this 关键字。当使用 new 运算符调用构造函数时,JavaScript 引擎会在执行第一行代码之前自动创建一个对象实例,只有通过 this 才能访问到这个对象。但是,这种模式同样存在每个对象都会创建自己版本的 showColor 函数的问题。为了解决这一问题,引入了原型模式。



3.原型方式

原型模式

原型模式利用对象的 prototype 属性,将其视为创建新对象所依赖的原型。这里使用一个空的构造函数来定义类名,然后将所有方法和属性直接赋值给 prototype 属性。具体实现如下。


代码示例如下。

function Car()
{}
Car.prototype.color="red";
Car.prototype.doors=4;
Car.prototype.mpg=23;
Car.prototype.drivers=new Array("Mike","Sue");
Car.prototype.showColor=function()
{
alert(this.color);
}

原型模式仅支持直接赋值,无法通过向构造函数传递参数来初始化属性值。在使用这种方式时,会遇到两个主要问题。首先,必须创建每个对象实例后才能修改属性的默认值,无法在创建对象时直接获得所需的属性值,这显得不够灵活。其次,当属性指向对象(如数组)时,会出现共享问题。虽然函数共享没有问题,但对象共享会导致所有实例指向同一个对象引用,而通常每个实例需要拥有自己的对象属性。



具体示例如下。


代码示例如下。

var oCar1=new Car();
var oCar2=new Car();
oCar1.drivers.push("Matt");
alert(oCar1.drivers);//输出 "Mike,Sue,Matt"
alert(oCar2.drivers);//输出"Mike,Sue,Matt"

由此可见,drivers 属性只是一个指向对象的指针,因此所有实例实际上共享同一个数组对象。鉴于上述问题,我们引入了混合使用构造函数和原型模式的方法。



4.混合的构造函数/原型方式

混合构造函数与原型模式

这种模式的思想是:使用构造函数定义对象的所有非函数属性(包括普通属性和指向对象的属性),而使用原型模式定义对象的函数属性(方法)。这样,所有函数只被创建一次,而每个对象都拥有自己的对象属性实例。具体代码示例如下。


代码示例如下。

function Car(sColor,iDoors,iMpg)
{
this.color=sColor;
this.doors=iDoors;
this.mpg=iMpg;
this.drivers=new Array("Mike","Sue");
}
Car.prototype.showColor=function()
{
alert(this.color);
}
var oCar1=new Car("red",4,23);
var oCar2=new Car("blue",3,25);
oCar1.drivers.push("Matt");
alert(oCar1.drivers);//输出 "Mike,Sue,Matt"
alert(oCar2.drivers);//输出 "Mike,Sue"

从实例代码可以看出,这种模式同时解决了原型模式的两个主要问题。不过,部分开发者认为这种方式仍不够完美。



5.动态原型方式

动态原型模式

我们知道,大多数面向对象语言在视觉上将属性和方法封装在类内部。而上述混合模式中,showColor 方法定义在类的外部。因此,开发者设计了动态原型模式。这种模式的基本思想与混合构造函数/原型模式相同,唯一的区别在于对象方法的位置。具体实现如下。


代码示例如下。

function Car(sColor,iDoors,iMpg)
{
this.color=sColor;
this.doors=iDoors;
this.mpg=iMpg;
this.drivers=new Array("Mike","Sue");
if(typeof Car._initialized=="undefined")
  {
Car.prototype.showColor=function()
     {
alert(this.color);
     }
  }
Car._initialized=true;
}

在动态原型模式中,Car.prototype.showColor 只会被创建一次。这使得代码结构更接近其他语言中的类定义。



6.混合工厂方式

混合工厂模式

这种模式通常被视为前一种方式的变通方法,其目的是创建假构造函数,仅返回另一种对象的新实例。具体代码示例如下。


代码示例如下。

function createCar()
{
var oTempCar=new Object;
oTempCar.color=“red”;
oTempCar.doors=4;
oTempCar.mpg=23;
oTempCar.showColor=function()
   {
alert(this.color);
   };
return oTempCar;
}
var car=new Car();

由于在 Car() 构造函数内部调用了 new 运算符,因此会自动忽略第二个 new 运算符。在构造函数内部创建的对象被传递回变量 var。这种方式在对象方法的内部管理上与经典工厂模式存在相同的问题,因此强烈建议:除非万不得已,应避免使用这种方式。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章