首先,var声明的变量只有函数级作用域和全局作用域,没有块级作用域,要想实现块级作用域,要用let。
1.2.1 作用域链
当使用一个变量时,js首先在当前作用域找这个变量,如果找不到,到上一级去找。我们通过一个例子说明。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script>
n0 = 0;
function Test1() {
var n1 = 1;
function Test2() {
var n2 = 2;
alert(n0); //一直找到全局作用域
alert(n1); //找到上一级Test1的作用域
alert(n2); //当前作用域
}
Test2();
}
Test1();
</script>
</head>
<body>
</body>
</html>
分别打印0,1,2,其实没啥不好理解的,一层层往上找呗。
1.2.2 变量提升
简单说,就是变量声明同一提前到当前作用域开始的地方,但是赋值操作不会提升。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script>
function Test1() {
var n1 = 1;
alert(n1);
alert(n2);
//var n2 = 2;
}
Test1();
</script>
</head>
<body>
</body>
</html>
这段代码执行到alert(n2)时会报错,如果放开注释,会打印undefined,原因就是变量提升。放开注释后类似于下面的代码
function Test1() {
var n1 = 1;
var n2;
alert(n1);
alert(n2);
n2 = 2;
}
个人理解变量提升是为了服务于变量作用域这个机制,但是我觉得这个机制让代码可读性下降,为了规避这个问题,变量声明在作用域头部我觉得更容易理解。
1.2.3 命名空间
因为全局变量会绑定到window作用域,假设一个页面引用了两个js文件,这两个js文件定义了同名的全局变量名,就会造成冲突,如下:
文件js1.js
var str = "js1";
文件js2.js
var str = "js2";
执行下面代码时:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="js2.js"></script>
<script src="js1.js"></script>
<script>
alert(str);
</script>
</head>
<body>
</body>
</html>
打印结果时最后引用js文件的str,本案例是js1;这个使得变量管理十分混乱,下面
我们用命名空间解决变量冲突问题
文件js1.js
var ns1 = {};
ns1.str = "js1";
文件js2.js
var ns2 = {};
ns2.str = "js2";
执行下面代码时:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="js2.js"></script>
<script src="js1.js"></script>
<script>
alert(ns1.str);
alert(ns2.str)
</script>
</head>
<body>
</body>
</html>
可以正常打印了,其实这个也很好理解,就是把每个js文件的的变量追加到一个不重名的对象上。至于js对象这个概念,我们会在[2 面向对象]在讨论。
1.2.4 块级作用域
所谓块级作用域,在c#里面就是说出了大括号,声明的变量会释放掉。但是在js里面,var只有函数和全局作用域,块级作用域要用let实现。废话不多说,直接上例子:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script>
function Test(n1) {
if (n1 > 0) {
var str = "More than 0";
alert(str);
} else {
str = "Less than 0" //这里没声明str,但是可以正常使用
alert(str);
}
}
Test(10);
Test(-1);
</script>
</head>
<body>
</body>
</html>
分别打印["More than 0"]和["Less than 0"],str出了if大括号依然可以使用,原因其实就是变量提升造成的,str的声明提升到了函数Test的头部。
那么我们就想实现大括号的块级作用域怎么办?let声明变量+[ use strict]就好了。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script>
'use strict'; //要在strict模式下
function Test(n1) {
if (n1 > 0) {
let str = "More than 0";
alert(str);
} else {
str = "Less than 0" //执行这一不,报错,变量未定义,加let就好了
alert(str);
}
}
Test(10);
Test(-1);
</script>
</head>
<body>
</body>
</html>