【问题标题】:Proper use of const for defining functions正确使用 const 定义函数
【发布时间】:2016-01-07 13:06:33
【问题描述】:

对于在 JavaScript 中使用 const 可以设置哪些类型的值有任何限制,尤其是函数?这是有效的吗?当然,它确实有效,但出于任何原因,它是否被认为是不好的做法?

const doSomething = () => {
   ...
}

应该在 ES6 中以这种方式定义所有函数吗?如果是这样的话,这似乎没有流行起来。

【问题讨论】:

  • 您似乎问了多个问题:1)“我很感兴趣,如果在 JavaScript 中使用 const 可以设置哪些类型的值有任何限制” 2) “这有效吗?” 是的。 3) “无论出于何种原因,它都被认为是不好的做法吗?” 我想它存在的时间还不够长,无法对此发表任何看法,但我不明白为什么这应该是垫练习。它与var doSomething = <function def>; 没有太大区别。 4) “所有函数都应该在 ES6 中以这种方式定义吗?” 对我来说似乎很麻烦。我喜欢函数声明。每个人都是自己的。
  • 在我看来(观点,不是事实),如果你想禁止重新定义函数,这是有道理的。无论它是理智的,还是它是否有一些功能用途——这是值得商榷的。如果您认为它适合您的使用场景,我不认为有人会争论您的决定并认为这是不好的做法。
  • 我想问题是你想用const 实现什么。您想防止自己覆盖该功能吗?我假设你知道你的代码无论如何都不要这样做。你想表达doSomething 的意图,即它拥有一个函数并且不改变它的值吗?我认为函数声明也清楚地传达了这个意图。因此,如果您需要“运行时保护”以防止覆盖,那就去做吧。否则我看不到太多好处。当然如果你主要使用var foo = function() {};,我会使用const而不是var
  • @FelixKling,“我假设你知道你的代码无论如何都不要这样做。” - 这是一个非常糟糕的论点。否则,const 毫无意义。
  • 我想补充一点,使用const 而不是function 来定义“函数”是可读性的回归。在中断 4 到 5 年之后,我刚刚再次启动 JS,遇到了一堆 const 来声明 function 代码,它正在破坏我的大脑。

标签: javascript function constants ecmascript-6


【解决方案1】:

你做的没有问题,但是你一定要记住函数声明和函数表达式的区别。

一个函数声明,即:

function doSomething () {}

完全提升到作用域的顶部(和letconst 一样,它们也是块作用域)。

这意味着以下将起作用:

doSomething() // works!
function doSomething() {}

一个函数表达式,即:

