简体   繁体   中英

Create a factory function that returns an object with immutable state

Link to CodePen https://codepen.io/jaspercreel/pen/Mvaodp

Basically, my function will look something like this:

const Factory = (args) => {

  const state = {
    args
  }

  const methods = {  

    getState() => {
      return args;
    },

    setState(args) => {
      state.args = args;
    },

    doStuffWithState() => {
      let args = this.getState();
      function(args) {
        return args plus something else;
      }();
    }

  }

  return Object.assign({}, methods);

}

The problem is that whenever I call doStuffWithState() it changes the state object. I thought I would be safe creating a new variable with the returned state, but I learned that when referencing an object you are only creating a new reference. So my question is this, how can I create immutable state that can be referenced but not changed (except by helper functions) in a factory function?

My end goal is to create a sorter factory function that will create an object that takes arrays and sorts and searches them in different ways. I want to be able to store a default array in the sorter that can be referenced to return different sorting options but not changed. Any advice?

One approach would be to store the JavaScript object as a JSON string with JSON.stringify() and use JSON.parse() to parse the JSON string to a JavaScript object, which does not reference the original object passed that is stored as a string.

You can also utilize Promise to return a JavaScript object reflecting properties of JSON string, passed to Object.assign() , which can return modified properties or values of original object, without affecting original object passed to Factory , which returns a Promise value as the JSON string format of the original passed object.

 // `const` declaration cannot be changed or deleted const Factory = (args) => Promise.resolve(JSON.stringify(args)); const state = Factory({ abc: [1, 2, 3] }); state.then(data => console.log(data)); // `{"abc":[1,2,3]}` // change object passed to `Factory` state.then(data => Object.assign(JSON.parse(data), { abc: JSON.parse(data).abc.concat(4, 5, 6) })) // do stuff with modified `JSON` `args` as JavaScript object.then(res => {console.log(res); return res}) // `{"abc":[1,2,3,4,5,6]}`.then(res => // `res`: `{"abc":[1,2,3,4,5,6]}` // `state` `Promise` value is original object // as valid `JSON` `{"abc":[1,2,3]}` // return original `args` as `JSON` string state.then(data => {console.log(res, data); return data}) ) // original `args` as JavaScript object.then(o => console.log(JSON.parse(o)))

I appreciate all the answers and comments guys. It turns out I was being very dumb. So in my tests I was sometimes able to return a value without altering the original and sometimes not, and I could not figure out why. Now I know it is because in my tests that altered the state, I was calling sort() on an array, and sort is an in-place function. It alters the original array.

Such a small oversight on my part, but I consider it a learning experience. Thanks again for the effort in the help guys. Hopefully my dumb mistake can be a learning experience for others too.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM