это мой код
"use strict";
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 "Wrong type of data.";
}else{
this._newGender = value;
}
},
get() {
return this._newGender;
}
});
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.info(mathTeacher);
После console.info(mathTeacher) я получаю два свойства, относящиеся к полу (_gender и _newGender). Я хочу получить только одно свойство под названием _gender и понятия не имею, как это сделать. Я попытался присвоить значение this._gender, но это не сработало из-за ошибки «Превышен максимальный размер стека вызовов».
Почему вы вообще используете сеттер? Просто сделайте if (typeof value !== "boolean") throw …; прямо в конструкторе
@Delfon ... ОП, возможно, ознакомится с моим поздно предоставленным ответом, который показывает возможный способ реализации примера ОП менее сломанным способом, чем исходный код ОП. Несмотря на то, что ОП получил отрицательное голосование, ему рекомендуется попробовать.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Существует несколько решений, но вопрос интересен тем, работает ли клонирование экземпляра класса для каждого подхода.
Чтобы клонировать экземпляр класса, мы могли бы использовать:
Object.assign(Object.create(Object.getPrototypeOf(obj)), obj);
Как оказалось, только последний подход работает при клонировании, когда свойство определено в прототипе. Предоставленное клонирование не вызывает конструктор. А вызов конструктора проблематичен, так как аргументы на момент клонирования неизвестны.
Но я бы сказал, что нам нужен какой-то метод clone() в классе, чтобы сделать клонирование более гибким. Так что наблюдаемые здесь неудачи клонирования не критичны, по моему мнению.
Итак, начнем:
Вы можете использовать частное свойство (CLONING FAILED!!!):
class Employee {
#_newGender = false;
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 "Wrong type of data.";
}else{
this.#_newGender = value;
}
},
get() {
return this.#_newGender;
}
});
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);
try{
mathTeacher._gender = 'test';
} catch(e){
console.info(e);
}
console.info(mathTeacher);
const clone = Object.assign(Object.create(Object.getPrototypeOf(mathTeacher)), mathTeacher)
console.info(clone);
try{
clone._gender = 'test';
} catch(e){
console.info(e);
}
clone._gender = false;
console.info(mathTeacher, clone);Или сделайте _newGender неперечислимым (CLONING FAIL !!!):
class Employee {
constructor(firstName, lastName, age, salary, gender) {
this._firstName = firstName;
this._lastName = lastName;
this._age = age;
this._salary = salary;
Object.defineProperty(this, "_newGender", {enumerable: false, value: false, writable: true});
Object.defineProperty(this, "_gender", {
enumerable: true,
configurable: true,
set(value) {
if (typeof value !== "boolean"){
throw "Wrong type of data.";
}else{
this._newGender = value;
}
},
get() {
return this._newGender;
}
});
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);
try{
mathTeacher._gender = 'test';
} catch(e){
console.info(e);
}
console.info(mathTeacher);
const clone = Object.assign(Object.create(Object.getPrototypeOf(mathTeacher)), mathTeacher)
console.info(clone);
try{
clone._gender = 'test';
} catch(e){
console.info(e);
}
clone._gender = false;
console.info(mathTeacher, clone);На самом деле вы могли бы создать для этого какую-нибудь утилиту и сохранить реальную ценность в ее объеме. (КЛОНИРОВАНИЕ НЕУДАЛО!!!):
const createValidatedProp = (obj, prop, cb, value) => {
let val = value;
Object.defineProperty(obj, prop, {
enumerable: true,
configurable: true,
set(value) {
if (!cb(value)){
throw "Wrong type of data.";
}else{
val = value;
}
},
get() {
return val;
}
});
};
class Employee {
constructor(firstName, lastName, age, salary, gender) {
this._firstName = firstName;
this._lastName = lastName;
this._age = age;
this._salary = salary;
createValidatedProp(this, '_gender', val => typeof val === 'boolean', 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);
try{
mathTeacher._gender = 'test';
} catch(e){
console.info(e);
}
console.info(mathTeacher);
const clone = Object.assign(Object.create(Object.getPrototypeOf(mathTeacher)), mathTeacher)
console.info(clone);
try{
clone._gender = 'test';
} catch(e){
console.info(e);
}
clone._gender = false;
console.info(mathTeacher, clone);Если вы хотите использовать метод получения/установки в прототипе (как это сделал бы класс JS (но свойство не является перечислимым)) используйте символ, чтобы сохранить реальное значение (свойства символа не перечислимы):
Символ — это встроенный объект, конструктор которого возвращает примитив символа, также называемый значением символа или просто символом, который гарантированно будет уникальным. Символы часто используются для добавления к объекту уникальных ключей свойств, которые не будут конфликтовать с ключами, которые любой другой код может добавить к объекту, и которые скрыты от любых механизмов, которые обычно используются другим кодом для доступа к объекту. Это обеспечивает слабую инкапсуляцию или слабую форму сокрытия информации.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol
(КЛОНИРОВАНИЕ ОК):
const createValidatedProp = (obj, prop, cb) => {
let name = Symbol(prop);
Object.defineProperty(obj, prop, {
enumerable: true,
configurable: true,
set(value) {
if (!cb(value)) throw "Wrong type of data.";
this[name] = value;
},
get() {
return this[name];
}
});
};
class Employee {
constructor(firstName, lastName, age, salary, gender, test) {
this._firstName = firstName;
this._lastName = lastName;
this._age = age;
this._salary = salary;
this._retirementYears = 0;
this._gender = gender;
this._subject = test;
}
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.");
}
}
}
createValidatedProp(Employee.prototype, '_gender', val => typeof val === 'boolean');
createValidatedProp(Employee.prototype, '_subject', val => typeof val === 'string');
class Teacher extends Employee {
constructor(firstName, lastName, age, salary, gender, subject, yearsOfExperience) {
super(firstName, lastName, age, salary, gender, subject);
this._subject = subject;
this._yearsOfExperience = yearsOfExperience;
}
}
const mathTeacher = new Teacher("John", "Smith", 35, 6000, true, "Math", 5);
try{
mathTeacher._gender = 'test';
} catch(e){
console.info(e);
}
console.info(mathTeacher);
const clone = Object.assign(Object.create(Object.getPrototypeOf(mathTeacher)), mathTeacher)
console.info(clone);
try{
clone._gender = 'test';
} catch(e){
console.info(e);
}
clone._gender = false;
console.info(mathTeacher, clone);Почему существует «#_newGender = false»?
@Delfon, просто инициализируй его с каким-то значением, можешь оставить undefined. мне не нравится undefined, опасно
@AlexanderNenachev ... в какой момент в исходном вопросе был введен аспект «клонирования»? И где фактический ответ, указывающий на недостатки конструкции ОП?
@PeterSeliger просто интересный аспект темы, которую я хотел изучить, изучая JS
@AlexanderNenachev ... «просто интересный аспект темы, которую я хотел изучить». Но разве не самая благородная задача отвечающего — ответить вопрошающему и указать решение его проблемы?
@PeterSeliger предложил 4 варианта, выбор свободен.
Можно возразить, что ваш алгоритм клонирования просто сломан :-) Если вы хотите по-настоящему реплицировать собственные свойства, включая неперечислимые и геттеры, вам следует использовать Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj)); вместо Object.assign(…). Конечно, это не поможет с приватными полями или замыканиями старого экземпляра, но если class использует их, в любом случае рекомендуется предоставить метод .clone(), который затем вызывал бы конструктор внутри. В любом случае, ничто из этого не решает проблему ОП, их волнует console.info клонирование.
@ Берги, ты недостаточно изучил мой ответ, вероятно, учитывая его размер и твою репутацию здесь (шучу, без обид). Каждое мое решение решает проблему ОП, плюс я упомянул, что .clone() — лучший способ поддержки клонирования. предложенное вами клонирование интересно, но, как уже упоминалось, клонирование выходит за рамки вопроса ФП, это всего лишь побочный продукт
Из моего комментария выше...
«Чего ОП ожидает от собственного
Employeeсвойства_gender, которое реализуется черезget/set, где установщик явно присваивает дополнительно введенному собственному_newGenderсвойству, а геттер всегда возвращает значение самого собственного_newGenderсвойства? вся реализацияEmployeeуже ошибочна».
Возможен рефакторинг...
gender правильно,class Employee {
constructor({
// ensure minimum default values.
firstName = '',
lastName = '',
age = 0,
salary = 0,
gender = false,
}) {
Object.defineProperty(this, 'gender', {
set(value) {
if (typeof value !== 'boolean') {
throw 'A boolean data type needs to be assigned.';
} else {
// assignement to the enclosed/local `gender` variable.
gender = value;
}
},
get() {
// return value of the enclosed/local `gender` variable.
return gender;
},
enumerable: true,
});
Object.assign(this, {
firstName, lastName, age, salary, gender,
});
}
// - either rename and correctly implement the `retirement` method
// - or implement `yearsUntilRetirement` via getter and setter.
getYearsUntilRetirement() {
if (this.gender === true) {
return 65 - this.age;
} else {
return 60 - this.age;
}
}
}
class Teacher extends Employee {
constructor({
// ensure minimum default values.
subject = '',
yearsOfExperience = 0,
// assume additional base configuration.
...baseConfig
}) {
super(baseConfig);
Object.assign(this, {
subject, yearsOfExperience,
});
}
}
const mathTeacher = new Teacher({
firstName: 'John',
lastName: 'Smith',
age: 35,
salary: 6000,
gender: true,
subject: 'Math',
yearsOfExperience: 5,
});
console.info({
mathTeacher,
});
console.info(
'mathTeacher.getYearsUntilRetirement() ...',
mathTeacher.getYearsUntilRetirement(),
);
mathTeacher.gender = false;
console.info({
mathTeacher,
});
console.info(
'mathTeacher.getYearsUntilRetirement() ...',
mathTeacher.getYearsUntilRetirement(),
);.as-console-wrapper { min-height: 100%!important; top: 0; }не отличается от моего третьего подхода, когда вы сохраняете значение свойства в частной области
@AlexanderNenachev ... На самом деле единственное сходство - это использование замыкания, которое инкапсулирует локальную переменную/состояние установки/получения собственного свойства gender.
в любом случае то же самое, я только что вынес логику наружу в служебную функцию
Что OP ожидает от собственного
Employeeсвойства_gender, которое реализуется черезget/set, где установщик явно присваивает дополнительно введенному собственному_newGenderсвойству, а геттер всегда возвращает значение самого собственного_newGenderсвойства? Вся реализацияEmployeeуже имеет недостатки.