[const | let | var] = function () {} (or () =>

是创建一个匿名函数 (function () {}) 并创建一个变量,然后将该匿名函数赋值给该变量。

因此,围绕范围内变量提升的常规规则——块范围变量(letconst)不会像 undefined 一样提升到其块范围的顶部。

这意味着:

if (true) {
    doSomething() // will fail
    const doSomething = function () {}
}

将失败,因为doSomething 未定义。 (它会抛出一个ReferenceError

如果您切换到使用var,您将获得变量的提升,但它将被初始化为undefined,因此上面的代码块仍然无法工作。 (这将抛出一个TypeError,因为doSomething 在你调用它时不是一个函数)

就标准做法而言,您应该始终使用适合工作的工具。

Axel Rauschmayer 有一篇关于范围和提升(包括 es6 语义)的精彩帖子:Variables and Scoping in ES6

【讨论】:

  • 想补充一点,es6 类在内部使用 const 来保护类名不被在类中重新分配。
  • a function a(){console.log(this);}b const a=_=>{console.log(this);} 之间的一个细微差别是如果您在 a 中将其称为 a.call(someVar);,它将在 b 中打印 someVar,它将打印window
【解决方案2】:

虽然使用const 来定义函数似乎是一种 hack,但它具有一些使其优越的巨大优势(在我看来)

  1. 它使函数不可变,因此您不必担心该函数会被其他代码更改。

  2. 您可以使用粗箭头语法,它更短且更简洁。

  3. 使用箭头函数为您处理this 绑定。

function 示例

// define a function
function add(x, y) { return x + y; }

// use it
console.log(add(1, 2)); // 3

// oops, someone mutated your function
add = function (x, y) { return x - y; };

// now this is not what you expected
console.log(add(1, 2)); // -1

const 相同的示例

// define a function (wow! that is 8 chars shorter)
const add = (x, y) => x + y;

// use it
console.log(add(1, 2)); // 3

// someone tries to mutate the function
add = (x, y) => x - y; // Uncaught TypeError: Assignment to constant variable.
// the intruder fails and your function remains unchanged

【讨论】:

  • 1.不变性是一个真正的好处,但很少有人真正覆盖一个函数。 2. 除非您的函数可以是表达式,否则胖箭头语法不会更短。 function f(x, y) { 是 18 个字符,const f = (x, y) => { 是 21 个字符,所以要长 3 个字符。 3. 仅当函数定义在方法(或其他有意义的函数)中时,保持此绑定才有意义。在顶级脚本中是没有意义的。我并不是说你错了,只是你提到的原因并不是很相关。
  • 老式函数语法的一个优点是在调试期间它有一个名称。
  • 我会使用 linter 来防止重新绑定函数声明。
  • @Nakedible - 我也更喜欢使用function,但在您的比较中,您忘记了function 需要return,而另一种方法则不需要。 (除非我不知道捷径?)
【解决方案3】:

这个问题被问到已经三年了,但我现在才遇到这个问题。由于这个答案离堆栈太远了,请允许我重复一遍:

问:我感兴趣的是对哪些类型的值有任何限制 在 JavaScript 中使用 const 设置——尤其是函数。这是有效的吗? 诚然它确实有效,但 对于任何人来说都被认为是不好的做法 原因?

在观察到一位多产的 JavaScript 程序员总是使用 const 声明 functions 后,我有动力做一些研究,即使没有明显的原因/好处。

在回答“出于任何原因它被认为是不好的做法吗?”让我说,IMO,是的,或者至少,使用有 优点 function 声明。

在我看来,这主要是偏好和风格的问题。上面提出了一些很好的论点,但没有一个像本文中所做的那样清楚:

Constant confusion: why I still use JavaScript function statements by medium.freecodecamp.org/Bill Sourour,JavaScript 大师、顾问和教师。

我敦促大家阅读那篇文章,即使您已经做出决定。

以下是要点:

函数语句比 [const] 函数有两个明显的优势 表达式:

优势 #1:意图清晰

扫描时 一天几千行代码,能搞清楚是很有用的 尽可能快速轻松地实现程序员的意图。

优势 #2:声明顺序 == 执行顺序

理想情况下,我想或多或少地按照我的顺序声明我的代码 预计它会被执行。

这对我来说是最重要的:使用 const 声明的任何值 关键字在执行到达之前是不可访问的。

我刚刚在上面描述的内容迫使我们编写看起来像 上下翻转。我们必须从最底层的函数开始工作 我们一路向上。

我的大脑不是这样工作的。我想要细节之前的上下文。

大多数代码都是由人类编写的。所以大多数人的想法是有道理的 理解顺序大致遵循大多数代码的执行顺序。

【讨论】:

  • 你能稍微评论一下意图的清晰性吗?我得到#2,但据我所知,#1 只是#2 的(前?)重申。我可以想到一个案例,即具有自己的 defaultErrorHandler const 的函数被分配为匿名函数,然后我可以从 Promise 处理程序调用该函数。这将使我能够根据需要在函数中选择性地“覆盖”这个默认错误处理程序。有些只需要返回一个错误对象,而另一些则需要返回一个 http 响应,不同的详细程度。然而,代码结构可能是一种熟悉的模式。
  • 也许我的想法太复杂了!只是围绕一些新的做法,还没有花很多时间来实现它们。只是发现我真的很喜欢.then( res.success, res.error ),而不是我声明要调用res.success(value); 的匿名函数。拥有一个 .then( ..., defaultErrorHandler) 通用模式可能会很好,定义一个顶级的 defaultErrorHandler 函数,并且可以根据需要在函数范围内声明一个 const defaultErrorHandler = error => { ... }
  • @JakeT。 RE “你能稍微评论一下意图的清晰度吗?”。好吧,首先,该声明不是我的我,而是那篇文章的作者 freecodecamp.org/Bill Sourour。但这对我来说是真正的常识。如果我阅读“函数明天()”,那么我立即知道它是一个函数。但是如果我读到“const明天=()=>”,我会停下来,在脑海中解析一下语法,最终确定,好的,是的,它是一个函数。
  • 另外一件事,如果我有一个别人写的很长的脚本,我想查看所有的功能,我可以在“功能”上快速搜索找到它们。或者更好的是,我可以编写一个快速的 JS RegEx 来提取所有函数。 IMO,“const”语句仅适用于不会改变的数据(不是函数)。
  • @JMichaelTX - 我明白你对阅读function tomorrow() { }const tomorrow = () => { } 的看法,但这肯定只是更习惯于看到一个而不是另一个的情况?一两天后,您应该适应同样能够发现新模式的能力......此外,如果您在搜索或正则表达式期间使用这些功能,将 function 替换为 ) => { 也一样好,如果不是更好的话,因为你避免任何碰巧在其中包含单词 function 的 cmets。
【解决方案4】:

使用const 有一些非常重要的好处,有些人会说应该尽可能使用它,因为它是经过深思熟虑和指示性的。

据我所知,它是 JavaScript 中最具指示性和可预测性的变量声明,也是最有用的声明之一,因为它的约束程度。为什么?因为它消除了varlet 声明可用的一些可能性。

当您阅读const 时,您能推断出什么?只需阅读const 声明语句,并且无需扫描对该变量的其他引用,您就可以了解以下所有内容:

  • 值绑定到该变量(尽管它的底层对象不是完全不可变的)
  • 不能在其直接包含的块之外访问它
  • 由于时间死区 (TDZ) 规则,在声明之前永远不会访问绑定。

以下引述来自an article 争论letconst 的好处。它还更直接地回答了您关于关键字约束/限制的问题:

letconst 提供的约束是一种使代码更易于理解的强大方法。尝试在您编写的代码中累积尽可能多的这些约束。限制一段代码含义的声明性约束越多,人类在未来阅读、解析和理解一段代码就越容易和更快。

当然,const 声明的规则比 var 声明的规则多:块范围,TDZ,在声明时赋值,没有重新赋值。而var 语句仅表示函数范围。然而,规则计数并不能提供很多洞察力。最好根据复杂性来衡量这些规则:规则是增加还是减少复杂性?在const 的情况下,块作用域意味着比函数作用域更窄的作用域,TDZ 意味着我们不需要从声明中向后扫描作用域以便在声明之前发现使用情况,而赋值规则意味着绑定将始终保留相同的引用。

受约束的语句越多,一段代码就越简单。当我们为语句的含义添加约束时,代码变得不那么不可预测了。这是静态类型程序通常比动态类型程序更容易阅读的最大原因之一。静态类型对程序编写者有很大的限制,但它也对程序的解释方式有很大的限制,使其代码更容易理解。

考虑到这些论点,建议您尽可能使用const,因为这是让我们思考的可能性最小的语句。

来源:https://ponyfoo.com/articles/var-let-const

【讨论】:

  • 这个问题是关于function vs const 而不是var vs const。而这个答案本身表明使用const 来定义function 不是可读性增强,实际上是现代JS 中的可读性回归。因为这个答案已经混淆了变量,而不是函数。
【解决方案5】:

在某些特殊情况下,arrow functions 无法解决问题:

  1. 如果我们要更改外部 API 的方法,并且需要对象的引用。

  2. 如果我们需要使用 function 表达式独有的特殊关键字:argumentsyieldbind 等。 了解更多信息: Arrow function expression limitations

示例:

我在Highcharts API 中将此函数指定为事件处理程序。 它是由库触发的,因此 this 关键字应该与特定对象匹配。

export const handleCrosshairHover = function (proceed, e) {
  const axis = this; // axis object
  proceed.apply(axis, Array.prototype.slice.call(arguments, 1)); // method arguments
};

使用箭头函数,this 将匹配声明范围,我们将无法访问 API obj:

export const handleCrosshairHover = (proceed, e) => {
  const axis = this; // this = undefined
  proceed.apply(axis, Array.prototype.slice.call(arguments, 1)); // compilation error
};

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-01-21
    • 2012-09-25
    • 2020-04-24
    • 1970-01-01
    • 2020-08-26
    • 1970-01-01
    相关资源
    最近更新 更多