手记

在JavaScript中为什么不要修改函数的输入参数

最近,我收到一个合并请求代码,其中多个函数都以同一个对象作为输入参数,将该对象作为共享状态,而有些函数在它们的逻辑中修改该对象的属性。

这种形成的最小抽象如下:

    const main = () => {  
      const obj = { ... }; // 初始化obj的属性  

      funcA(obj);  
      funcB(obj);  
      funcC(obj);  
    }  

    // 下面的函数基于obj的属性进行逻辑处理  
    // 并且根据需要更新obj的一些属性  
    const funcA = (obj) => { ... }  
    const funcB = (obj) => { ... }  
    const funcC = (obj) => { ... }

我没有找到一篇好的文章来解释为什么这是一个不良做法,所以我写了这篇文章来收集这些原因:它让代码难以修改、阅读和测试。

1 它让代码难以改动.

如上面的例子所示,这三个函数都依靠共享的 obj 来运作,每个函数都需要 obj 的最新状态。这意味着 funcB 认为 funcA 已经正确设置了或更新了 obj,而 funcC 则依赖于 funcAfuncB 谨慎地修改 obj,而不影响其他不应修改的属性。换句话说,这些函数紧密相连,并且需要严格的执行顺序。

几周后,如果我们需要在工作流中加入一个 funcD,我们需要仔细考虑将其放在哪里:它应该放在 funcAfuncB 之间,还是应该放在 funcC 后面?如果我们不阅读并跟踪 obj 在所有相关函数中的每处修改情况,就无法确定 funcD 的放置位置而不破坏现有逻辑。

同样,我们能否调整 funcBfuncC 的顺序,或者让它们并行执行?没有详细调查,这一点还不清楚。同样地,如果 funcA 本来只改变 objpropA,我们能否也同时改变 propB?同样,不完全理解这些内容的话,我们无法预测这会如何影响现有代码的行为。

2 让代码难以理解

正如之前提到的,每次要修改这种代码时,必须仔细阅读并彻底理解;否则,改动可能不起作用,或者影响现有代码的行为。

达到这种完全的理解并非易事。仅仅通过查看每一行代码就预测obj在任意时刻的最新状态是相当困难的。相反,我们必须从头到尾跟踪代码,跟随每一个if…else分支和嵌套的函数调用,以理解obj何时被访问、使用了什么值、何时被修改以及原因。

任何误解或忽略条件都可能导致严重的逻辑混淆,从而导致在如何使用或与现有代码互动时做出糟糕的决定。每个函数中的逻辑分支越多,整个代码的行为就越难以理解,所需的理解和阅读时间也会增加。

3 这使得代码难以进行测试

为了评估测试一个函数难度,让我们考虑最简单的例子:纯粹的函数。对于已知输入,我们可以简单地检查输出是否符合预期。

当我们使用一个共享的 obj,它有许多属性时,我们必须考虑到所有可能的属性组合,以覆盖不同的分支。

尤其是如果 obj 的某些属性在每个函数中并没有被明确使用,这样一来,测试用例的设计会变得非常复杂:我们是否应该为未使用的属性分配随机值?若有些属性是隐式使用的(例如,通过反射、运行时动态属性访问,或传递给嵌套函数或第三方库),又该怎么办?

除此之外,这些函数修改输入的 obj 而不是返回结果,因此哪些属性应该被修改而哪些不应该被修改就变得非常不清楚。此外,因为这些更改通常是基于条件的,很容易忽略一些边界条件的检查。

最后,这就是结论

请避免在JavaScript中修改函数的输入参数,这样会使代码难以更改、理解和测试。

简单英语

感谢你加入__In Plain English网站_!在你离开之前,想对你说:

0人推荐
随时随地看视频
慕课网APP