变量提升

解析器会先解析代码,然后把声明的变量的声明提升到最前,这就叫做变量提升。

定义其实包含了-> 声明 + 赋值

1
2
3
4
var web = 'https://blog.farmer233.top';
// 上面这条代码其实包含了两个步骤,如下:
var web;
web = 'https://blog.farmer233.top';

使用 var 声明代码会被提升到前面

1
2
3
4
5
6
7
8
9
console.log(a); //undefined
var a = 1;
console.log(a); //1

//以上代码解析器执行过程如下
var a;
console.log(a); //1
a = 1;
console.log(a); //1

下面是 if(false) 中定义的var也会发生变量提升,注释掉if 结果会不同

1
2
3
4
5
6
7
8
9
var web = "https://blog.farmer233.top";
function hd() {
if (false) {
var web = "https://blog.farmer233.top";
}
console.log(web);
}
hd();

使用 var 定义的代码,声明会被提升到前面,赋值还在原位置

1
2
3
4
5
6
7
console.log(blog);
var blog = 'https://blog.farmer233.top';

//以上代码解析器执行过程如下
var blog;
console.log(blog); // https://blog.farmer233.top
blog = 'https://blog.farmer233.top';

小结

  1. 变量提升就是代码在预解析的时候将变量的声明提到程序的开头
  2. 通俗的讲就是将代码块内的变量都在开头先声明。

变量提升会导致很多莫名其妙的bug,那么该如何避免变量提升

  • 为了解决这个问题,JavaScript后来推出了let和const
  • 使用严格模式

TDZ

TDZ 又称暂时性死区,指变量在作用域内已经存在,但必须在let/const声明后才可以使用。
TDZ可以让程序保持先声明后使用的习惯,让程序更稳定。

  • 变量要先声明后使用
  • 所以我们应该使用let/const来定义变量, 少用var

使用 let/const 声明的变量在声明前存在临时性死区(TDZ)使用会发生错误

1
2
console.log(x); // Cannot access 'x' before initialization
let x = 1;

在run函数作用域中产生TDZ,不允许变量在未声明前使用。

1
2
3
4
5
6
blog = "https://blog.farmer233.top";
function run() {
console.log(blog);
let blog = "farmer233";
}
run();

下面代码b没有声明赋值不允许直接使用

1
2
3
4
5
6
7
blog = "https://blog.farmer233.top";
function run() {
console.log(blog);
let blog = "farmer233";
}
run();

总结

  1. 多使用JavaScript的严格模式
  2. 由于JavaScript的历史遗留原因,很多老问题都被新版本修复了,因此我们应多使用新版本的工具。

栗子

可能我上面的表达不太清楚,这几天在群里吹水解答沙雕群友的时候写了点小栗子也顺便贴上来吧。(随便写写.)
欢迎随便看看。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 变量提升是因为变量的定义会被浏览器先预览一次
var web = 'https://blog.farmer233.top';
console.log(web);
var class = 'https://blog.farmer233.top';
// 注意!class是JavaScript的语关键字
// 所以这里class作为变量名是不被允许的(会报错)
// 我们都知道代码是从上到下运行的,那么是不是会把变量web打印后再报错? 答案是程序一运行就报错了
// 这是因此解析器会先预览一次变量的定义,在运行程序,上述代码可以写成下面这样
var web;
var class;
web = 'https://blog.farmer233.top';
console.log(web);
class = 'https://blog.farmer233.top';
// 这里变量的定义给提到上面去了,所以这就叫变量提升
1
2
3
4
5
// 和上面同理
console.log(web);
var web = 'https://blog.farmer233.top';
// 运行结果: undefined
// 程序并没有报错,这也是因为变量的声明提升到了前面,打印了一次后再给变量赋值。
1
2
3
4
5
6
7
8
// 上面讲的都是顺序程序结构,那如果变量是在代码块内定义的会怎么样? 其实一样会提升
if(false){
var web = 'https://blog.farmer233.top';
}
console.log(web);
// 运行结果: undefined
// 很明显if内的代码是不会给执行的,但运行结果仍然没有报错。

1
2
3
4
5
// 再探究一下
var web = 'https://blog.farmer233.top';
console.log(web); // https://blog.farmer233.top
console.log(window.web); // https://blog.farmer233.top
// 这里可以看到 web变量还可以作为全局对象window的属性。