JavaScript 中的“伪私有”与“真私有”:你以为的私有变量真的安全吗?

在前端开发中,我们经常看到变量名以下划线开头,比如 _title_count,这是一种常见的编码约定,用来表示这个变量是"受保护的",不应该被外部直接访问。

但你有没有想过:

这些"下划线变量"真的是私有的吗?它们真的不能被修改吗?

本文将带你一步步揭开 JavaScript 中"伪私有"与"真私有"的面纱,深入理解变量作用域、闭包机制以及如何实现更安全的数据封装。


🧠 JavaScript 为什么没有"真正的私有"?

JavaScript 在设计之初并没有像 Java、C++ 那样的 private 关键字来限制类成员的访问权限。直到 ES6 引入了类(class)语法,才支持了使用 # 前缀定义真正的私有字段(如 #title),但在那之前,开发者只能通过作用域闭包来模拟私有性。

这就导致了一个现象:很多开发者误以为以下划线开头的变量就是"私有变量",但实际上它只是个"伪私有"。


🔍 什么是"伪私有变量"?

来看一个典型的构造函数写法:

javascript 复制代码
function Book(title) {
    const _title = title;

    this.getTitle = function () {
        return _title;
    };
}

在这个例子中:

  • _title 是用 const 声明的局部变量,没有挂到 this 上。
  • 外部无法通过 book._title 访问。
  • 看起来像是"私有变量",但它真的不可见或不可改吗?

⚠️ 实际情况:

  • 如果你在控制台打印出 book 对象,可能会在闭包中看到 _title 的值。
  • 在某些调试器中,甚至可以直接修改它的值。
  • 所以,这种变量并不是完全私有的 ------ 它只是一个"受保护的命名约定"。

🔒 那什么才是"真正的私有变量"?

真正的私有变量应该满足两个条件:

  1. 外部无法直接访问
  2. 只能通过特定的方法间接操作

要实现这一点,我们需要借助 闭包(Closure)

看下面这段代码:

javascript 复制代码
function Book(title) {
    let count = 0; // ✅ 真正的私有变量

    const _title = title;

    this.getTitle = function () {
        return _title;
    };

    this.increaseCount = function () {
        count++;
    };

    this.getCount = function () {
        return count;
    };
}

在这个例子中:

  • count 是用 let 声明的局部变量,没有暴露给外部。
  • 外部既不能访问也不能修改 count,除非调用公开方法(如 increaseCount()getCount())。
  • 这种方式利用了闭包的特性,确保变量只存在于内部上下文中,从而实现了"真正的私有性"。

📌 类比理解:办公室里的抽屉和保险柜

写法 类比 安全性
this.title = title 文件堆在办公桌上 ❌ 完全不安全
const _title = title 文件锁在抽屉里,钥匙你自己拿着 ⚠️ 可能被撬开
let count = 0 文件锁在保险柜里,只有你有钥匙 ✅ 完全安全

🤔 为什么推荐使用闭包来封装数据?

闭包之所以强大,是因为它可以:

  • 创建独立的作用域空间
  • 保持变量的生命周期
  • 实现对外隐藏、对内开放的封装效果

这正是现代模块化开发中推崇的设计理念之一。

例如,我们可以这样封装一个计数器模块:

javascript 复制代码
function createCounter() {
    let count = 0;

    return {
        increment() {
            count++;
        },
        getCount() {
            return count;
        }
    };
}

const counter = createCounter();
counter.increment();
console.log(counter.getCount()); // 输出 1
console.log(counter.count);      // 输出 undefined

在这个例子中,外部根本无法访问 count,只能通过返回的对象方法进行操作 ------ 这就是真正的封装。


💡 Demo一下:"伪私有"和"真私有"的区别

运行以下代码看看输出结果:

javascript 复制代码
function Book() {
    const _name = "JavaScript";
    let count = 0;

    this.getName = function () {
        return _name;
    };

    this.inc = function () {
        count++;
    };

    this.getCount = function () {
        return count;
    };
}

const b = new Book();

console.log(b._name);     // undefined
console.log(b.count);     // undefined
console.log(b.getName()); // JavaScript

b.inc();
console.log(b.getCount()); // 1

虽然 _namecount 都不在 this 上,但通过方法仍然可以访问和操作。这说明:

  • _name 是"伪私有"
  • count 是"真私有"

🛠 如何判断一个变量是否真正私有?

你可以从以下几个方面判断:

判断标准 描述
是否可以通过对象属性访问 不能访问则可能是私有
是否可以在控制台查看 如果看不到,则更接近私有
是否可以通过闭包操作 如果只能通过方法修改,则是真正的私有
是否属于闭包变量 属于闭包的变量更安全

JavaScript 中没有"真正的私有"关键字,但你可以通过闭包来模拟私有性。_title 是一种"受保护的变量",适合封装;而 let count = 0 才是真正的私有变量,外部完全看不见。


📘 推荐阅读&建议学习

如果你希望进一步掌握闭包、作用域链等进,建议继续学习:

  • 《你不知道的 JavaScript》系列(上卷、中卷)
  • MDN 文档中的 闭包
  • 使用 IIFE(立即执行函数表达式)封装模块
  • ES6+ 中的私有类字段(#field
相关推荐
sorryhc19 分钟前
【AI解读源码系列】ant design mobile——Space间距
前端·javascript·react.js
页面仔Dony1 小时前
绝对路径与相对路径的区别及作用
前端·javascript
林太白1 小时前
Zustand状态库(简洁、强大、易用的React状态管理工具)
前端·javascript·react.js
YuJie2 小时前
vue3 无缝滚动
前端·javascript·vue.js
小野鲜2 小时前
前端打开新的独立标签页面,并且指定标签页的大小,管理新标签页面的打开和关闭(包含源码和使用文档)
前端·javascript
十五_在努力2 小时前
参透 JavaScript —— 解析浅拷贝、深拷贝及手写实现
前端·javascript
王六岁2 小时前
JavaScript值和引用详解:从栈堆内存到面试实战
javascript·面试
ikonan3 小时前
译:Chrome DevTools 实用技巧和窍门清单
前端·javascript
小高0074 小时前
⚡️ Vue 3.5 正式发布:10× 响应式性能、SSR 水合黑科技、告别 .value!
前端·javascript·vue.js
葡萄城技术团队4 小时前
【前沿解析】JavaScript 的未来,将迎来哪些新特性?
javascript