You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Object.defineProperty与类语法实现属性getter/setter并解决属性冗余?

解决类中结合Object.defineProperty实现getter/setter的问题

问题根源分析

你的代码存在两个核心问题:

  1. 多余属性暴露:setter中将值存在this._newGender,导致实例上同时存在_gender(访问器属性)和_newGender(数据属性),不符合只保留_gender的需求。
  2. 栈溢出错误:如果尝试在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 23:27:11