尝试重构此代码以仅使用不可变结构



我是函数式编程的学生。我仍然在戒掉旧的可变突变习惯。但有时我会卡住。好的,所以这里有一个问题 - 假设我们有以下闭包

const bookShelf = () => {
let books = []
const listBooks = () => books
const addBook = (book) => {
books = books.concat(book)
return function removeBook() { books = books.filter( b => b !== book ) }
}
return {addBook,listBooks}
}
const { addBook, listBooks } = bookShelf()
const removeMobyDick = addBook('Moby Dick')
const removeWalden = addBook('Walden')
removeWalden()
console.log(listBooks()) // ["Moby Dick"]

请注意,我有一个变异的对象:书籍。

我的问题是,我怎样才能重构这段代码,使书籍不可变,同时我却能达到相同的最终结果。如果需要,可以随意使用像Ramda这样的函数库。我在这里的天真想法是以某种方式使用递归来传递书籍的新值,然后将该版本传递回去。似乎有点过分了,所以我想向这个领域更有知识的人寻求帮助。

感谢您的见解!

只需将book常数留在您的bookshelf中即可。当然,这需要每次都创建一个新bookshelf,所以最简单的方法是books成为函数的参数:

function bookShelf(books) {
return {
listBooks() { return books },
addBook(book) { return bookShelf(books.concat([book])); }
}
}
const empty = bookShelf([]);
const shelfWithMobyDick = empty.addBook('Moby Dick');
const shelfWithMobyDickAndWalden = shelfWithMobyDick.addBook('Walden');
console.log(shelfWithMobyDick.listBooks());

如您所见,不需要removeBook函数 - 您只需使用尚未包含本书的旧值。

如果您希望能够从任意书架中删除刚刚添加的书籍,还可以返回新书架和删除器函数:

…,
addBook(book) {
return {
bookShelf: bookShelf(books.concat([book]));
removeThis(shelf) { return bookShelf(shelf.listBooks().filter(b => b !== book)); }
};
}

用作

const empty = bookShelf([]);
const {bookShelf: shelfWithMobyDick, removeThis: removeMobyDick} = empty.addBook('Moby Dick');
const {bookShelf: shelfWithMobyDickAndWalden, removeThis: removeWalden} = shelfWithMobyDick.addBook('Walden');
const shelfWithWalden = removeMobyDick(shelfWithMobyDickAndWalden);
console.log(shelfWithWalden.listBooks());

书架类型在这里似乎并没有真正完成任何事情,所以让我们把它做成一个列表(数组(。

let bookshelf = [];

现在看起来您想要一种方法来生成包含新项目的列表,以及从列表中删除该项目的方法。有点奇怪,但您可以通过在元组(数组(中返回两者来做到这一点:

const remove = (list, value) =>
list.filter(x => x !== value);
const addRemovable = (list, value) =>
[[...list, value], list => remove(list, value)];
let bookshelf = [];
let removeMobyDick;
let removeWalden;
[bookshelf, removeMobyDick] = addRemovable(bookshelf, 'Moby Dick');
[bookshelf, removeWalden] = addRemovable(bookshelf, 'Walden');
bookshelf = removeWalden(bookshelf);
console.log(bookshelf);

这看起来不太好,你可能不想写类似的东西,但它确实实现了与原版相同的目标。

最新更新