方便吗?
真的很方便。无论是对于代码的读者还是编写代码的人本身都是如此。这并不是因为变量提升,而是因为函数提升。通过这种方式,您可以将辅助函数放在代码底部,将显示业务逻辑的更抽象的函数放在顶部。
我喜欢quora上关于同一主题的这个回答https://www.quora.com/Why-does-JavaScript-hoist-variables
换句话说,JavaScript实现了函数声明的提升,以便程序员不必强制将最内层函数放在脚本块的顶部,将最外层(顶级)函数放在底部。这种顺序,在ML语言(如LISP)中是痛苦的,因为程序员更喜欢从上到下阅读代码,而不是从下到上。像C / C ++这样的语言通过使用头文件和独立声明来解决此问题,而JavaScript没有。此外,提升对于实现相互递归是必需的。
var foo = function () { ... }
代替function foo() { ... }
。所以我认为函数提升和变量提升在技术上没有区别,只是一种概念上的区别。 - Bart Hofland嗯,每件事都有其优点和缺点,但在这种情况下,缺点似乎超过了优点。
在我看来,提升使代码更难理解。但这只是我的观点。当我编写代码时,我希望我的绑定在我声明它们的代码点处声明。这样,如果我错误地在声明之前使用它们,我就会得到通知。而且我肯定不想意外地声明两次,这是完全有效但也很令人困惑的。
我希望编译器/解释器在我做一些愚蠢的事情时能帮助我。提升允许我做太多方便和/或愚蠢的事情,可能导致数小时的复杂调试会话。因此,自ES6以来,我主要使用let
和const
而不是var
。如果可以避免提升,那就不要用它。;)
所以,对于“提升的(主要)优点是什么?”这个问题,我的答案是:“我真的不在乎。”
编辑:
好吧,也许我在这里有点太苛刻了。函数提升实际上可能是有用和方便的,可以提高代码的可读性。但在函数内部,我会尽量避免变量提升。
托管对于创建函数调用链非常有用。就像 RxJS 提供的那些一样。
var
时,作用域是函数体;使用 let
/const
时,作用域是最近的封闭 {}
块。由于 Javascript 中可能存在嵌套作用域,必须明确变量属于哪个块。如果你在特定的 块 中的 任何位置 提到了 var foo
/let foo
,那么该块就被保留为此变量的作用域。function () {
/**** scope of foo *****/
/*/
/*/ var foo;
/*/
/*/ function () {
/*/ /** scope of bar **/
/*/ /*/
/*/ /*/ var bar;
/*/ /*/
/*/ /******************/
/*/ }
/**********************/
}
如果变量作用域只从声明var ...
的确切行开始,那将会非常令人困惑;因此,函数的一半将有一个变量作用域,而另一半则有另一个。因此,如果在块中的任何地方存在var
/let
,那么该名称将被提升以涵盖整个块。
提升在某些情况下非常有用。例如,我们正在创建一个大型项目。它有很多帮助方法,通过将它们放在另一个脚本中进行分离。如果使用函数声明来声明它们,我们就不必担心脚本的顺序。否则,我们可能会遇到问题。
另一个用途是当我们使用递归时。考虑一个使用递归打印数字的函数。
(function print(n){
if(n === 0) return;
console.log(n)
print(n-1)
})(4);
print
函数递归调用自身的事实,即使没有提升也可以正常工作:const print = function (n) { if (n === 0) return; console.log(n); print(n - 1); };
同样是完全有效的。递归函数只有在它们是相互递归时才可能成为问题,例如当函数 A 调用函数 B 并且函数 B 再次调用函数 A 时。如果没有提升,创建这样的函数将会很棘手。(首先,您需要使用 let
创建两个函数的占位符变量,然后为它们分配函数体。这不是很直观。) - Bart Hofland