Как сделать так чтобы значение свойство после вызова defineProperty не изменялось на undefined?

У меня есть код

const obj = {
  props: "Hello"
}

function useChange(obj, props, fn) {
  Object.defineProperty(obj, props, {
    set(newValue) {
      fn(newValue)
    }
  })
}

useChange(obj, "props", (newValue) => {
  console.log(newValue)
})

obj.props = "Привет"
console.log(obj.props) //undefined

У меня проблема, что после вызова defineProperty значение obj.props равно не "привет", а undefined.

При это если я делаю данный вариант, то выходит ошибка:

const obj = {
  props: "Hello"
}

function useChange(obj, props, fn) {
  Object.defineProperty(obj, props, {
    set(newValue) {
      fn(newValue)
      obj[props] = newValue;
    }
  })
}

useChange(obj, "props", (newValue) => {
  console.log(newValue)
})

obj.props = "Привет"
console.log(obj.props) //undefined

Uncaught RangeError: Maximum call stack size exceeded

Если знаете как сделать так чтобы после вызова defineProperty свойство не изменяло своё значение на undefined, пишите. Заранее спасибо за ответ!


Ответы (1 шт):

Автор решения: Grundy

Вызывая Object.defineProperty(obj, props свойство props полностью переписывается на новое. В данном случае новое свойство содержит только сеттер. Сеттер, который никуда не сохраняет новое значение. Поэтому и получить его невозможно.

В зависимости от того, как планируется применять функцию useChange варианты решения могут быть разными.

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

либо воспользоваться замыканием.

Например:

const obj = {
  props: "Hello"
}

function useChange(obj, props, fn) {
  var propsVal = obj[props]; // используем замыкание
  Object.defineProperty(obj, props, {
    set(newValue) {
      fn(newValue)
      propsVal = newValue;
    },
    get() {
      return propsVal;
    }
  })
}

useChange(obj, "props", (newValue) => {
  console.log(newValue)
})

obj.props = "Привет"
console.log(obj.props) //undefined

const obj2 = {
  props: "Hello"
}

function useChange2(obj, props, fn) {

  var key = Symbol(props);
  obj[key] = obj[props]; // используем специальное имя
  Object.defineProperty(obj, props, {
    set(newValue) {
      fn(newValue)
      obj[key] = newValue;
    },
    get() {
      return obj[key];
    }
  })
}

useChange2(obj2, "props", (newValue) => {
  console.log(newValue)
})

obj2.props = "Привет2"
console.log(obj2.props) //undefined

→ Ссылка