JavaScript 中定义类与对象的六种常用方式详解
本文详细解析 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。这种方式在对象方法的内部管理上与经典工厂模式存在相同的问题,因此强烈建议:除非万不得已,应避免使用这种方式。
ES6模块化中js文件怎么用import和export交互
2026-11-05 17:00:48
JS读取本地文件时报错或无响应的常见原因
2026-11-05 16:32:31
let、const与var的区别:JavaScript变量声明最佳实践
2026-11-05 14:16:38
JS中直接定义静态二维数组的字面量写法
2026-11-05 14:16:08
JS 怎么查找字符串中所有匹配项的位置
2026-11-05 14:15:53
