JavaScript内核与高级编程之：`JavaScript`的`Private Fields`：其在 `JavaScript` 类中的实现与语法糖。

admin
Tue, 26 Aug 2025 11:35:42 +0000
Javascript, fields, javascript, private

各位观众老爷们，大家好！今天咱们来聊聊 JavaScript 里那些“羞答答的玫瑰静悄悄地开”的私有字段（Private Fields）。这玩意儿，说实话，没它也能活，但有了它，代码的安全性瞬间提升一个档次，逼格也跟着嗖嗖往上涨。 先别急着说：“私有？JavaScript 不是啥都能访问吗？” 以前确实是这样，搞得很多开发者只能用 _ 或者 __ 开头的变量来暗示“这是私有的，别碰我！”，但实际上，谁想碰就能碰，毫无约束力。 好在，ES2019（也就是 ES10）引入了真正的私有字段，这才让 JavaScript 的类有了点“隐私”可言。 为什么要用私有字段？ 想象一下，你写了一个复杂的类，里面有一些内部状态，这些状态的改变必须经过特定的方法才能保证类的正常运行。如果外部代码可以随意修改这些内部状态，那你的类就很容易崩坏，就像辛辛苦苦搭建的乐高模型，被熊孩子一脚踹散了。 私有字段就像是给这些内部状态加上了一层保护罩，只有类自身才能访问和修改，外部代码根本摸不着，从而保证了类的稳定性和可靠性。 私有字段的语法 JavaScript 的私有字段使用 # 符号来声明。 注意，这个#可不是随便 …

继续阅读“JavaScript内核与高级编程之：`JavaScript`的`Private Fields`：其在 `JavaScript` 类中的实现与语法糖。”

The post JavaScript内核与高级编程之：`JavaScript`的`Private Fields`：其在 `JavaScript` 类中的实现与语法糖。 first appeared on 智猿学院-前后端，数据库，人工智能，云计算等领域前沿技术讲座.

各位观众老爷们，大家好！今天咱们来聊聊 JavaScript 里那些“羞答答的玫瑰静悄悄地开”的私有字段（Private Fields）。这玩意儿，说实话，没它也能活，但有了它，代码的安全性瞬间提升一个档次，逼格也跟着嗖嗖往上涨。

先别急着说：“私有？JavaScript 不是啥都能访问吗？” 以前确实是这样，搞得很多开发者只能用 _ 或者 __ 开头的变量来暗示“这是私有的，别碰我！”，但实际上，谁想碰就能碰，毫无约束力。

好在，ES2019（也就是 ES10）引入了真正的私有字段，这才让 JavaScript 的类有了点“隐私”可言。

为什么要用私有字段？

想象一下，你写了一个复杂的类，里面有一些内部状态，这些状态的改变必须经过特定的方法才能保证类的正常运行。如果外部代码可以随意修改这些内部状态，那你的类就很容易崩坏，就像辛辛苦苦搭建的乐高模型，被熊孩子一脚踹散了。

私有字段就像是给这些内部状态加上了一层保护罩，只有类自身才能访问和修改，外部代码根本摸不着，从而保证了类的稳定性和可靠性。

私有字段的语法

JavaScript 的私有字段使用 # 符号来声明。 注意，这个#可不是随便加的，它是语法的一部分，必须紧跟在字段名之前，并且字段必须在类的内部声明。

class MyClass {
  #privateField = 42; // 声明一个私有字段

  constructor() {
    console.log(this.#privateField); // 在类的内部访问私有字段
  }

  getPrivateFieldValue() {
    return this.#privateField; // 通过方法访问私有字段
  }
}

const myInstance = new MyClass(); // 输出 42
console.log(myInstance.getPrivateFieldValue()); // 输出 42
// console.log(myInstance.#privateField); // 报错：私有字段 '#privateField' 必须在封闭类中声明

可以看到，在类的外部直接访问 #privateField 会报错。

私有方法

除了私有字段，JavaScript 还支持私有方法，语法也很简单，就是在方法名前面加上 #。

class MyClass {
  #privateMethod() {
    console.log("这是一个私有方法");
  }

  publicMethod() {
    this.#privateMethod(); // 在类的内部调用私有方法
  }
}

const myInstance = new MyClass();
myInstance.publicMethod(); // 输出 "这是一个私有方法"
// myInstance.#privateMethod(); // 报错：私有字段 '#privateMethod' 必须在封闭类中声明

和私有字段一样，私有方法也只能在类的内部调用，外部无法直接访问。

私有字段的限制

必须在类中声明： 私有字段必须在类的内部声明，不能在类的外部添加。

只能在类中访问： 私有字段只能在类的内部访问，外部无法直接访问。

同一个类的不同实例共享私有字段的定义： 虽然每个实例都有自己的私有字段的值，但它们的定义是共享的，这意味着你不能在不同的实例中动态添加或删除私有字段。

不能使用 delete 删除私有字段： 试图使用 delete 删除私有字段会报错。

私有字段 vs. 闭包

在私有字段出现之前，很多开发者使用闭包来模拟私有性。 闭包是一种函数，它可以访问并操作函数外部的变量，即使外部函数已经执行完毕。

function createCounter() {
  let count = 0; // 这是一个闭包变量

  return {
    increment: function() {
      count++;
    },
    decrement: function() {
      count--;
    },
    getCount: function() {
      return count;
    }
  };
}

const counter = createCounter();
counter.increment();
counter.increment();
console.log(counter.getCount()); // 输出 2
// console.log(counter.count); // undefined，无法直接访问 count

