Dictionary 高效的Redux缩减器,避免不必要的对象拷贝

Dictionary 高效的Redux缩减器,避免不必要的对象拷贝,dictionary,functional-programming,ecmascript-6,functor,redux,Dictionary,Functional Programming,Ecmascript 6,Functor,Redux,我想我的问题也可以概括为 是否有一种惯用的ES6方法: array.map(identity)==array array.filter(i=>true)==array {obj…,attr:obj.attr}==obj 我知道,它在ES6中并没有像这样实现,但是否有一些可能的语法我遗漏了,或者有一些简单的帮助函数使这些属性为真而不诉诸于不可变的库 我使用Babel和新的JS特性,以及不可变的JS对象 我想知道如何使我的还原程序更有效,并生成更少不必要的对象副本 我对lib(Mori/Immut

我想我的问题也可以概括为

是否有一种惯用的ES6方法:

array.map(identity)==array

array.filter(i=>true)==array

{obj…,attr:obj.attr}==obj

我知道,它在ES6中并没有像这样实现,但是否有一些可能的语法我遗漏了,或者有一些简单的帮助函数使这些属性为真而不诉诸于不可变的库


我使用Babel和新的JS特性,以及不可变的JS对象

我想知道如何使我的还原程序更有效,并生成更少不必要的对象副本

我对lib(Mori/ImmutableJS)解决方案不感兴趣

我有一个管理分页列表的缩减器

pages
属性实际上是一个
Array[Array[item]]

这是我的减速机:

const initialState = {
  isLoading: false,
  pages: [],
  allStamplesLoaded: false
};

function reducer(state = initialState, event) {
  
  switch (event.name) {

    case Names.STAMPLE_DELETED:
      return {
        ...state,
        pages: removeStampleFromPages(state.pages,event.data.stampleId)
      };

    case Names.STAMPLE_UPDATED:
      return {
        ...state,
        pages: updateStampleInPages(state.pages,event.data.apiStample)
      };

    case Names.STAMPLE_PAGES_CLEANED:
      return {
        ...initialState,
      };

    case Names.STAMPLE_PAGE_REQUESTED:
      return {
        ...state,
        isLoading: true
      };

    case Names.STAMPLE_PAGE_LOADED:
      const {stamplePage,isLastPage} = event.data;
      return {
        ...state,
        isLoading: false,
        pages: [...state.pages, stamplePage],
        isLastPage: isLastPage
      };

    case Names.STAMPLE_PAGE_ERROR:
      return {
        ...state,
        isLoading: false
      };

    default:
      return state;
  }
}
我还具有以下辅助功能:

function removeStampleFromPages(pages,deletedStampleId) {
  return pages.map(page => {
    return page.filter(apiStample => apiStample != deletedStampleId)
  })
}
function updateStampleInPages(pages,newApiStample) {
  return pages.map(page => {
    return updateStampleInPage(page,newApiStample);
  })
}
function updateStampleInPage(page,newApiStample) {
  return page.map(apiStample => {
    if (apiStample.id === newApiStample.id) {
      return newApiStample;
    }
    else {
      return apiStample;
    }
  })
}

正如您所注意到的,每次触发诸如
STAMPLE_UPDATED
之类的事件时,my reducer都会返回一个新状态,返回一个新的页面数组,即使数组中的任何项都没有实际更新。这会创建不必要的对象复制和GC


我不想过早地对此进行优化,也不想在我的应用程序中引入一个不可变的库,但我想知道是否有任何惯用的ES6方法来解决这个问题?

不可变的数据结构,如immutable.js和Mori使用了一个巧妙的技巧来避免一直重新创建整个结构

该策略相当简单:当您更新一个属性时,深入到该属性,对其进行更改,并从该节点到根节点重写所有属性

假设您希望在以下状态下将属性
c
更改为
4

const state1 = {
  a: {
    b: {
      c: 1
    },
    d: [2, 3, 4],
    e: 'Hello'
  }
}
第一步是将
c
更新为
4
。之后,您需要创建

  • b
    的新对象(因为
    c
    已更改)
  • a
    的新对象(因为
    b
    已更改)
  • 和状态的新对象(因为
    a
    已更改)
  • 您的新状态如下所示(对象旁边的
    *
    表示该对象已被重新创建)

    请注意,
    d
    e
    如何未被触摸

    现在,您可以验证工作是否正常:

    state1 === state2 // false
    state1.a === state2.a // false
    state1.a.b === state2.a.b //false
    state1.d === state2.d // true
    state1.e === state2.e // true
    
    您可能注意到
    state1
    state2
    之间共享
    d
    e

    您可以使用类似的策略共享您所在州的信息,而无需一直重新创建一个全新的州

    关于你最初的问题:

    array.map(identity) !== array
    array.filter(i => true) !== array
    {obj..., attr: obj.attr} !== obj
    
    答案很简单

    创建数组或对象时,Javascript VM会在内部为该对象分配一个标识符。标识符是增量的,因此没有两个数组/对象是相同的

    对数组或对象执行标识检查时,仅检查内部标识符是否匹配

    a = [] // internal identifier 1
    [] // internal identifier to 2
    b = [] // internal identifier 3
    a === b // id 1 === id 3 is FALSE!
    a === a // id 1 === id 1 is TRUE!
    

    它是这样工作的,因为方法就是这样定义的。ES6在这里没有提供替代方案或任何有帮助的东西。您是否过度关注这里的优化?您是否尝试过对减速器进行基准测试,以确定这是否需要太长时间,或者进行内存分析,看看这是否真的是一个内存问题?我认为有一个库可以做到这一点。当我专注于优化我的应用程序时,我可能会这样做:)我知道不可变结构和身份比较是如何工作的,我正在寻找一种方法来解决所描述的用例,而不是解释为什么
    {obj…,attr:obj.attr}!==obj
    。我重写了我的问题以使它更清楚
    a = [] // internal identifier 1
    [] // internal identifier to 2
    b = [] // internal identifier 3
    a === b // id 1 === id 3 is FALSE!
    a === a // id 1 === id 1 is TRUE!