js Class中 静态属性和私有属性使用场景得的区别

静态属性 / 方法(static)和私有属性 / 方法(# 前缀)是 Class 语法中两个核心的封装特性,但设计目标、访问范围、使用场景完全不同。下面用「核心区别 + 场景对比 + 实战案例」的方式讲清楚,帮你彻底区分。

一、核心区别(一张表看懂)

维度 静态属性 / 方法(static) 私有属性 / 方法(# 前缀)
归属 属于「类本身」,而非实例(所有实例共享同一静态成员) 属于「实例」,但仅能在类内部访问(每个实例有独立的私有成员)
访问范围 类外部可通过 类名.静态成员 访问,实例无法访问 仅类内部可访问,类外部 / 实例都无法直接访问
设计目标 复用「不依赖实例状态」的通用逻辑 / 常量 隐藏「内部实现细节」,避免外部篡改 / 误操作
继承性 子类可通过 子类名.静态成员 继承(可重写) 私有成员不能被继承(子类无法访问父类的 # 成员)
内存占用 全局唯一(类加载时初始化,仅占一份内存) 每个实例独立占用内存(实例创建时初始化)

二、静态属性 / 方法(static):类级别的通用能力

核心理解

static 修饰的成员属于「类」,不是「实例」------ 你可以把类想象成一个「工具库」,静态成员就是工具库里直接能用的工具,不需要先 "打开工具库(实例化)"。

典型使用场景

场景 1:工具函数 / 常量(无需实例化,直接调用)

适用于「不依赖实例状态」的通用逻辑,比如数学计算、日期格式化、常量定义。

典型使用场景

场景 1:工具函数 / 常量(无需实例化,直接调用)

适用于「不依赖实例状态」的通用逻辑,比如数学计算、日期格式化、常量定义。

javascript

运行

javascript 复制代码
class TimeUtil {
  // 静态常量:通用配置
  static ONE_DAY = 24 * 60 * 60 * 1000; // 一天的毫秒数
  
  // 静态方法:日期格式化(不依赖任何实例属性)
  static formatDate(date, format = 'YYYY-MM-DD') {
    const d = new Date(date);
    return format
      .replace('YYYY', d.getFullYear())
      .replace('MM', String(d.getMonth() + 1).padStart(2, '0'))
      .replace('DD', String(d.getDate()).padStart(2, '0'));
  }
}

// 直接用类名调用,无需 new
console.log(TimeUtil.ONE_DAY); // 86400000
console.log(TimeUtil.formatDate(new Date())); // 2025-12-05

// 实例无法访问静态成员
const util = new TimeUtil();
console.log(util.ONE_DAY); // undefined

场景 2:单例模式(限制类只能实例化一次)

通过静态方法控制实例的创建,确保全局只有一个实例(如全局状态管理、弹窗管理器)。

javascript

运行

javascript 复制代码
class ModalManager {
  static instance; // 静态属性:存储唯一实例
  constructor() {
    this.modals = []; // 实例属性:管理所有弹窗
  }

  // 静态方法:获取唯一实例
  static getInstance() {
    if (!ModalManager.instance) {
      ModalManager.instance = new ModalManager();
    }
    return ModalManager.instance;
  }

  // 实例方法:添加弹窗
  addModal(modal) {
    this.modals.push(modal);
  }
}

// 两次调用都返回同一个实例
const manager1 = ModalManager.getInstance();
const manager2 = ModalManager.getInstance();
console.log(manager1 === manager2); // true

场景 3:工厂方法(统一创建实例)

用静态方法封装实例化逻辑,对外提供更友好的创建接口(比如参数校验、默认值处理)。

javascript

运行

javascript 复制代码
class User {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  // 静态工厂方法:创建合法的用户实例
  static createUser(userInfo) {
    // 前置校验:避免创建非法实例
    if (!userInfo.name || userInfo.age < 0) {
      throw new Error('用户名不能为空,年龄不能为负数');
    }
    return new User(userInfo.name, userInfo.age);
  }
}

// 通过工厂方法创建实例(无需关心内部校验逻辑)
const user = User.createUser({ name: '张三', age: 20 });
// 错误示例:会抛出异常
// const invalidUser = User.createUser({ name: '', age: -1 });

三、私有属性 / 方法(# 前缀):实例级别的隐私保护

核心理解

# 修饰的成员属于「实例」,但被严格隔离在类内部 ------ 就像手机的内部零件,用户只能用手机的功能(公开方法),但不能直接拆零件(私有成员),避免外部篡改导致逻辑出错。

典型使用场景

场景 1:保护核心状态(避免外部随意修改)

比如用户的密码、订单的状态码,只能通过类的公开方法修改,确保修改逻辑可控。

javascript

运行

javascript 复制代码
class Order {
  #status; // 私有属性:订单状态(0-待支付,1-已支付,2-已取消)
  constructor(orderId) {
    this.orderId = orderId;
    this.#status = 0; // 初始状态:待支付
  }

  // 公开方法:修改状态(带校验逻辑)
  pay() {
    if (this.#status === 0) {
      this.#status = 1;
      console.log(`订单${this.orderId}支付成功`);
    } else {
      console.log(`订单${this.orderId}状态异常,无法支付`);
    }
  }

  // 公开方法:获取状态(只读,不暴露原始值)
  getStatusText() {
    const statusMap = { 0: '待支付', 1: '已支付', 2: '已取消' };
    return statusMap[this.#status];
  }
}

const order = new Order('123456');
order.pay(); // 订单123456支付成功
console.log(order.getStatusText()); // 已支付

// 外部无法直接修改私有属性(报错)
// order.#status = 2; // SyntaxError

场景 2:封装内部辅助逻辑(避免暴露无关方法)

类的内部计算、校验等辅助逻辑,不需要对外暴露,用私有方法封装,让公开接口更简洁。

javascript

运行

javascript 复制代码
class FormValidator {
  constructor(formData) {
    this.formData = formData;
  }

  // 公开方法:对外提供的核心能力
  validate() {
    return this.#validateRequired() && this.#validateFormat();
  }

  // 私有方法:校验必填项(内部逻辑,外部无需关心)
  #validateRequired() {
    const requiredFields = ['username', 'phone'];
    return requiredFields.every(field => this.formData[field]);
  }

  // 私有方法:校验格式(内部逻辑)
  #validateFormat() {
    const phoneReg = /^1[3-9]\d{9}$/;
    return phoneReg.test(this.formData.phone);
  }
}

const validator = new FormValidator({ username: '张三', phone: '13800138000' });
console.log(validator.validate()); // true

// 外部无法调用私有方法(报错)
// validator.#validateRequired(); // SyntaxError

场景 3:避免命名冲突(多人协作 / 继承场景)

私有成员不会被外部 / 子类覆盖,确保类的内部逻辑稳定。

javascript

运行

javascript 复制代码
class Parent {
  #commonMethod() {
    console.log('父类私有方法');
  }

  callPrivate() {
    this.#commonMethod(); // 调用父类私有方法
  }
}

class Child extends Parent {
  // 子类可以定义同名的私有方法,不会和父类冲突
  #commonMethod() {
    console.log('子类私有方法');
  }

  callChildPrivate() {
    this.#commonMethod(); // 调用子类私有方法
  }
}

const child = new Child();
child.callPrivate(); // 父类私有方法
child.callChildPrivate(); // 子类私有方法

四、核心总结:怎么选?

需求场景 选 static 选 # 私有
不需要实例化,直接用的工具 / 常量
控制实例的创建逻辑(工厂 / 单例)
保护实例的核心状态,避免外部篡改
封装内部辅助逻辑,简化公开接口
逻辑不依赖实例状态(无 this)
逻辑依赖实例状态,但需隐藏

一句话记忆

  • static:给「类」加工具,所有实例共享,外部可访问;
  • # 私有:给「实例」加隐私,仅类内部可用,外部碰不到。

实战小技巧

  • 如果你写的逻辑「不需要 new 就能用」,优先用 static;
  • 如果你写的逻辑「需要保护,不让外部改」,优先用 # 私有;
  • 大部分业务场景中,static 用于「通用工具」,# 私有用于「实例隐私」,两者也可结合(比如静态方法调用私有方法,但需先创建实例)。

比如结合案例:

javascript

运行

javascript 复制代码
class UserService {
  static #API_BASE = '/api/user'; // 静态私有常量:接口地址(类级别的隐私)
  
  // 静态方法:调用用户接口(工具能力 + 私有常量)
  static async getUserInfo(userId) {
    const res = await fetch(`${this.#API_BASE}/${userId}`);
    return res.json();
  }
}

// 调用静态方法,无需实例化,且接口地址被私有保护
UserService.getUserInfo(123);
相关推荐
名字越长技术越强1 小时前
CSS之选择器|弹性盒子模型
前端·css
用户93816912553601 小时前
VUE3项目--路由切换时展示进度条
前端
小王码农记1 小时前
vue2中table插槽新语法 v-slot
前端·vue.js
前端婴幼儿1 小时前
前端直接下载到本地(实时显示下载进度)
前端
三小河1 小时前
前端 Class 语法从 0 开始学起
前端
hjt_未来可期1 小时前
js实现复制、粘贴文字
前端·javascript·html
米诺zuo1 小时前
Next.js 路由与中间件
前端
小明记账簿_微信小程序1 小时前
webpack实用配置dev--react(一)
前端
小帆聊前端2 小时前
JS this取值深度解读
前端·javascript