【问题标题】:Is this not a pure function?这不是纯函数吗?
【发布时间】:2019-05-15 13:07:02
【问题描述】:

我正在学习 Web 开发的状态管理,并遇到了这个具有如下纯函数的 redux 教程。然而声明: “action.todo.id = state.todos.length + 1;” 让我怀疑这个纯函数是……不纯的。 请赐教,谢谢!

export function rootReducer(state: IAppState, action): IAppState {

    switch (action.type) {
        case ADD_TODO:
            action.todo.id = state.todos.length + 1;    
            return Object.assign({}, state, {
                todos: state.todos.concat(Object.assign({}, action.todo)),
                lastUpdate: new Date()
            })

        case TOGGLE_TODO:
            var todo = state.todos.find(t => t.id === action.id);
            var index = state.todos.indexOf(todo);
            return Object.assign({}, state, {
                todos: [
                    ...state.todos.slice(0, index),
                    Object.assign({}, todo, {isCompleted: !todo.isCompleted}),
                    ...state.todos.slice(index+1)
                ],
                lastUpdate: new Date()
            })
        case REMOVE_TODO:
            return Object.assign({}, state, {
                todos: state.todos.filter(t => t.id !== action.id),
                lastUpdate: new Date()
            })
        case REMOVE_ALL_TODOS:
            return Object.assign({}, state, {
                todos: [],
                lastUpdate: new Date()
            })
    }
    return state;
}

【问题讨论】:

    标签: javascript redux state pure-function


    【解决方案1】:

    TL;DR - 不,不是。

    让我们来看看纯函数的定义。 来自维基百科:

    在计算机编程中,纯函数是具有 以下属性:

    1. 对于相同的参数,它的返回值是相同的(没有变化 局部静态变量、非局部变量、可变引用 来自 I/O 设备的参数或输入流)。

    2. 它的评价没有 副作用(局部静态变量没有突变,非局部 变量、可变引用参数或 I/O 流)。

    虽然您的函数确实符合第二个条件,但使用 new Date() - 会使其不纯。

    在您的情况下它不纯的原因是每个函数调用的日期都不同 - 无论传递的参数如何。

    为了使其更纯粹,您应该将日期作为附加参数传递,这将允许您对相同的输入具有相同的输出。

    Zaptree 还提到,改变您的项目 ID 的长度,即 action.todo.id = state.todos.length + 1 是不纯的,因为它可能会影响引用它的其他方。

    【讨论】:

    • “action.todo.id = state.todos.length + 1;”不是也会产生副作用,因为它会改变输入值吗?
    • 虽然您的陈述是 100% 正确的,但新的 Date() 代码不会导致 redux 出现问题。改变状态是一个问题,因为你可能会产生意想不到的副作用,因为 redux 如何通过进行浅层比较来确定是否需要进行渲染来优化性能。变异状态将是一个问题,但不是这样的。
    • @TLuu - 不,因为状态是作为参数传递的。如果您将 {todos: [1,2]} 之类的东西作为状态传递 - 您将始终获得 2 作为其 length 属性,因此您将获得相同的结果。作为旁注 - 增加列表的长度属性,而不实际添加项目 - 只是一种不好的做法。考虑将一个项目添加到列表中 - 并在创建期间为其分配 id。
    • @Zaptree - 关于 redux 错误断言及其强制不变性的事实有很多话要说,但是 OP 询问了函数的纯度。推断它是 Redux 的一部分对于这个答案应该无关紧要。
    • @silicakes 我只是为了给 TLuu 在他工作的上下文中一个解释而发表评论。此外,您关于可以改变参数的评论也不正确,它违反了您提到的规则 2。
    【解决方案2】:

    您非常正确地意识到以下代码是不纯的:

    action.todo.id = state.todos.length + 1;
    

    执行此操作的正确方法如下(像您一样使用 es5 语法):

    var newTodo = Object.assign({}, action.todo, {
      id: state.todos.length + 1
    });
    return Object.assign({}, state, {
        todos: state.todos.concat(Object.assign({}, newTodo)),
        lastUpdate: new Date()
    })
    

    在您的原始代码中,您基本上是在改变从动作传入的待办事项。

    【讨论】:

      猜你喜欢
      • 2019-10-05
      • 1970-01-01
      • 1970-01-01
      • 2021-05-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-04-12
      • 2018-04-30
      相关资源
      最近更新 更多