JavaScript 模拟类机制及私有变量的方法及思路
本文探讨如何在 JavaScript 中模拟面向对象类机制,重点解析通过 Class 构造函数实现初始化与原型继承的方法。进一步介绍利用 eval 和 toString 提取函数代码以构建有效闭包,从而实现私有变量与方法的思路,并对比了传统直接定义私有变量的方式及性能差异。
JavaScript 类机制模拟概述
在调用部分 JavaScript 框架时,开发者可能会遇到如下形式的代码结构。
具体代码示例如下:
var MyClass = new Class({
initialize: function(param, ...) {
this.param = param;
...
},
func1: function(...) {
...
}
});
var myObj = new MyClass(param);
myObj.func1(...);这种写法体现了典型的面向对象类机制应用,相较于原生 JavaScript 的类实现方式,其逻辑更加清晰自然。同时,基于此结构实现类的继承也相对便捷。那么,这一机制具体是如何实现的?
众所周知,在 JavaScript 中,将函数作为构造器即可创建对象。上述代码可简化为以下形式:
简化后的代码如下:
function MyClass(param) {
this.param = param;
this.func1 = function(..) {
...
};
}
var myObj = new MyClass(param);
myObj.func1();这种基础实现虽然简单易懂,但在构建大型 JavaScript 类库时,代码结构容易变得混乱。从大量代码中区分类、函数、类方法及类属性将变得十分困难。
此处并非为了比较两种方式的优劣,而是出于好奇探究 new Class 的具体实现原理。
Class 构造器实现原理
在上面的代码中,使用 new MyClass() 语句意味着 MyClass 必须是一个函数。这也暗示 new Class 需要返回一个函数对象。从字面含义看,initialize 函数被用作构造函数,因此 new Class 返回的函数内部必须调用 initialize 来初始化对象。基于此分析,可得出如下代码实现:
相关代码如下:
function Class(argu) {
return function() {
var init = argu['initialize'] || function() {}; // 若未提供构造函数 initialize,则使用空函数作为默认构造函数
for(var p in argu) {
this[p] = argu[p];
}
init.apply(this, arguments); // 使用当前匿名函数的 this 替代原 initialize 中的 this
}
}上述代码虽不够严谨,但足以说明问题。需特别注意 init.apply(this, arguments) 这一行,其中涉及变量指代:一是 this,原本 initialize 中默认的 this 已被替换为返回的匿名函数的 this,而该匿名函数是通过 new Class 创建的自定义类构造器;二是 arguments,它指代匿名函数的参数,即上述 new MyClass(param) 中的 param。
this 的转换可能令人困惑,是否存在更简单的方法?请参考以下代码:
改进后的代码如下:
function Class(argu) {
var obj = argu['initialize'] || function() {};
for(var p in argu) {
obj.prototype[p] = argu[p]; // 注意,此处使用的是 prototype
}
return obj; // 实际上仍返回一个函数
}这样看来,实现逻辑更加直观。
私有变量与闭包实现思路
至此,一个简单的类机制构建完成。通过该机制,可以创建类的构造函数、方法及属性,但这些显然都是公有的。那么,如何实现私有变量及方法呢?
我们知道,JavaScript 类的私有变量通常通过闭包机制实现。但在使用 new Class({...}) 转换后,显然难以形成有效的闭包。如何绕过这一限制?
JavaScript 提供了两个方法:eval() 及函数对象的 toString() 方法。前者较为常见,而后者可用于获取函数的具体代码。利用这两个方法,可以简单模拟类的私有变量:
实现代码如下:
function Class(argu) {
var _ = argu['private'] || {};
eval('var obj = ' + (argu['initialize'] || function() {}).toString());
for(var p in argu) {
if(p == 'initialize' || p == 'private')
continue;
if(typeof argu[p] == 'function')
eval('obj.prototype[p] = ' + argu[p].toString());
else
obj.prototype[p] = argu[p];
}
return obj;
}通过函数对象的 toString() 方法提取函数代码,并使用 eval 执行这些代码,即可构造出一个有效的闭包范围,从而实现私有机制。我们可以按如下方式应用:
应用示例代码如下:
var Person = new Class({
private: {
height: 160,
weight: 50
},
initialize: function(name, height, weight) {
this.name = name;
_.height = height || _.height;
_.weight = weight || _.weight;
},
show: function() {
alert('Name:' + this.name + '/nheight:' + _.height + '/nweight:' + _.weight);
}
});
var my = new Person("Zh");
my.show();看似效果不错,但在实际应用中,其用途有限。主要原因是效率问题,相比通常的实现方式,该方法大约需要多花费四倍的时间。在构建大型类库时,这种性能损耗是不可接受的;而在小型应用中,实现以下代码更为简单直接:
传统实现代码如下:
function MyClass(param) {
var privateVar = ...;
this.param = param;
this.func = function() {
alert(privateVar);
};
}什么时候该用JS构造器:与工厂模式、字面量的选择对比
2026-11-05 16:18:22
推荐几款轻量级JS照片墙开源库及对比
2026-11-05 15:56:19
JavaScript递归写法有哪些常见陷阱及避免方法
2026-11-05 14:53:38
JS数组浅拷贝与深拷贝怎么选?场景与方法总结
2026-11-05 14:41:02
p5.js 中 setup() 和 draw() 函数的执行机制详解
2026-11-05 12:32:38
