JavaScript 类的封装与静态方法实现详解

作者:半夏

时间:2026-10-23

浏览:239

本文详解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程序设计有所帮助。

实用工具推荐

电子包浆

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

使用 ›
图片压缩

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

使用 ›
相关文章