【问题标题】:Redux: why using Object.assign if it is not perform deep clone?Redux:如果不执行深度克隆,为什么要使用 Object.assign?
【发布时间】:2017-09-03 16:31:33
【问题描述】:

Redux 的一个核心概念是,状态是immutable。但是,我看到了很多例子,包括在 Redux docs 中使用 javascript Object.assign。然后,我在MDN看到了这个警告:

对于深度克隆,我们需要使用其他替代方法,因为 Object.assign() 复制属性值。如果源值是 引用一个对象,它只复制该引用值。

那么,如果重点是不变性,为什么还要使用 Object.assign 呢?我在这里遗漏了什么吗?

【问题讨论】:

    标签: javascript redux cloning


    【解决方案1】:

    让我们看看你链接的例子:

    function todoApp(state = initialState, action) {
      switch (action.type) {
        case SET_VISIBILITY_FILTER:
          return Object.assign({}, state, {
            visibilityFilter: action.filter
          })
        default:
          return state
      }
    }
    

    是的,这是state 的浅表副本,它使用旧的所有内容创建一个新对象,但更新了visibilityFilter。但是,如果您一致对不可变对象进行处理,那么如果新状态和旧状态共享对其他不可变对象的引用就可以了。后来,大概,如果您要更改其他内容,您将遵循相同的模式。此时,我们上面制作的浅拷贝将继续使用旧的,而您的新对象将使用新的。

    如果您一直应用不变性,那么您所需要的只是您正在修改的级别及其所有父级别的浅拷贝。在上面的例子中,修改是在顶层,所以它只是一个副本。

    但如果它更深呢?假设你有这个对象:

    let obj = {
        a: {
            a1: "a1 initial value",
            a2: "a2 initial value"
        },
        b: {
            b1: "b1 initial value",
            b2: "b2 initial value"
        }
    };
    

    如果你想更新a,就像上面的状态示例一样;您只需使用obj 的一份浅表副本即可:

    obj = Object.assign({}, obj, {a: {a1: "new a1", a2: "new a2"}});
    

    spread properties(目前处于第 3 阶段,通常在 JSX 转译器设置中启用,可能会成为 ES2018):

    obj = {...obj, a: {a1: "new a1", a2: "new a2"}};
    

    但如果您只是想更新a1,该怎么办?为此,您需要obja 的副本(因为如果您不复制obj,您正在修改它所引用的树,违反了主体) :

    obj = Object.assign({}, obj, {a: Object.assign({}, obj.a, {a1: "updated a1"})});
    

    或具有传播属性:

    obj = {...obj, a: {...obj.a, a1: "updated a1"}};
    

    【讨论】:

      【解决方案2】:

      Redux 只是一个数据存储。因此,就其纯粹 的意义而言,redux 并不真正需要 不变性或深度克隆来作为一个概念工作。

      但是,redux 需要不可变性才能很好地与构建在它之上的 UI 框架(例如 React)一起工作。

      出于这个简单的原因:我的状态的哪些部分在上次查看框架时发生了变化?

      鉴于这个目标,您能看出深度克隆实际上是如何无济于事的吗?如果您查看一个已被深度克隆的对象,那么该对象的每个子部分现在在身份方面都不同 (===)。

      作为一个具体的例子,如果你运行以下代码:

      const bookstore = { name: "Jane's books", numBooks: 42 };
      const reduxData = { bookstore, employees: ['Ada', 'Bear'] };
      

      现在假设您只想更改书店的图书数量。

      如果你做了一个深拷贝,像这样:

      const reduxClone = JSON.parse(JSON.stringify(reduxData));
      reduxClone.bookstore.numBooks = 25;
      

      然后你会看到书店和员工现在都不同了:

      console.log(reduxData.bookstore === reduxClone.bookstore); // returns false
      console.log(reduxData.employees === reduxClone.employees); // returns false, but we haven't changed the employees
      

      这是一个问题,因为看起来一切都发生了变化。现在 React 必须重新渲染所有内容以查看是否有任何变化。

      正确的解决方案是使用简单的不变性规则。如果更改对象的值,则必须创建该对象的新副本。所以,既然我们想要一个新的 numBooks,我们需要创建一个新的书店。既然我们有一个新书店,我们需要做一个新的 redux 商店。

      const newBookstore = Object.assign({}, bookstore, {numBooks: 25});
      const shallowReduxClone = Object.assign({}, reduxData, {bookstore: newBookstore});
      

      现在,您会看到书店发生了变化(耶!),但员工却没有(双倍耶!)

      console.log(reduxData.bookstore === shallowReduxClone.bookstore); // returns false
      console.log(reduxData.employees === shallowReduxClone.employees); // returns true
      

      我希望这个例子有帮助。不变性允许您在进行更改时更改最少的对象量。如果您保证永远不会更改对象,那么您可以在您构建的其他树中重用该对象。在这个例子中,我们可以使用employees 对象两次,没有危险,因为我们承诺永远不会改变employees 对象。

      【讨论】:

        【解决方案3】:

        不变性意味着:我(作为开发人员)从不为对象属性分配新值;要么是因为编译器不允许,要么对象被冻结,要么我只是不这样做。相反,我创建了新对象。

        如果您始终牢记不应该改变对象并遵守它,那么您将永远不会修改现有对象的内容,即使您事先浅拷贝而不是深拷贝它(修改现有对象就是immuting code 允许防止,因为这使得代码的行为更容易预测)。

        因此,要创建不可变代码,您不需要深度复制。

        为什么要避免深度复制?

        • 更好的浅拷贝性能
        • 浅拷贝的内存占用更小

        无需深拷贝的不可变代码示例(在幕后,它使用Object.assign):

        const state = { firstName: 'please tell me', lastName: 'please tell me' }
        const modifiedState = { ...state, firstName: 'Bob' }
        

        当然,如果你浅拷贝而不是深拷贝一个对象,你可能会做错:

        const state = { firstName: 'please tell me', lastName: 'please tell me' }
        const modifiedState = state
        modifiedState.firstName = 'Bob' // violates immuting, because it changes the original state as well
        

        【讨论】:

          猜你喜欢
          • 2017-06-10
          • 2012-10-16
          • 2013-04-28
          • 1970-01-01
          • 2020-02-09
          • 1970-01-01
          • 2017-09-17
          • 2012-03-30
          • 2010-10-15
          相关资源
          最近更新 更多