我想我的问题也可以概括为类似
是否有惯用的ES6方式:
array.map(identity) === array
?
array.filter(i => true) === array
?
{obj..., attr: obj.attr} === obj
?
我知道,它还没有像ES6那样实现,但是我是否缺少一些可能的语法或简单的辅助函数,以便在不诉诸不可变的lib的情况下使这些属性成为现实?
我使用Babel和新的JS功能,以及不可变的js对象。
我想知道如何使我的Reducer更有效率并减少不必要的对象副本
我对lib(Mori / ImmutableJS)解决方案不感兴趣。
我有一个管理分页列表的reducer。
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
等事件时,我的reducer总是会返回一个新的状态,其中包含一个新的数组数组,即使数组的所有项都不是更新。这会创建不必要的对象复制和GC。
我不想过早地优化它,也不会在我的应用中引入不可变的库,但我想知道是否有任何惯用的ES6方法来解决这个问题?
答案 0 :(得分:1)
Immutable.js和Mori等不可变数据结构使用了一个聪明的技巧,以避免一直重建整个结构。
策略非常简单:当您向属性更新属性时,更改它并将所有属性从此节点重新包装到根节点。
假设您希望在以下状态下将属性c
更改为4
:
const state1 = {
a: {
b: {
c: 1
},
d: [2, 3, 4],
e: 'Hello'
}
}
第一步是将c
更新为4
。之后你需要创建
b
的新对象(因为c
已更改)a
的新对象(因为b
已更改)a
已更改)。您的新状态将如下所示(对象旁边的*
表示该对象已重新创建)
const state2 = *{
a: *{
b: *{
c: 4
},
d: [2, 3, 4],
e: 'Hello'
}
}
请注意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
您可能会注意到d
和e
之间共享state1
和state2
。
您可以使用类似的策略在您的州内共享信息,而无需一直重新创建一个全新的状态。
关于你的初步问题:
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!