JavaScript 模拟类机制及私有变量的方法及思路

作者:闭包旅人

时间:2026-10-27

浏览:301

本文探讨如何在 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);
    };
  }

实用工具推荐

电子包浆

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

使用 ›
图片压缩

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

使用 ›
相关文章