[JavaScript]何为变量提升?

【版权声明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权)
https://www.cnblogs.com/cnb-yuchen/p/18252500
出自【进步*于辰的博客

关于编译与解释,详述可查阅博文《[Java]知识点》中的【编译与解释】一栏。
参考笔记二,P43.3、P46.1、P9.3。

附言

你在阅读本篇文章时,会看到一些名词,如:函数作用域,也就是由 var 声明的变量的作用域的名称,它是我根据我的理解自定义的。后来我了解到,JS中似乎并没有这个概念,对应的概念好像是“词法作用域”。

我暂未系统地学习JS,故无法详细地为你说明这个概念。当然,尽管我的用词可能不对,但就目前,这有助于我的学习理解。

如果你想进一步地学习这方面的知识,推荐一篇博文《JavaScript执行机制:变量提升、作用域链、词法作用域、块级作用域、闭包和this》(转发)。

1、介绍

什么是“变量提升?

“变量提升”是指在解释时,解释器先扫描整个JS脚本,将所有声明(包括变量和函数)移动到作用域顶端的机制,其本质就是声明与定义不同步的错觉。

var 与 let 具有相同的变量提升机制,故经变量提升后(“解释”后)的脚本相同。

PS:大家可能不明其意,往下看。

先说说两种修饰符声明变量的作用域的定义:

  1. var:作用域的“上界”是变量声明处“往上”的第一个函数花括号({),故也称为“函数作用域”。var 允许重复声明和定义。
  2. let:作用域的“上界”是变量声明处“往上”的第一个花括号({).,故也称为“块级作用域”。let 不允许重复声明和定义(同一作用域)。

let 与 var 不同的是,let 声明的变量在定义之前,存在“暂时性死区”,

示例:

console.log(str)
let str = 1

输出结果:
在这里插入图片描述
因为示例代码经“变量提升”后:

let str
console.log(str)
str = 1

这就是“暂时性死区”,在定义前访问或赋值会报错。如:let str是声明,let str = 1是定义。

2、解答

1:如何理解“函数的变量提升优先于变量?”(PS:一些资料中可能会这样阐述)

事实上,之前由于我的JS功底不够扎实,也误解了这句话,以为是这样:

console.log(b)
var a = 1
var b = function() {
	return 2
}

经变量提升后:

var b
var a
console.log(b);// undefined
a = 1
b = function() {
	return 2
}

如果真的如我之前这般理解,无意义,并且也理解错误。

那么,“函数的变量提升优先于变量”这句话到底是什么意思?我从博文《JavaScript执行前的秘书——预编译》(转发)中取经得知,如下:

console.log(b)
var a = 1
function b() {
	return 2
}

经变量提升后:

function b() {
	return 2
}
var a
console.log(b);// [Function: b]
a = 1

所以,我之前是把var b = function() {}的形式误解为"函数声明”,实际上,这也是“变量声明”,只是变量值定义为函数而已。

最后,引用一段“取经”博文中的阐述:
在这里插入图片描述

2:如何解释“let 不允许重复声明和定义”?

我们先来看由 var 修饰的情况,示例:

var a = 1
var a = 2

经变量提升后:

var a
a = 1
a = 2

也就是:变量提升会将重复声明进行覆盖

再来看 let 的情况。如果两个同名的变量都由 let 修饰,报错,这是 let 的特性。大家疑惑的多是这种情况:

var a = 1
let a = 2

先解答:也会报错。为什么?这涉及到一个细节:

var 的变量提升的优先级高于 let。

也就是说,经变量提升后:

var a
let a
a = 1
a = 2

这种情况 let 同样不允许,故报错。

稍作修改:

let a = 1
var a = 2

这种情况与上述完全相同,故也不允许。

3、一种特殊情况

在上文中,我们说到,这种情况不允许:

var a = 1
let a = 2

那么,请问下面这种情况能正常执行吗?

var a = 1
{----------------------A
	let a = 2
}

要解决这个问题,就要研究 var 与 let 的作用域了,大家还记得我在上文中所述的它们的作用域的定义吗?结论:

第一个a的作用域是“全局”,而第二个a的作用域是 A 处的代码块。

因此,两个a的作用域不同,故不报错。

再给大家抛出一个问题:这样会报错吗?

let a = 1
{
	var a = 2
}

PS:相信大家看到这里,已经对 var 和 let 有了足够的掌握,这个问题就交由大家思考了。

最后

为大家补充两个结论:

  1. var 是ES5的语法,let 是ES6的语法。
  2. 定义变量时可以不用 var 或 let 修饰(即直接a = 1),那么 var 与 let 的作用是什么?var 与 let 定义 / 决定了变量的作用域。因此,定义变量时如果不用 var 或 let 修饰,就不存在“变量提升”,则在定义前访问或赋值将直接报错。

还有,const也会“变量提升”,如let一般,具有块级作用域。

本文完结。

posted @ 2024-06-18 02:28  进步·于辰  阅读(9)  评论(0编辑  收藏  举报