如何用Object.defineProperty与类语法实现属性getter/setter并解决属性冗余?
解决类中结合Object.defineProperty实现getter/setter的问题
问题根源分析
你的代码存在两个核心问题:
- 多余属性暴露:setter中将值存在
this._newGender,导致实例上同时存在_gender(访问器属性)和_newGender(数据属性),不符合只保留_gender的需求。 - 栈溢出错误:如果尝试在setter中直接赋值
this._gender = value,会无限触发setter自身调用,形成递归循环,最终导致Maximum call stack size exceeded。
正确实现方案
方案1:ES私有字段(推荐,ES6+)
使用#开头的私有字段存储实际值,私有字段仅类内部可访问,不会被枚举或暴露在实例上,完美解决多余属性问题,同时避免递归调用:
"use strict"; class Employee { #_gender; // 私有字段,不会出现在实例上 constructor(firstName, lastName, age, salary, gender) { this._firstName = firstName; this._lastName = lastName; this._age = age; this._salary = salary; Object.defineProperty(this, "_gender", { enumerable: true, configurable: true, set(value) { if (typeof value !== "boolean") { throw new Error("Wrong type of data."); } this.#_gender = value; // 存入私有字段,不会暴露 }, get() { return this.#_gender; } }); this._gender = gender; this._retirementYears = 0; } retirement() { if (this._gender === true) { return this._retirementYears = 65 - this._age; } else if (this._gender === false) { return this._retirementYears = 60 - this._age; } else { console.error("Invalid gender."); } } } class Teacher extends Employee { constructor(firstName, lastName, age, salary, gender, subject, yearsOfExperience) { super(firstName, lastName, age, salary, gender); this._subject = subject; this._yearsOfExperience = yearsOfExperience; } } const mathTeacher = new Teacher("John", "Smith", 35, 6000, true, "Math", 5); console.log(mathTeacher); // 仅显示_gender属性,无多余字段
方案2:Symbol作为内部存储键(兼容旧环境)
如果环境不支持私有字段,可以用唯一的Symbol作为存储键,Symbol键不会被常规枚举方法遍历,console.log也不会显示:
"use strict"; const GENDER_STORAGE = Symbol('gender'); // 唯一Symbol键 class Employee { constructor(firstName, lastName, age, salary, gender) { this._firstName = firstName; this._lastName = lastName; this._age = age; this._salary = salary; Object.defineProperty(this, "_gender", { enumerable: true, configurable: true, set(value) { if (typeof value !== "boolean") { throw new Error("Wrong type of data."); } this[GENDER_STORAGE] = value; // 存入Symbol键下,不会暴露 }, get() { return this[GENDER_STORAGE]; } }); this._gender = gender; this._retirementYears = 0; } retirement() { if (this._gender === true) { return this._retirementYears = 65 - this._age; } else if (this._gender === false) { return this._retirementYears = 60 - this._age; } else { console.error("Invalid gender."); } } } // Teacher类和实例化代码不变
方案3:类原生访问器语法(更简洁)
不需要手动调用Object.defineProperty,类本身支持直接定义getter/setter,写法更简洁,效果一致:
"use strict"; class Employee { #_gender; constructor(firstName, lastName, age, salary, gender) { this._firstName = firstName; this._lastName = lastName; this._age = age; this._salary = salary; this._gender = gender; // 直接触发setter this._retirementYears = 0; } // 类原生getter/setter get _gender() { return this.#_gender; } set _gender(value) { if (typeof value !== "boolean") { throw new Error("Wrong type of data."); } this.#_gender = value; } retirement() { if (this._gender === true) { return this._retirementYears = 65 - this._age; } else if (this._gender === false) { return this._retirementYears = 60 - this._age; } else { console.error("Invalid gender."); } } } // Teacher类和实例化代码不变
验证效果
修改后执行console.log(mathTeacher),只会看到_gender属性,没有多余的存储字段;赋值和取值操作正常,不会出现栈溢出错误。
内容的提问来源于stack exchange,提问作者Delfon
相关产品推荐
相关产品推荐

