千家信息网

JS私有属性的实现方式有哪些

发表于:2026-11-01 作者:千家信息网编辑
千家信息网最后更新 2026年11月01日,这篇文章主要为大家展示了"JS私有属性的实现方式有哪些",内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下"JS私有属性的实现方式有哪些"这篇文章吧。class
千家信息网最后更新 2026年11月01日JS私有属性的实现方式有哪些

这篇文章主要为大家展示了"JS私有属性的实现方式有哪些",内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下"JS私有属性的实现方式有哪些"这篇文章吧。

class 是创建对象的模版,由一系列属性和方法构成,用于表示对同一概念的数据和操作。

有的属性和方法是对外的,但也有的是只想内部用的,也就是私有的,那怎么实现私有属性和方法呢?

不知道大家会怎么实现,我梳理了下,我大概用过 6 种方式,我们分别来看一下:

_prop

区分私有和公有最简单的方式就是加个下划线 _,从命名上来区分。

比如:

class Dong {    constructor() {        this._name = 'dong';        this._age = 20;        this.friend = 'guang';    }    hello() {        return 'I\'m ' + this._name + ', '  + this._age + ' years old';    }}const dong = new Dong();console.log(dong.hello());

这里的 Dong 就有私有属性 _name、_age,公有属性 friend。

但是这种方式只是一种命名规范,告诉开发者这个属性、方法是私有的,不要调用,但终究不是强制的,如果别人要用也阻止不了。

不过这种方式用的还是挺多的,历史比较悠久。

那怎么能基于这种规范实现真正的私有呢?这就要用到 Proxy 了:

Proxy

Proxy 可以定义目标对象的 get、set、Object.keys 的逻辑,可以在这一层做一下判断,如果是下划线 _ 开头就不让访问,否则就可以访问。

比如还是这个 class:

class Dong {    constructor() {        this._name = 'dong';        this._age = 20;        this.friend = 'guang';    }    hello() {        return 'I\'m ' + this._name + ', '  + this._age + ' years old';    }}const dong = new Dong();

我们不直接调用它的对象的属性方法了,而是先用一层 Proxy 来约束下 get、set、getKeys 的行为:

const dong = new Dong(); const handler = {    get(target, prop) {        if (prop.startsWith('_')) {            return;        }        return target[prop];   },   set(target, prop, value) {    if (prop.startsWith('_')) {        return;     }     target[prop] = value;   },   ownKeys(target, prop) {      return Object.keys(target).filter(key => !key.startsWith('_'))   }, } const proxy = new Proxy(dong, handler)

我们通过 new Proxy 来给 dong 定义了 get、set、ownKeys 的 handler:

  • get: 如果以下划线 _ 开头就返回空,否则返回目标对象的属性值 target[prop]

  • set: 如果以下划线 _ 开头就直接返回,否则设置目标对象的属性值

  • ownKeys: 访问 keys 时,过滤掉目标对象中下划线开头的属性再返回

这样就实现了下划线开头的属性的私有化:

我们测试下:

const proxy = new Proxy(dong, handler)for (const key of Object.keys(proxy)) {    console.log(key, proxy[key])}

确实,这里只打印了公有属性的方法,而下划线开头的那两个属性没有打印。

我们基于 _prop 这种命名规范实现了真正的私有属性!

再调用下方法试试:

咋是 undefined 了?

因为 proxy.hello 方法的 this 也是指向 proxy 的,也会受限制,所以要再做下处理:

如果用的是方法,那就给它绑定 this 为目标对象。

这样 hello 方法就可以访问到那些 _ 开头的私有属性了:

我们通过 Proxy 给下划线的命名规范实现了真正的私有属性,但是要定义一层 Proxy 比较麻烦,有没有不定义 Prxoy 的方式呢?

确实有,比如 Symbol:

Symbol

Symbol 是 es2015 添加的一个 api,用于创建唯一的值。基于这个唯一的特性,我们就可以实现私有属性。

比如这样:

