JS中为何用getter/setter而非直接访问属性?附示例对比
Getter/Setter vs 直接访问属性:哪种方案更优?
很棒的问题!很多刚接触JavaScript存取器的开发者都会有这个疑惑——毕竟在简单场景下,直接访问属性确实看起来更省事。咱们先把你给出的两个等价代码示例摆出来,再详细分析:
两个等价代码示例
示例1:使用Getter/Setter
const menu = { _courses: { appetizers: [], mains: [], desserts: [], }, get appetizers() { return this._courses.appetizers; }, set appetizers(appetizer) { this._courses.appetizers = appetizer; } } menu.appetizers.push(['food']); console.log(menu.appetizers);
示例2:直接访问属性
const menu = { _courses: { appetizers: [], mains: [], desserts: [], }, } menu._courses.appetizers.push(['food']); console.log(menu._courses.appetizers);
你提到这两段代码功能完全等价、输出结果一致,并且觉得第二种直接访问的方式更易懂,想知道为什么Getter/Setter会被认为是更优方案,以及针对这个具体示例哪种方式更好。
为什么Getter/Setter被视为更优方案?
Getter和Setter的核心价值在于封装性和可维护性,虽然在这个极简示例里显得多余,但在实际业务的复杂场景下,它们的优势会非常明显:
- 封装内部实现细节:假设未来你需要修改
appetizers的存储逻辑——比如从直接存在_courses对象里,改成从后端接口动态获取,或者把存储结构从数组改成Map。如果用了Getter/Setter,你只需要修改存取器的内部代码,所有外部调用menu.appetizers的地方完全不需要改动;但如果是直接访问menu._courses.appetizers,所有用到这个路径的代码都得跟着改,维护成本会指数级上升。 - 添加数据校验与约束:你可以在Setter里加入校验逻辑,确保存入的数据符合预期,比如:
这样就能避免错误的数据被存入,而直接访问属性的话,任何人都可以随意修改set appetizers(newAppetizers) { if (!Array.isArray(newAppetizers)) { throw new Error("开胃菜列表必须是数组类型"); } // 还可以进一步校验数组中的每个元素是否为合法的菜品名称 const isValid = newAppetizers.every(item => typeof item === 'string'); if (!isValid) { throw new Error("每个开胃菜必须是字符串类型"); } this._courses.appetizers = newAppetizers; }_courses.appetizers,没有任何约束,很容易引入bug。 - 统一对外接口:不管内部数据结构怎么变化,外部始终通过
menu.appetizers这个统一接口来访问和修改数据,代码风格更一致,也更符合面向对象编程的封装原则——把内部私有细节(比如_courses这个下划线开头的属性,通常约定为内部私有,不应该直接访问)隐藏起来,只暴露安全、可控的接口。
针对你的示例,哪种方式更好?
在这个超简单、无扩展需求的场景下,直接访问属性确实更简洁易懂,完全没问题——毕竟代码越少,心智负担越低。但如果这个menu对象是一个会被多人维护、或者未来有扩展需求的模块(比如要添加菜品校验、修改存储方式等),那强烈建议使用Getter/Setter:
- 它能给未来的迭代留有余地,避免后续重构时大规模改动调用代码;
- 也能明确区分对外暴露的公共接口和内部私有属性,让代码的可读性和可维护性更高。
简单总结:短期小项目、一次性脚本,直接访问更高效;长期维护、需要扩展的代码,Getter/Setter是更稳健的选择。
内容的提问来源于stack exchange,提问作者Abhay Maurya
相关产品推荐
相关产品推荐

