JavaScript悬挂的入门知识

165 阅读4分钟

容器设计的基本原则

JavaScript类似于一种解释型语言。JS一次编译一行,然后运行这一行。为了确保每个函数都能访问其作用域中的所有变量,JS将变量从其定义的作用域中移到其上方的外层作用域中。

简介

我个人知道很多JavaScript(JS)开发者都忽略了一些比较基本的概念。我不能责怪他们;JS是一种古怪的语言,很多关于它的教育都跳过了一些更高层次的JS原则,比如事件循环,即使它们对写好JavaScript代码至关重要。在这篇文章中,我们将探讨扬帆,这是一个影响你所写的每一行代码的JS基础。

扬起主帆

简而言之,起吊是指JS编译器改变变量和函数在代码中的位置。我们稍后将探讨为什么会发生这种情况,但现在,请看一下这段代码。

"use strict" //enables strict mode, where undeclared variables can not be used
b = "Section is cool" //variable is changed here
alert(b) //variable is used here
var b //variable is declared here

你可以在这里的repl.it上运行它。正如你所看到的,这可以工作,而且警报Section is cool ,很好。但是为什么呢?这个变量是在代码的底部声明的。我们希望它要么不打印任何东西,要么打印未定义。试着把 "var b "这行注释掉,看看没有它就不会有警报。

这是一个提升的例子。JS将上述代码重组为。

"use strict"
var b //variable is declared here
b = "Section is cool" //variable is changed here
alert(b) //variable is used here

JavaScript将变量声明提升到我们代码的顶部。

为什么JavaScript会编辑你的代码

JS类似于解释型语言,它是一种即时编译型语言(但这不在本文的范围内)。JS每次编译一行,然后运行这一行。为了确保每个函数可以访问其作用域中的所有变量,JS将变量从其定义的作用域中移到其上方的外部作用域。当在一个作用域的中间而不是在该作用域的顶部定义新的变量时,这是必要的。

在C99之前,C语言的程序员必须在作用域的开头声明他们需要的所有局部变量,而不是其他地方。现在,现代语言支持在任何地方进行变量声明。

这里有另一个很好的提升的例子来帮助你理解这个概念。在这里试一试吧。

for(var i =  0; i <  5; i++){
	console.log(i)
}
console.log(i)

在真正的编译语言中,底部日志会抛出一个错误,因为i 只在for循环内声明,但JS将i 悬挂到外部范围--for循环之外,允许我们访问该变量。

通过提升,JS将上面的例子变成了这样。

var i //now initialized in the outer scope.
for(i = 0; i <  5; i++){
	console.log(i)
}
console.log(i)

情况变得更糟了

利用你对悬挂的了解,下面的代码会做什么?

function printMessage(){
	console.log(msg)
	var msg = "Section is cool"
}
printMessage()

如果你猜是undefined ,那你就对了。你可以在这里测试一下。

下面是JS看到的情况。

function printMessage(){
	var msg
	console.log(msg)
	msg = "Section is cool"
}
printMessage()

正如我们所看到的,只有声明被提升,而初始化则停留在原地。

提升函数

某些函数也会被提升。让我们看一下这段代码。

printMessage()
function printMessage(){
	var msg = "Section is cool"
	console.log(msg)
}

在其他语言中,编译器会因为我们在声明函数之前调用它而生气,但JS很好地将我们的函数移到了范围的顶部,并且在printMessage() 函数调用之前。你可以在这里查看实时版本。

像箭一样直的函数

使用ES6?ES6为语言添加的最棒的东西之一是箭头函数,比如。

const printMessage = ()  =>  {
	console.log("Section is cool")
}
printMessage()

这些看起来像普通函数,像普通函数一样工作,而且在大多数情况下,它们的行为也像普通函数一样。然而,它们并不像普通函数那样被提升。

让我们看看当我们把printMessage() 函数调用移到我们的函数上面时会发生什么,就像我们上面做的那样。

printMessage()
const printMessage = ()  =>  {
	console.log("Section is cool")
}

我们得到了很多红色的错误。在这里自己检查一下。

如果这仍然令人困惑,我不怪你。对于我们的最后一个例子,让我们检查一下let 。这是在JS中声明变量的其他方式之一。我们将重新使用之前的一个例子。

for(let i =  0; i <  5; i++){
	console.log(i)
}
console.log(i)

一如既往,你可以在这里试一试。

let 声明一个仅仅在范围内的变量。它'取消'了hoisting。这就是为什么运行上面的代码对0到4都很正常,但当我们试图访问范围之外的 ,却会出现错误。如果你记住了作用域, 这个关键字是非常有用的。i let

但是等等,还有更多

你会遇到更多关于悬挂的情况,虽然没有必要写代码来解释悬挂的所有诡计,但把这些知识记在脑海中还是很有帮助的。

吊装看起来很复杂,而且不可避免,确实如此,但现在你应该有更多的工具来更好地理解和编写我们都喜欢的这种古怪的语言。