我是否应该在该功能中放置仅在另一个功能中使用的功能?


30

具体来说,我正在用JavaScript编写。

假设我的主要功能是功能A。如果功能A多次调用功能B,但在其他任何地方都没有使用功能B,那么我是否应该将功能B放在功能A中?

那是好习惯吗?还是应该将功能B与功能A放在相同的范围内?

Answers:


32

我通常支持嵌套函数,尤其是在JavaScript中。

  • 在JavaScript中,限制功能可见性的唯一方法是将其嵌套在另一个功能中。
  • 辅助函数是一个私有的实现细节。将其置于相同的范围类似于将类的私有功能公开。
  • 如果事实证明它具有更广泛的用途,则很容易移出该目录,因为您可以确信该助手当前仅被该功能使用。换句话说,它使您的代码更具凝聚力。
  • 您通常可以消除参数,这会使函数签名和实现的详细程度降低。这与使变量全局化不同。这更像是在私有方法中使用类成员。
  • 您可以为辅助函数指定更好,更简单的名称,而不必担心冲突。
  • 助手功能更容易找到。是的,有些工具可以为您提供帮助。稍微将眼睛向上移到屏幕上仍然比较容易。
  • 代码自然形成了一种抽象树:根基上有一些通用功能,可分为几个实现细节。如果您使用具有功能折叠/折叠功能的编辑器,则嵌套会在同一抽象级别上创建紧密相关的功能的层次结构。这样就很容易在您需要的级别上研究代码并隐藏细节。

我认为,很多程序员要么是C / C ++ / Java的传统,就是由其他程序员所教,这一事实引起了很多反对。嵌套函数看起来不太自然,因为我们在学习编程时没有太多接触它们。这并不意味着它们没有用。


14

由于多种原因,您应该将其置于全球范围内。

  • 将辅助函数嵌套到调用方中会增加调用方的长度。函数长度几乎总是一个负指标;简短的函数更易于理解,记忆,调试和维护。

  • 如果辅助函数的名称合理,则只需读取该名称即可,而无需查看附近的定义。如果确实需要查看帮助程序定义以了解调用程序功能,则该调用程序执行的过多或正在同时处理太多的抽象级别。

  • 如果使助手全局可用,则使该助手全局可用会允许其他函数调用它。如果没有可用的帮助程序,则很容易将其剪切和粘贴,或者忘记并重新实现,效果很差,或者使另一个功能的执行时间超出了必需的时间。

  • 嵌套辅助函数会增加在没有声明的情况下使用调用方作用域中的变量的诱惑,因此不清楚辅助函数的输入和输出是什么。如果某个功能没有明确说明其操作的数据以及其产生的影响,则通常表明职责不明确。将帮助程序屁股声明为独立函数会迫使您知道其实际功能。

编辑

事实证明,这是一个比我想象的更具争议性的问题。澄清:

在JavaScript中,大文件扩展功能通常扮演类的角色,因为该语言不提供任何其他范围限制机制。当然,辅助函数应该放在此类准类内部,而不是外部类。

而且,关于重用更容易的前提是,假设子例程的确得到了更广泛的使用,则您愿意将其完全移出原处并将其放置在适当的位置,例如字符串实用程序库或放入全局配置注册表中。如果您不想像这样对代码进行排序,那么您最好嵌套该子例程,就像使用普通的“ 方法对象 ”以更具“阻塞性”的语言进行操作一样。


谢谢,所有非常好的观点。另外,我通常应该如何订购我的功能?例如,目前,对于小型程序,我只是按字母顺序对其进行排序。但是我应该将辅助功能和调用者功能组合在一起吗?我猜我至少应该根据应用程序的哪些部分分解功能,例如?
加里

我已经很久没有打扰订购方法的定义了。如果您以任何专业水平进行编程,那么您都应该拥有一个可以通过几次击键跳到任何定义的环境。因此,简短的方法很有用,但是简短甚至是有序的类文件几乎没有价值。(当然,JavaScript曾经要求将所有定义置于其使用之上,否则从技术上讲,结果将是未定义的行为-无法回忆是否仍然存在。)
Kilian Foth 2014年

2
这是一个很好的答案。使用内部函数时,很多人都不会考虑破坏封装的问题。
sbichenko 2014年

2
全局变量是一种代码气味。它们导致不必要的耦合,从而阻止了重构,导致名称冲突,暴露了实现细节等。这在Javascript中尤其糟糕,因为所有变量都是可变的,代码通常直接从第三方加载,因此(尚未)广泛使用的模块系统,甚至广泛使用“ let”的实现。在这样的敌对环境中,我们唯一的防御策略是将功能封装在单发功能内。
Warbo

1
“履行类的角色”试图将JS当作其他东西来编写。什么是“阶级的角色”?类型检查?模块化?分阶段编译?遗产?这个问题涉及范围界定,所以我假设您是指类的粗略范围界定规则。这简直就是白费:应该如何限制类的作用域?PHP使它们成为全局的,不提供封装。Python用词法作用域化类,但是如果您在词法作用域的块中,为什么将所有内容包装在另一个类作用域的块中?JS没有这种冗余:只需使用所需的词法作用域即可。
Warbo 2014年

8

我将提出第三条路径,将两个函数都放在一个闭包中。它看起来像:

var functionA = (function(){
    function functionB() {
        // do stuff...
    }

    function functionA() {
        // do stuff...
        functionB();
        // do stuff...
    }

    return functionA;
})();

我们通过将两个函数的声明包装在IIFE中来创建闭包。IIFE的返回值是公共函数,存储在函数名称的变量中。可以用与声明为全局函数(即)完全相同的方式调用public函数functionA()。请注意,返回值是function,而不是对该函数的调用,因此最后没有paren。

通过像这样将两个函数包装起来,functionB现在是完全私有的,并且不能在闭包外部访问,而仅对可见functionA。它不弄乱全局命名空间,不是混乱的定义functionA


0

在函数A中定义函数B可使函数B访问A的内部变量。

因此,在函数A中定义的函数B会给人印象(或至少是可能性),即B正在使用或更改A的局部变量。在A之外定义B时,很明显B没有。

因此,为使代码清晰起见,仅当B需要访问A的局部变量时,才在A内定义B。如果B的明确目的与A无关,那么我肯定会在A之外定义它。


-1

您不应该嵌套它,因为否则每次调用外部函数时都会重新创建嵌套函数。


-3

由于在其他任何地方都没有使用函数B,因此您最好将其作为函数A的内部函数,因为这是它存在的唯一原因。


1
在之前的3个答案中所提出和解释的观点看来,这似乎并没有增加任何实质性内容
t
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.