Проблема заключается в том, что при создании нового объекта вы используете спред-нотацию для копирования свойств из исходного объекта:
return {
...o,
// ...
};
Проблема в том, что он копирует затем -current значение свойств средства доступа, а не определение свойства средства доступа. Вы можете увидеть это здесь:
const obj1 = {
get example() {
return 42;
}
};
console.log("Notice that the property descriptor is for an accessor property:");
console.log(Object.getOwnPropertyDescriptor(obj1, "example"));
const obj2 = {...obj1};
console.log("Notice that the property descriptor is for a simple data property:");
console.log(Object.getOwnPropertyDescriptor(obj2, "example"));
.as-console-wrapper {
max-height: 100% !important;
}
Это очень похоже на то, как вы это сделали:
for (const key of Object.keys(o) {
newObject[key] = e[key];
}
e[key]
получает текущее значение свойства, а не определение свойства.
Чтобы исправить это, используйте Object.getOwnPropertyDesciptors
, чтобы получить дескрипторы свойств, и используйте Object.defineProperties
, чтобы определить те же свойства для нового объекта. Поскольку вы делаете это (и добавляете больше свойств) как минимум в двух местах, вам, вероятно, понадобится служебная функция:
function assignPropertyDescriptors(target, obj, updates) {
// B
return Object.defineProperties(
// A
Object.defineProperties(
target,
Object.getOwnPropertyDescriptors(obj)
),
Object.getOwnPropertyDescriptors(updates)
);
}
Вызов "A" Object.defineProperties
копирует дескрипторы свойств исходного объекта и применяет их на новый объект. Вызов "B" Object.defineProperties
также применяет те, которые вы добавляете к этому новому объекту.
Но давайте обобщим это в al oop, аналогично Object.assign
(отсюда и название assignPropertyDescriptors
):
function assignPropertyDescriptors(target, ...updates) {
for (const update of updates) {
Object.defineProperties(
target,
Object.getOwnPropertyDescriptors(update)
);
}
return target;
}
withFlying
и withWalking
будут использовать эту рабочую функцию, например:
function withFlying(o) {
let _isFlying = false;
return assignPropertyDescriptors({}, o, {
fly () {
_isFlying = true;
},
land () {
_isFlying = false;
},
get isFlying () {
return _isFlying
}
});
};
Вот полный пример:
function assignPropertyDescriptors(target, ...updates) {
for (const update of updates) {
Object.defineProperties(
target,
Object.getOwnPropertyDescriptors(update)
);
}
return target;
}
function withFlying(o) {
let _isFlying = false;
return assignPropertyDescriptors({}, o, {
fly () {
_isFlying = true;
},
land () {
_isFlying = false;
},
get isFlying () {
return _isFlying
}
});
};
function withWalking(o) {
let isWalking = false;
return assignPropertyDescriptors({}, o, {
startWalking() {
isWalking = true;
return this
},
stopWalking() {
isWalking = false;
return this
},
isWalking: () => isWalking
});
}
const bird = withWalking(withFlying({}))
console.log(bird.isFlying) // _isFlying starts false
bird.fly() // should set _isFlying to true
console.log(bird.isFlying) // _isFlying is true