Javascript: использование свойств аксессора в замыканиях объектов - PullRequest
4 голосов
/ 13 апреля 2020

Недавно я узнал о составлении объектов вместе, используя функции из этой статьи . После этого я получаю следующий код:

function withFlying(o) {
  let _isFlying = false;
  return {
    ...o,
    fly () {
      _isFlying = true;
    },
    land () {
      _isFlying = false;
    },
    isFlying () {
        return _isFlying
    }
  }
};

function withWalking(o) {
    let isWalking = false;
    return {
        ...o,
        startWalking() {
            isWalking = true;
            return this
        },
        stopWalking() {
            isWalking = false;
            return this
        },
        isWalking: () => isWalking
    }
}

const bird = withWalking(withFlying({}))

Все здесь работает. Однако я хотел бы иметь возможность вызывать isFlying как свойство вместо функции:

// current (working)
bird.isFlying() // return value of `_isFlying`

// desired
bird.isFlying // return value of `_isFlying`

Я знаю, что get и set - это ключевые слова, которые можно использовать в литералах объекта, и вот я попробовал это:

function withFlying(o) {
  let _isFlying = false
  return {
    ...
    get isFlying () {
      return _isFlying
    }
  }
}

Но он не показывает правильное значение после обновления с использованием других функций. Я подумал, что если свойство get является функцией, замыкания будут применяться аналогично другим функциям. Я ошибаюсь в этом предположении? Есть ли базовое поведение с get, которое я не понимаю, и то, что я пытаюсь достичь, возможно, так, как я это делаю сейчас?

Вот фрагмент кода с кодом, который я пытался использовать :

function withFlying(o) {
  let _isFlying = false;
  return {
    ...o,
    fly () {
      _isFlying = true;
    },
    land () {
      _isFlying = false;
    },
    valueOf_isFlying() {
      return _isFlying;
    },
    get isFlying () {
      return _isFlying
    }
  }
};

function withWalking(o) {
  let isWalking = false;
  return {
    ...o,
    startWalking() {
        isWalking = true;
        return this
    },
    stopWalking() {
        isWalking = false;
        return this
    },
    isWalking: () => isWalking
  }
}


const bird = withWalking(withFlying({}))

// desired
console.log(bird.isFlying) // _isFlying starts false
bird.fly() // should set _isFlying to true
console.log(bird.isFlying) // still returns false
console.log(bird.valueOf_isFlying()) // shows _isFlying is true

1 Ответ

1 голос
/ 13 апреля 2020

Проблема заключается в том, что при создании нового объекта вы используете спред-нотацию для копирования свойств из исходного объекта:

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...