在这个例子中，count 变量被 increment、decrement 和 getCount 函数所闭包，外部无法直接访问 count，从而实现了类似私有变量的效果。

私有字段和闭包的比较

特性 | 私有字段 (#) | 闭包
语法 | #privateField | 使用函数作用域和变量
性能 | 通常比闭包更高效 | 可能有性能损耗（尤其是在大量创建实例时）
可读性 | 更加清晰，易于理解和维护 | 代码可能比较复杂，不易理解
适用场景 | 类的内部状态需要保护时 | 更加灵活，可以用于各种场景
浏览器兼容性 | 现代浏览器支持，需要 Babel 转译才能兼容旧版本 | 广泛支持
调试 | 调试器可以识别私有字段 | 调试可能比较困难

总的来说，私有字段在语法和性能上都优于闭包，是实现类私有性的首选方案。 但是，闭包仍然是一种非常有用的技术，在很多场景下都有其独特的优势。

私有字段的实际应用

保护内部状态： 比如，在一个银行账户类中，余额应该是一个私有字段，只能通过存款和取款方法来修改，防止外部代码直接修改余额，导致账户出现问题。

控制访问权限： 比如，在一个配置类中，某些配置项是敏感信息，应该设置为私有字段，只有管理员才能访问和修改。

隐藏实现细节： 比如，在一个缓存类中，缓存的存储方式是内部实现细节，应该设置为私有字段，防止外部代码依赖于特定的存储方式，导致代码难以维护。

高级用法：私有字段与 WeakMap

虽然 # 语法提供了原生的私有字段支持，但它也有一些限制。 例如，你不能在类的外部访问私有字段，即使你已经获得了类的实例。 有时候，你可能需要在类的外部进行一些特殊的操作，比如序列化或反序列化。

为了解决这些问题，可以使用 WeakMap 来模拟私有字段。 WeakMap 是一种特殊的 Map，它的键必须是对象，而且当键对象被垃圾回收时，WeakMap 中的对应条目也会被自动删除。

const _privateData = new WeakMap();

class MyClass {
  constructor(data) {
    _privateData.set(this, { value: data });
  }

  getValue() {
    return _privateData.get(this).value;
  }

  setValue(newValue) {
    _privateData.get(this).value = newValue;
  }
}

const myInstance = new MyClass("Hello");
console.log(myInstance.getValue()); // 输出 "Hello"

// 在类的外部访问和修改私有数据（不推荐，但可以实现）
const privateData = _privateData.get(myInstance);
privateData.value = "World";
console.log(myInstance.getValue()); // 输出 "World"

在这个例子中，我们使用 WeakMap 来存储每个 MyClass 实例的私有数据。 WeakMap 的键是 MyClass 的实例，值是一个包含私有数据的对象。 这样，我们就可以在类的外部通过 WeakMap 来访问和修改私有数据。

注意： 这种方法虽然可以实现类似私有字段的效果，但它本质上仍然是一种模拟，并不能真正阻止外部代码访问私有数据。 只有使用 # 语法才能实现真正的私有性。

语法糖： 类装饰器 (Class Decorators)

虽然 JavaScript 原生支持私有字段，但有时候我们可能需要对类进行一些额外的处理，比如添加日志、验证参数、缓存结果等等。 这时候，可以使用类装饰器来简化代码。

类装饰器是一种特殊的函数，它可以用来修改类的行为。 类装饰器使用 @ 符号来标记，并放在类声明之前。

function logClass(constructor) {
  return class extends constructor {
    constructor(...args) {
      super(...args);
      console.log(`创建了一个 ${constructor.name} 类的实例`);
    }
  };
}

@logClass
class MyClass {
  constructor(name) {
    this.name = name;
  }
}

const myInstance = new MyClass("Alice"); // 输出 "创建了一个 MyClass 类的实例"

在这个例子中，logClass 是一个类装饰器，它会在创建 MyClass 类的实例时打印一条日志。

私有字段与 TypeScript

TypeScript 提供了更强大的私有性控制机制。 TypeScript 使用 private 关键字来声明私有成员，这些成员只能在类的内部访问，外部无法访问。

class MyClass {
  private privateField: number = 42;

  constructor() {
    console.log(this.privateField);
  }

  getPrivateFieldValue(): number {
    return this.privateField;
  }
}

const myInstance = new MyClass();
console.log(myInstance.getPrivateFieldValue());
// console.log(myInstance.privateField); // 报错：属性“privateField”为私有属性，只能在类“MyClass”中访问

TypeScript 的 private 关键字提供了一种更简洁、更易于理解的私有性声明方式。 重要的是，TypeScript 的私有性是在编译时检查的，这意味着如果你试图在类的外部访问私有成员，编译器会报错。

总结

JavaScript 的私有字段是保护类内部状态的重要工具。 它们可以防止外部代码随意修改类的内部状态，从而保证类的稳定性和可靠性。 虽然私有字段有一些限制，但它们仍然是实现类私有性的首选方案。

使用 # 语法声明私有字段和私有方法。

私有字段只能在类的内部访问。

可以使用 WeakMap 来模拟私有字段，但这种方法并不能真正阻止外部代码访问私有数据。

可以使用类装饰器来简化代码。

TypeScript 提供了更强大的私有性控制机制。

希望今天的讲座能帮助大家更好地理解 JavaScript 的私有字段。 记住，保护好你的内部状态，让你的代码更加健壮！ 感谢各位的收看，咱们下期再见！