为什么JavaScript最初没有实现块级作用域?

72

我读过并通过自己的经验发现,JavaScript 没有块级作用域。假设这种语言被设计成这种方式是有原因的,请问可以向我解释一下这个原因吗?

我在谷歌和这里找到的文章只是重申 JS 具有函数作用域而不是块级作用域,但没有解释为什么会这样。我很好奇实际上是什么原因导致了这种情况。


4
JavaScript中的函数式编程风格似乎不太被鼓励,再加上JS是一个仓促设计的概念。但是在ES6中将会引入let,所以我们很快就会拥有块级作用域。Firefox已经支持let - elclanrs
该语言的创始人已经回答了这个问题,@Aadit和我自己的解释似乎涵盖了任何其他问题,因此我认为它不再仅仅是意见。 - mplungjan
4个回答

114

将我的评论转换为答案

创建者的选择:我在推特上@了Brendan,得到了以下回答

@mplungjan 10天时间不足以实现块级作用域。此外,那个90年代中期的许多“脚本语言”范围很小,并且后来才逐渐增加。


话虽如此,以下是一些相关要点:

重要提示:ECMAScript2015(第6版)之前的JavaScript没有块级作用域。在块内引入的变量作用域限于包含函数或脚本,并且设置它们的效果会持续超出块本身。换句话说,块语句不会引入作用域。虽然“独立”的块是有效的语法,但是你不应该在JavaScript中使用独立块,因为它们不会像C或Java中的这些块那样做你认为它们会做的任何事情。

我们可以通过创建新函数并立即调用它们来人为地引入作用域。

letconst声明的变量被提升,但它们不会像var一样初始化为undefined。因此,在分配值之前引用letconst声明的变量会引发ReferenceError。

在同一块级别作用域内重新声明相同的变量会引发SyntaxError。


我之前并没有完全理解 "letconst 声明的变量会被提升但不会被初始化为 undefined" 这句话,直到我看了这个代码示例 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let#Another_example_of_temporal_dead_zone_combined_with_lexical_scoping 。你是在谈论暂时性死区吗? - christo8989

15

截至2015年的新回答。 ES6在使用letconst关键字定义变量时具有块级作用域。


2
不幸的是,一些用户可能仍然停留在2010年,因此依赖全新的语言特性并不是最佳实践。 - riv
1
@riv - JavaScript 不仅仅用于在浏览器中编写最低公共分母的代码。它可以在像 node.js 这样的环境中使用,您可以控制整个执行环境并现在可以使用 let。或者,您可以使用其中一个转译器,让您在 ES6 中编写代码,然后将其转换为 ES5 兼容代码,以便在旧版浏览器中运行。 - jfriend00
不是一个新答案,因为JS在ES3中使用try/catch块作用域。 - Trung
@Trung - 你能提供一个解释吗?我没有看到任何有关变量定义的块作用域的证据。 - jfriend00
1
@Trung - 这是一个非常特殊的情况(仅在catch处理程序中),这不太可能是OP所问的,并且不能以任何通用方式在ES3常规块中创建自己的块作用域变量。如果您认为这回答了OP的问题,请随意提供您自己的答案。 - jfriend00
显示剩余3条评论

11

以下是未实施块级作用域的原因:

  1. 这使得语言更容易实现。JavaScript 最初被设计为一种编写交互式 Web 应用程序的语言。因此,它需要保持小巧且易于实现。
  2. 块级作用域会对像 JavaScript 这样的动态语言产生性能影响。这是因为当您尝试访问当前范围之外的某个变量时,JavaScript 首先检查当前范围,然后检查父范围,依此类推,直到找到该变量或到达末尾。因此,引入块级作用域将使循环和嵌套循环中的变量访问非常缓慢。
  3. 缺少块级作用域使编写程序更容易。例如,假设您只想在特定条件为真时创建变量。在 JavaScript 中,您只需在 if 语句内声明并定义变量。在 C 等语言中,您必须在 if 语句外声明变量并在 if 语句内定义变量。
  4. 缺少块级作用域允许声明被提升。这在函数声明的情况下特别有用。例如,请参见此 fiddle:http://jsfiddle.net/L6SgM/(但请注意,此示例在 Firefox 中无法正常工作)。
  5. 由于 JavaScript 支持一级函数表达式,因此我们不需要块级作用域。它们可以使用立即调用函数表达式(IIFE)进行模拟。

3
“块级作用域会对像JavaScript这样的动态语言产生性能影响。” -- 这是一种实现的属性,而不是语言本身的属性。也许它在最初的JavaScript实现中存在,但是过去十年编写的任何引擎都不应该受到它的影响。 - Marijn
严格来说,我们并不比一开始需要JavaScript一样“需要”块作用域...幸运的是,ECMA团队似乎通过添加对块作用域的支持(通过'let'关键字)不同意这种观点...更简单的语言结构总是受欢迎的。 - Jorge Garcia
3
我能否提供证据证明 Brendan Eich 没有在 JavaScript 中实现块级作用域,以支持你所提到的 5 个原因?我怀疑你将历史原因(#1)与你的原因(#2至#5)混淆了。 第三点是错误的:变量总是被创建的,只是它的初始化是有条件的(请参见:https://jsfiddle.net/paercebal/yvwhu7r0/)。第四条是伪命题(在 Chrome 中添加“use strict”会导致错误:“Uncaught SyntaxError: In strict mode code, functions can only be declared at top level or immediately within another function.”)。第五点是一个肮脏的解决方法... - paercebal
它可能会让编写程序更容易,但肯定不会让维护它们更轻松。 - mbomb007

0

有很多原因,但一些我想到的是为了帮助解析/调试使用对象字面量(有时看起来像块)的代码,并简化本地变量的垃圾收集。

我希望承诺的支持(例如在此处讨论:http://esdiscuss.org/notes/2012-07-25)能够得到实现,因为这将非常方便,可以使用仅限于单个循环的本地变量,例如i


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接