块级作用域
在ES6之前,作用域分为全局作用域和函数作用域,全局作用域指最顶部的js作用域,也可以理解为单个js文件内或是script内的最顶层代码。函数作用域也可以称之为局部作用域,其只在函数定义时生成。
在全局和函数作用域中,有许多不合理的地方,例如:
var txt = '外层变量';
function fn() {
console.log(txt);
if (false) {
var txt = '内层变量';
}
}
fn();//undefined
此打印为undefined,其代码一开始的想法是在fn函数中调用全局作用域下的txt变量,然后再对函数作用域下定义一个txt变量,那为什么后续打印的时候值为undefined呢?其原因在于var声明的变量,其会有变量提升的情况。其执行代码的顺序如下:
var txt = '外层变量';
function fn() {
var txt
console.log(txt); //txt为undefined
if (false) {
txt = '内层变量';
}
}
fn();//undefined
let、const
ES6时引入了let和const声明块级作用域变量的关键字,其相对于var关键字的区别在于
- 其定义的变量和常量只在其命令所在的代码块内有效,其let和const声明时会以{}为一个作用域。
{
let num = 10
var num2 = 20
}
console.log(num); //num is not defined
console.log(num2);//20
- 其不会变量提升,故会导致暂时性死区的情况,不同于var,var会变量提升,其引用变量不会报错,因为定义了num,而let则不会变量提升,在定义前引用变量会使得代码报错。
console.log(num); //undefined
console.log(num2);//ReferenceError: Cannot access 'num2' before initialization
var num = 10
let num2 = 20
- 其不能重复声明,规范的代码编写,同一代码下声明多个同名变量会导致后续的引用出错
var num = 10
var num = 20
console.log(num);//20
let num2 = 10
let num2 = 30//Identifier 'num2' has already been declared
其let块级作用域的特性非常适合在for循环中对i的遍历
var a = []
for (var i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
}
}
a[5]() //10
其代码的本意是遍历10次,每次以函数的形式将引用i值的函数存入a数组中,在对下标5的函数执行理应打印的i值为5,其真实打印的值为10,这是因为var的特性,在每个i的值自始自终就只有1个,也就是每个i值的引用指向的都是同一个内存块,其循环完后其值会变成10,故在a数组中每个元素执行打印的都是10。
var a = []
for (let i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
}
}
a[5]() //5
在用let对i进行定义时,其每个循环中,i都是一个独立的值,其每个循环都是一个作用域。