const nameSymbol = Symbol('name');const ageSymbol = Symbol('age');class Dong {    constructor() {        this[nameSymbol] = 'dong';        this[ageSymbol] = 20;    }    hello() {        return 'I\'m ' + this[nameSymbol] + ', '  + this[ageSymbol] + ' years old';    }}const dong = new Dong();

我们不再用 name 和 age 作为私有属性名了,而是用 Symbol 生成唯一的值来作为名字。

这样外面因为拿不到属性名,就没法取到对应的属性值:

这种方式比 Proxy 的方式更简单一些,也是用的很多的一种实现私有属性的方式。

如果想暴露出去,可以定义个 get 方法:

但是这种私有属性是真的没法访问么?

不是的,有一个 api 叫做 Object.getOwnPropertySymbols,可以取到对象的所有 Symbols 属性,然后就可以拿到属性值了:

所以说这种方式只是 Object.keys 取不到对应的属性而已,不如 Proxy 那种方式完善。

那不用 Proxy 的方式,还比有没有 Symbol 更完善的呢?

那可以试试这种:

WeakMap

外面可以访问到属性和方法是因为我们把它挂到了 this 上,那不挂到 this 上外面不就访问不到了么?

比如用一个 Map 保存私有属性:

const privateFields = new Map();class Dong {    constructor() {        privateFields.set('name', 'dong');        privateFields.set('age', 20);    }    hello() {        return 'I\'m ' + privateFields.get('name') + ', '  + privateFields.get('name') + ' years old';    }}

我们测试下:

这样貌似可以,但不知道大家有没有发现其中的问题:

  • 所有对象都用同一个 Map,之间相互影响

  • 对象销毁了这个 Map 依然存在

怎么解决这个问题呢?

不知道大家用没用过 WeakMap,它的特性是只能用对象作为 key,对象销毁,这个键值对就销毁。

完美解决了上面两个问题:

  • 因为是用对象作为 key 的,那不同的对象是放在不同的键值对上的,相互没影响

  • 对象销毁的时候,对应的键值对就销毁,不需要手动管理

貌似是很完美,我们实现下:

const dongName = new WeakMap();const dongAge = new WeakMap();const classPrivateFieldSet = function(receiver, state, value) {    state.set(receiver, value);}const classPrivateFieldGet = function(receiver, state) {    return state.get(receiver);}class Dong {    constructor() {        dongName.set(this, void 0);        dongAge.set(this, void 0);        classPrivateFieldSet(this, dongName, 'dong');        classPrivateFieldSet(this, dongAge, 20);    }    hello() {        return 'I\'m ' + classPrivateFieldGet(this, dongName) + ', '  + classPrivateFieldGet(this, dongAge) + ' years old';    }}

每个属性定义了一个 WeakMap 来维护,key 为当前对象,值为属性值,get 和 set 使用 classPrivateFieldSet 和 classPrivateFieldGet 这两个方法,最终是通过从 WeakMap 中存取的。

在构造器里初始化下当前对象对应的属性值,也就是 dongName.set(this, void 0),这里的 void 0 的返回值是 undefined,一个意思。

测试下:

哇,通过 WeakMap 也能实现私有属性!

不过这里的 classPrivateFieldGet 没必要定义吧,直接 xxMap.get 不就行么?

确实,包一层的目的是为了可以加一些额外的逻辑,这里也可以直接从 weakMap 取。

但这样写起来也很麻烦呀,有没有更简单的方式呢?

能不能设计一种语法糖,它自动编译成这种方式呢?

想的没错,确实有这种语法糖:

#prop

现在有一个私有属性的 es 草案,可以通过 # 的方式来标识私有属性和方法。

比如这样:

