理解 Getter 和 Setter 的基本语法与工作机制
掌握在对象字面量和 ES6 类中定义访问器的方法
能够结合私有字段实现安全的数据封装
了解 Object.defineProperty() 动态创建访问器的高级用法
明确 Getter/Setter 的适用场景与性能权衡
Getter 和 Setter 是 ECMAScript 5(2009)引入的特殊方法,用于拦截对对象属性的读取和赋值操作。它们让属性访问看起来像普通字段,但背后可执行自定义逻辑。
语法透明:使用时如同访问普通属性(obj.prop),无需调用函数
逻辑隐藏:可在读写时执行验证、计算、日志等操作
增强封装:配合私有字段可完全控制数据访问
类中的 Getter 与 Setter
class Person {
constructor(name) {
this._name = name; // 下划线表示“受保护”
}
// Getter:当访问 person.name 时自动调用
get name() {
return this._name;
}
}
const person = new Person('Anjali');
console.log(person.name); // 输出: Anjali(看似读属性,实则调用方法)class Person {
constructor(name) {
this._name = name;
}
set name(newName) {
if (typeof newName !== 'string' || newName.trim() === '') {
throw new Error('Name must be a non-empty string');
}
this._name = newName.trim();
}
}
const person = new Person('Anjali');
person.name = 'Ayushi'; // 触发 setter
// person.name = ''; // 抛出错误注意:
set方法必须且只能接受一个参数(新值)
不仅类支持,普通对象也可直接定义 Getter/Setter:
const user = {
firstName: "Anurag",
lastName: "Das",
// Getter:组合全名
get fullName() {
return `${this.firstName} ${this.lastName}`;
},
// Setter:拆分全名
set fullName(name) {
const parts = name.split(" ");
this.firstName = parts[0] || "";
this.lastName = parts[1] || "";
}
};
console.log(user.fullName); // "Anurag Das"
user.fullName = "Anuj Jain";
console.log(user.firstName, user.lastName); // "Anuj" "Jain"ES2022+
使用 # 前缀声明真正的私有字段,配合 Getter/Setter 提供安全接口:
class BankAccount {
#balance; // 私有字段,外部无法直接访问
constructor(initialBalance) {
this.#balance = initialBalance;
}
// 只读访问余额
get balance() {
return this.#balance;
}
// 受控存款
set balance(amount) {
if (amount < 0) {
console.log("Balance cannot be negative!");
return;
}
this.#balance = amount;
}
// 更合理的存款方法(推荐)
deposit(amount) {
if (amount > 0) {
this.#balance += amount;
}
}
}
const account = new BankAccount(1000);
console.log(account.balance); // 1000
account.balance = -500; // 输出警告,余额不变
account.deposit(200);
console.log(account.balance); // 1200
优势:
外部无法通过 account.#balance 访问私有字段(语法错误)
所有状态变更必须经过验证逻辑
Object.defineProperty()
当需要在运行时动态添加访问器时,可使用此方法:
const employee = { name: "Anjali" };
Object.defineProperty(employee, "greeting", {
get() {
return `Hello, ${this.name}!`;
},
set(newName) {
this.name = newName; // 更新原始字段
},
enumerable: true, // 可枚举(for...in 中可见)
configurable: false // 不可删除或重新配置
});
console.log(employee.greeting); // "Hello, Anjali!"
employee.greeting = "Ayushi"; // 触发 setter
console.log(employee.name); // "Ayushi"const obj = {};
['x', 'y'].forEach(prop => {
let value = 0;
Object.defineProperty(obj, prop, {
get() { return value; },
set(newValue) {
if (typeof newValue === 'number') value = newValue;
}
});
});class DataProcessor {
constructor(data) {
this.rawData = data;
this._processedData = null;
}
get processedData() {
if (this._processedData === null) {
console.log("Computing expensive operation...");
this._processedData = this.rawData.map(x => x * 2);
}
return this._processedData;
}
}
const processor = new DataProcessor([1, 2, 3]);
console.log(processor.processedData); // 首次计算并缓存
console.log(processor.processedData); // 直接返回缓存结果class Temperature {
constructor(celsius) {
this.celsius = celsius;
}
set celsius(value) {
if (value < -273.15) {
throw new Error("Temperature below absolute zero!");
}
this._celsius = value;
}
get fahrenheit() {
return (this._celsius * 9/5) + 32;
}
}class Config {
set debugMode(enabled) {
console.log(`Debug mode changed to: ${enabled}`);
this._debug = enabled;
}
get debugMode() {
return this._debug;
}
}经验法则:
若属性需要验证、计算或副作用 → 用 Getter/Setter
若操作明显是行为(如
save(),calculate())→ 用普通方法若仅为简单数据容器 → 直接属性即可
命名一致性:Getter/Setter 名称应与概念属性名一致(如 name 而非 getName)
避免过度使用:简单属性无需包装
文档说明:在复杂逻辑处添加注释
优先私有字段:在现代 JS 中用 #field 而非 _field
Getter/Setter 让属性访问兼具字段的简洁性与方法的灵活性
在类和对象字面量中均可定义,也支持通过 Object.defineProperty() 动态创建
配合私有字段(#)可实现真正的数据封装
适用于:数据验证、计算属性、懒加载、调试追踪等场景
应避免在 Getter 中产生副作用,Setter 中必须包含必要验证
性能敏感场景需谨慎使用,但多数应用中开销可忽略
如果一个类同时定义了 get prop() 和普通属性 prop,会发生什么?为什么?
如何使用 Getter 实现一个“只读”属性,使其在严格模式下赋值时报错?