JavaScript 类的封装与静态方法实现详解
本文详解JavaScript中类的三种封装方式:门户大开型、命名规范型及闭包型,分析其优缺点。同时介绍静态函数的两种定义方法,包括直接挂载类与类中类模式,旨在帮助开发者理解数据保护、代码重构及模块解耦的实际应用。
本文通过具体示例介绍JavaScript中类的封装技术,供读者参考学习。
JavaScript类的封装必要性
一、为何需要封装?
从信息管理的角度来看,核心目标是隐藏内部信息,而封装则是实现这一目标的手段之一。
在JavaScript中,类的定义主要存在以下三种模式:
第一种为完全暴露的“门户大开”模式;第二种是通过命名规范区分私有与公有成员;第三种则是利用闭包机制。
接下来将逐一详细解析这三种定义方式的具体实现。
完全暴露型封装
首先探讨第一种模式:完全暴露型。
我们先看一个基础场景。
(1)声明一个简单的类,代码示例如下:
function Person(age,name) {
this.name=name;
this.age=age;
}
(2)实例化该类并进行调用。
var p=new Person(-10,"小明");
alert(p.age)//结果出现年龄出现负数
从运行结果看,程序虽能执行,但年龄出现了负数,这不符合实际业务逻辑。我们需要一种能正确处理并符合现实情况的解决方案。
为了解决上述年龄验证问题,我们可以采用扩展原型链的方式。
Person.prototype={
checkAge:function (age) {
if(0<age&&age<150){
return true;
}else {
return false;
}
}
}
加入解决方案后的代码如下:
(1)基础类定义。
function Person(age,name) {
this.name=name;
//调用方法判断验证
if(!this.checkAge(age)){
throw new Error("年龄必须在0-150之间");
}
this.age=age;
}
(2)添加年龄判断验证逻辑。
Person.prototype={
checkAge:function (age) {
if(0<age&&age<150){
return true;
}else {
return false;
}
}
}
(3)执行调用。
var p2=new Person(10,"小明");
alert(p2.age)
此外,我们还可以为name属性添加读取验证,当name为空时返回默认值,这同样通过扩展原型链实现。
相关代码如下:
Person.prototype["getName"]=function () {
return this.name||"我是默认的";
}
//调用
var p3=new Person(10,"");
alert(p3.getName())//结果为“我是默认的”
总结而言,当类定义为完全暴露型时,容易出现与实际业务不符的情况。虽然可以通过在原型上添加验证方法来补救,但这会导致类结构变得臃肿。
命名规范型封装
接着探讨第二种模式:通过命名规范区分私有与公有成员。
具体步骤如下:
(1)定义类,在类内部定义变量(包括私有和公有变量)并调用验证方法。
//用命名规范来区别私有和公有
function Person(name,age,email) {
//定义私有变量
this._name;//私有
this.setName(name);//只是方法的调用,方法中有验证,而不是在类中验证
this._age;//私有
this.setAge(age);
this.email=email;//公有的
}
(2)在类的原型上扩展赋值方法。
Person.prototype={//直接扩展至原型上,可以在本类的内部使用this调用
setName:function (name) {
this._name=name;
},
setAge:function (age) {
//需要做判断符号实际情况
if(age>0&&age<150){//验证不在类中,类不会变的臃肿
this._age=age;
}else {
throw new Error("年龄必须是在0到150范围内")
}
}
}
(3)实际应用。
var text1=new Person("测试",-10,"qq.com");
alert(text1._age)//-10 程序会报错 这是我们想要的(说明验证是对的)
var p2=new Person("测试2",10,"qq.cpm");
alert(p2._age)//程序正常运行 达到我们的目标
总结:在类定义时使用命名规范来区分私有和公有变量,并将验证和赋值方法扩展到原型链上,在类内部调用这些方法(通常有返回值),这样可以避免类变得臃肿。
闭包型封装
第三种模式:利用闭包实现封装。
这种方式类似于高级语言,在定义类时使用get和set方法来操作数据。
(1)定义一个基本类,包含变量及操作变量的方法。
function Person(name,age,email) {
//(1)声明变量和对变量进行操作的get和set方法
this.email=email;//公有变量
//get方法
this.getName=function (name){
return this.name;//为什么是this调用呢?请看set方法
}
this.getAge=function (age){
return this.age;
}
//set方法 这里相当于在类上的扩展
this.setName=function (name) {
this.name=name;//Person.prototype.name 这里写明了get中this的写法的来源
}
this.setAge=function (age) {
if(age>0&&age<150){
this.age=age;//Person.prototype.name 这里写明了get中this的写法的来源
}else {
throw new Error("年龄必须是在0到150范围内");
}
}
//(2)写一个构造函数 做初始化 实现闭包 确保set是在get之前的,不然get时会出现错误
this.init=function () {
this.setName(name);
this.setAge(age);
}
this.init();//显示调用
}
(2)实际应用。
var p=new Person("text",-10,"qq.com");
alert(p.age)//程序由于不符合实际而被阻断,符合要求
注意:这里还涉及额外的闭包写法,即var变量的方式。
var _sex="M";
this.getSex=function () {
return _sex;
}
this.setSex=function () {
_sex=sex;
}
总结:
(1)此处仅为函数和属性的简单封装,若业务需求复杂,需使用get和set方法,并配合构造函数实现初始化顺序的闭包,随后显式调用,确保set在get之前执行。
(2)闭包的实现依赖于get和set方法,通过this方式赋值时并未直接暴露,而是通过get和set方法实现闭包逻辑。
静态函数的实现方式
二、静态化实现
普通属性和函数作用于对象实例,而静态函数则定义在类本身之上。
第一种静态函数的写法:直接定义在类上。
(1)首先,定义一个简单的类,例如:
function Person(name,age) {
this.name=name;
this.age;age;
this.showName=function () {
alert(this.name);
}
}
(2)定义一个定义在类上的方法,即Person.add(类.函数)或(类.属性),例如:
Person.add=function (x,y) {
return x+y;
}
(3)实际应用。
alert(Person.add(10,20));//结果为30
总结:这种定义方式类似于高级语言中的静态类,用法与高级语言相同,可直接通过类进行调用。
第二种静态函数的写法:使用“类中类”的方式,使每个对象都拥有当前类中相同的属性和函数。注意:类中类的方式是一次性赋值的。
(1)类的定义格式如下:
var cat=(function () {
//私有静态属性
var AGE=1;
function add(x,y) {
return x+y;
}
return function () {//类中类 return返回的类中持有与上面类中相同的属性与函数 则共同的AGE和add称为静态属性和静态函数
this.AGE=AGE;
this.add=function (x,y) {
return add(x,y)
}
}
})()//实例化cat,实质是通过return实例化的
(2)实际应用。
alert(new cat().add(1,3))//4
alert(new cat().AGE)//1
总结:从上述代码格式可以看出,在一个类中定义私有属性和方法,以及一个返回可初始化本类私有静态属性和方法的类,我们称之为“类中类”。当我们实例化外层类时,实质上是通过该类内部的类return实例化的。
封装的主要优点:
(1)保护内部数据的完整性是封装的一大用途。 (2)使对象的重构变得轻松,(如果没有封装,你敢动正在运用的代码吗?)答案肯定是不敢的。 (3)降低模块间的耦合度。
封装的弊端:
(1)私有方法会变得难以进行单元测试。 (2)使用封装意味着需要处理更复杂的代码逻辑。 (3)最大的问题在于JavaScript中很难完美实现封装,除非运用自如,否则到处封装会使测试变得困难。
以上仅为初步学习理解,如有不当之处,恳请多多指正。
感兴趣的朋友可以使用在线HTML/CSS/JavaScript代码运行工具:http://tools.jb51.net/code/HtmlJsRun测试上述代码运行效果。
更多关于JavaScript相关内容感兴趣的读者可查看本站专题:《javascript面向对象入门教程》、《JavaScript错误与调试技巧总结》、《JavaScript数据结构与算法技巧总结》、《JavaScript遍历算法与技巧总结》及《JavaScript数学运算用法总结》
希望本文所述对大家JavaScript程序设计有所帮助。
JavaScript闭包与类(Class)的区别及适用场景对比
2026-11-05 15:48:57
JavaScript入门要学ES6吗?let与箭头函数等新特性解析
2026-11-05 14:49:16
JavaScript 8种数据类型详解:基本类型与引用类型的区别
2026-11-05 14:25:15
怎么在JavaScript中使用async/await优化Ajax异步流程
2026-11-05 13:56:15
JavaScript replace回调函数实现动态正则替换逻辑
2026-11-05 13:52:13