class Dong {    constructor() {        this.#name = 'dong';        this.#age = 20;        this.friend = 'guang';    }    hello() {        return 'I\'m ' + this.#name + this.#age + 'years old';    }}

这里的 name 和 age 都是私有的,而 friend 是公有的。

这种新语法 JS 引擎没那么快支持,但是可以通过 babel 或者 ts 编译器来编译成低版本语法的方式来提前用。

比如 babel 有 @babel/proposal-private-property-in-object 的插件,它可以实现这种语法的编译:

babel 就是把 #prop 编译成上面那种 WeakMap 的方式来实现的。

这个插件在 @babel/preset-env 的预设里,会自动引入:

除了 babel,ts 里也可以直接用这种语法:

也是会编译成 WeakMap 的方式来实现。

其实 ts 实现的新语法还是不少的,比如 ? 和 ?? 分别是可选链和默认值的语法,下面这两种写法等价:

const res = data?.name ?? 'dong';const res2 = data && data.name  || 'dong';

这种新语法都是直接可用的,babel 的话需要引入下 proposal 插件。

对了,我记得 ts 里 class 也是有 private 的修饰符的,那个不也是私有属性么?

其实它是私有属性但也不完全是,我们来看一下:

ts private

ts 可以通过 private 来修饰属性、方法的可见性:

  • private 表示属性私有,只有 class 内部可访问

  • protected 表示保护,只有 class 和子 class 可访问

  • public 表示公有,外部也可访问

类型检查和提示的时候是有区别的,比如 private 属性在 class 外部不可访问:

而 class 内部是可以访问的:

但是这种约束只是用于类型检查的,只存在编译期间,运行时并没有这种约束。

我们可以看下编译后的代码:

可以看到没有做任何处理。

而如果用 #prop 的方式,除了编译时是 private 的,运行时也是:

所以,要实现真正的 private 的话,还是用 #prop 的方式,如果只是编译时约束那声明下 private 就行。

总结

class 用于定义围绕某个概念的一系列属性和方法,这些属性和方法有的是内部用的,有的是对外的。只有内部用的属性、方法需要实现私有化。

实现私有属性方法,我树立了 6 种方式:

  • 通过下划线 _prop 从命名上区分

  • 通过 Proxy 来定义 get、set、ownKeys 的逻辑

  • 通过 Symbol 来定义唯一的属性名,不能通过 keys 拿到

  • 通过 WeakMap 来保存所有对象的私有属性和方法

  • 通过 #prop 的 es 新语法实现私有,babel 和 tsc 会把它们编译成 WeakMap 的方式

  • 通过 ts 的 private 在编译时约束

这六种方式,有三种只是伪私有,比如 _prop(依然可以访问)、ts 的 private(运行时可访问)、Symbol(可以通过 Object.getOwnSymbols 拿到 symbol 来访问)。

另外三种是真正的私有,包括 Proxy、WeakMap、#prop(目前是编译为 WeakMap 的方式)。

有的是从属性名上想办法,比如 _prop 和 Symbol,有的是从 this 上想办法,比如 Proxy(包一层) 和 WeakMap(不挂到 this),有的是从语言本身想办法,比如 ts 的 private 或者 es 新语法的 #prop。

以上是"JS私有属性的实现方式有哪些"这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注行业资讯频道!

属性 私有 方式 方法 对象 语法 编译 开头 下划线 只是 目标 有的是 可以通过 还是 两个 内容 办法 只有 插件 篇文章 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 伟宏互联网科技 互联网品科技 河南真二互联网科技有限公司 极限挑战互联网科技 成都狮子座互联网科技 卓越科技互联网 招聘 丰台科技园 互联网公司 中化能源互联网科技招聘 互联网经济与金融科技 湖南一路畅行互联网科技 广州市恰铎互联网科技有限公司 消费医药和互联网科技 呼和浩特新华互联网科技学院 深圳艾果互联网通讯科技 张家口科技产业互联网平台 易租星互联网科技有限公司 悟空问答互联网科技 互联网科技大会大佬 互联网领先科技成果宣传视频 互联网企业和网络科技有限公司 江苏根荣互联网科技 互联网科技小贷 上海万想互联网科技 互联网对科技的发展 中国互联网公司排名锤子科技 佛山市翊博互联网络科技有限公司 南博望互联网科技有限公司 互联网科技年终奖 张家口科技产业互联网平台 三亚科技创新互联网基地
0