本文主要介绍JavaScript中提升(变量提升和函数声明提升)的相关内容,并分享给大家参考学习。以下就不多说了。我们来看看详细的介绍。
如何将函数声明/变量“移动”到作用域的顶部。
术语“提升”在许多JavaScript博客文章中用于解释标识符的解析。实际上,术语“提升”是用来解释变量和函数声明如何被提升到函数或全局范围的顶部。在任何JavaScript文档中都找不到这个术语。当我们说“吊装”时,我们只是用它的字面意思作为比喻。
如果你对JavaScript范围是如何工作的有一个基本的了解,那么对提升的更深入的理解将有助于你建立更强的基础知识。(愚人码头注:变量提升和函数声明提升作为JavaScript中的一个通用概念,在前端开发面试中经常被问到或者出现在前端开发试题中。可见,了解吊装是很重要的。)
为了更好的理解基础知识,我们来复习一下“吊装”到底是什么意思。另外,提醒大家,JavaScript是一种解释性语言,不同于编译语言,这意味着JS代码是逐行执行的。
考虑以下示例:
console.log(未声明);//print ' undefined ' var notyeddedclared='现在它被声明了';吊装();函数提升(){ console . log(notyet declared);//打印“undefined”var notyet declared=“声明方式不同”;console.log(未声明);//打印“不同声明”}分析上述示例代码后,提出了几个问题:
第6行,为什么这个函数在声明之前可以被访问?在第一行中,没有抛出错误,因为变量notyetdeclared此时不存在?第四行,未声明,已在全局范围内声明。为什么打印第九行时还是未定义?JavaScript非常符合逻辑,所有这些奇怪的问题都有明确的解释。
我们从头开始解释,当代码在JavaScript中执行时,运行时上下文就建立了。JavaScript中运行时上下文主要有两种类型——全局运行时上下文和函数运行时上下文(注意:要特别注意运行时上下文不同于我们通常所说的上下文,后者指的是作用域,而通常的上下文指的是这个的值)。因为JavaScript是基于单线程执行模型的,所以一次只能执行一段代码。
对于我们上面的代码,这个过程如图所示:
调用上述示例代码的堆栈:
程序从堆栈上的全局运行时上下文执行。调用延迟()函数时,新的函数运行时上下文被推送到堆栈上,全局运行时上下文被挂起。提升()执行完成后,从栈中弹出提升()运行时上下文,恢复全局运行时上下文。这个过程是不言自明的,但是它并没有真正解释我们在执行示例代码时看到的异常。当运行时上下文跟踪代码的执行时,词法环境跟踪标识符到特定变量的映射。词法环境基本上是JavaScript作用域机制的内部实现。一般来说,词法环境与JavaScript代码的特定结构相关联,例如函数或for循环代码块。每当创建一个函数时,对它创建的词法环境的引用将在名为[[环境]]的内部属性中传递。
所有这些术语涵盖了一个简单且非常符合逻辑的概念。让它分解。词法环境是一个有趣的名字,用来跟踪代码块中的变量和函数。除了跟踪局部变量、函数声明和参数,每个词法环境还跟踪其父词法环境。因此,上面的示例代码将在JavaScript引擎中这样解析。上述代码的词法环境如图所示:
注意:
如果理解有问题,请查看以下三篇文章:
深刻理解JavaScript中的范围和上下文;分析JavaScript核心概念的范围和闭包实例;为了在词法环境中解析标识符,JavaScript引擎将检查当前环境的引用。如果没有找到引用,请使用[[环境]]移动到外部环境。这将继续,直到找到标识符或引发“未定义”错误。
基本上,JavaScript代码分两个阶段执行。第一阶段注册当前词法环境中的所有变量和函数声明。完成后,第二阶段的JavaScript执行就开始了!
所以第一阶段要详细说明:分两步起作用。
扫描当前函数声明中的代码。函数表达式和箭头函数被跳过。对于每个发现的函数,都会创建一个新函数,并使用函数名将其绑定到环境。如果标识符的名称已经存在,其值将被覆盖。然后扫描当前环境的变量。找到var定义的变量和其他函数之外的变量,注册一个值被初始化为undefined的标识符。如果标识符存在,该值将保持不变。注意:let和const用于定义块变量,与var略有不同。在另一篇文章中了解更多信息。
既然您已经对词法环境的基本概念有了一定的了解,那么让我们回到示例代码并解释这些问题。
设置全局上下文时,会扫描环境,并将提升()函数附加到标识符上。然后,在下一步中,注册变量notyetdeclared,并将其值初始化为undefined。按照此步骤继续理解代码。
现在让我们解释一下示例代码中提出的三个问题:
第6行,为什么这个函数在声明之前可以被访问?
在第一阶段,提升()功能已在标识符中注册。当JS代码在第二阶段的全局执行上下文中开始执行时,会找到提升的词法环境,并在定义之前找到函数。
在第一行中,没有抛出错误,因为变量notyetdeclared此时不存在?
同样,notyetdeclared用标识符注册,并在阶段1中初始化为undefined,因此不会引发错误。
最后,
第四行,未声明,已在全局范围内声明。为什么打印第九行时还是未定义?
现在我们进入功能提升的环境。在第一阶段,notyetdeclared被注册并初始化为undefined,因为notyetdeclared的变量还没有在这个词法环境中注册。如果第12行不包含var关键字,那么情况就不同了。
我希望现在可以清楚地看到,在JavaScript中,提升只是我们用来解释背后原理的一个点。从技术上讲,函数和变量不会移动。
摘要
以上就是本文的全部内容。希望本文的内容能给你的学习或工作带来一些帮助。有问题可以留言交流。谢谢你的支持。