创见博客
通过闭包实现的私有变量与Class的私有变量
七崽爱吃小饼干2026/01/20阅读 0专栏 JavaScript

一、Class 私有属性 ≠ 闭包实现

ES6 Class 的私有属性(# 开头的属性)是 JS 语言层面原生支持的特性,并非通过闭包实现;而我们之前用闭包实现的“私有变量”,是开发者利用语言特性(作用域+闭包)手动模拟的封装效果。

两者虽然都能实现“私有性”,但底层原理、表现形式、使用限制完全不同。

二、对比:闭包模拟私有变量 vs Class 原生私有属性

1. 闭包模拟私有变量(手动封装)

这是 ES6 之前没有 Class 私有属性时的“变通方案”,核心是利用函数作用域和闭包,让变量只在函数内部可访问,外部无法直接获取。

javascript
// 闭包模拟私有变量
function Person(name) {
  // 闭包私有变量:外部无法直接访问,没有挂载到this实例上
  let _age = 18; // 约定用下划线标识私有(仅规范,非语法限制)
  
  this.name = name; // 公有属性
  
  // 闭包方法:能访问 _age
  this.getAge = function() {
    return _age;
  };
  
  this.setAge = function(newAge) {
    if (newAge > 0) _age = newAge;
  };
}

const p = new Person("张三");
console.log(p.name); // 张三(公有属性可访问)
console.log(p._age); // undefined(闭包变量外部拿不到)
console.log(p.getAge()); // 18(通过方法间接访问)

核心特点:

  • _age 存在于构造函数的作用域中,只有内部方法(getAge/setAge)能访问;
  • 本质是闭包保留了构造函数作用域的变量,外部无法直接访问;
  • 下划线 _age 只是开发者的“命名约定”,并非语法层面的私有(如果强行通过 hack 手段,理论上能拿到)。

2. Class 原生私有属性(语言层面封装)

ES2022 引入的 # 开头的私有属性,是 JS 引擎原生支持的特性,和闭包无关。

javascript
// Class 原生私有属性
class Person {
  // 私有属性:必须用 # 声明,且只能在类内部访问
  #age = 18;
  // 公有属性
  name;

  constructor(name) {
    this.name = name;
  }

  // 类内部方法可访问私有属性
  getAge() {
    return this.#age;
  }

  setAge(newAge) {
    if (newAge > 0) this.#age = newAge;
  }
}

const p = new Person("李四");
console.log(p.name); // 李四(公有属性可访问)
console.log(p.#age); // 报错!SyntaxError: Private field '#age' must be declared in an enclosing class
console.log(p.getAge()); // 18(通过方法间接访问)

核心特点:

  • #age 是语法层面的私有属性,JS 引擎会直接禁止外部访问(哪怕是 hack 手段也无法绕过);
  • 私有属性属于实例本身(挂载在 this 上),而非闭包保留的作用域变量;
  • 无需依赖函数作用域/闭包,是 Class 语法的原生能力。

三、关键区别总结表

特性闭包模拟私有变量Class 原生私有属性(#)
实现原理依赖函数作用域 + 闭包JS 引擎原生支持(语法层面)
外部访问限制软限制(约定俗成,可 hack)硬限制(语法禁止,无法访问)
变量归属构造函数作用域(非实例本身)类的实例本身(挂载在 this 上)
兼容性所有 JS 环境都支持ES2022 及以上环境支持

总结

  1. 核心区别:Class 私有属性(#)是 JS 语言原生特性,和闭包无关;闭包是手动利用作用域特性模拟的私有性,属于“编程技巧”而非“语法特性”;
  2. 使用场景:现代开发中优先用 Class 原生私有属性(#)实现封装(更规范、更安全);闭包模拟私有变量多用于兼容旧环境,或非 Class 场景(如函数式编程);
  3. 本质相通点:两者最终都能实现“变量私有化”,但底层原理、安全性、使用方式完全不同。
评论
0/100