块级作用域
1.在ES5版本只存在全局作用域和函数作用域
var v = 100; //在全局作用域中
function fn() {
//在函数作用域中-只能在当前函数作用域被访问
var w = 10;
console.log(v) //100
console.log(w); //10
}
console.log(v) //100
fn()
console.log(w) //报错 w is not defined
结果为 100
100
10
w is not defined
2.在ES5版本中没有块级作用域 只有全局作用域和局部作用域
if (true) {
var v = 100 //全局变量
}
console.log(v); //100
这是一个块级作用域的代码块,因为ES5只有全局和局部作用域,所以v为全部变量
3.在ES6中提供了块级作用域 使用let关键字定义块级作用域的变量
if (true) {
//块级作用域--定义的变量只能在当前块级作用域中被访问
let v = 100;
}
// 在全局作用域中访问变量v
console.log(v) //v is not defined
因为ES6中块级作用域只能在当前块级作用域中被访问,全局不可访问
let与var的区别
let与var关键字的区别–都是定义变量
let -提供了块级作用域,也可以在全局作用域和函数作用域定义变量
var -提供了全局作用域和函数作用域
var m = 100; //全局变量 -在全局作用域中定义
let n = 1000; //全局变量 - 在全局作用域中定义
console.log(m) //100
console.log(n); //1000
let不允许声明提前
我们使用var 关键字定义变量–存在声明提前的现象
console.log(m); //undefined
var m = 10;
上述代码等价于:
var m; //只声明变量,并不初始化 -默认值为undefined
console.log(m) //undefined
m = 10 //初始化变量--赋值
可以看出 var关键字
存在声明提前的现象
我们使用let 关键字定义变量时候–不存在声明提前
console.log(v) //v is not defined
let v = 10
当你用let关键字声明提前的话它会直接给你报错。
let不允许重复声明
1.使用var关键字允许重复声明
var v = 100
console.log(v); //100
var v = 1000; //重复声明
console.log(1000) //1000
2.使用let关键字不允许重复声明
let v = 100;
console.log(v) //100
let v = 1000 //重复声明--SyntaxError: Identifier 'v' has already been declared
console.log(1000)
但是let关键字可以重新赋值
let v = 100;
console.log(v) //100
v = 1000 //重新赋值
console.log(1000)
var、let与函数参数的关系
1.使用var关键字声明局部变量与形参的关系
因为var关键字允许声明提前,所以输出100
function fn(a) {
console.log(a); //100
var a = 1000
}
fn(100)
2.在ES6中函数的参数相当于使用let关键字定义的局部变量
因为ES6中let关键字不能重复声明,所以报错
function fn(a) {
let a = 1000 //重复声明
console.log(a); //Identifier 'a' has already been declared
}
fn(100)
那我们在上段代码中吧中间两行代码换下位置会是什么结果?
function fn(a) {
console.log(a); //Identifier 'a' has already been declared
let a = 1000 //重复声明
}
fn(100)
他还是会报错,因为不管位置如何,只要重复声明,let都会报错
为什么需要块级作用域
ECMAScript5只存在全局作用域和函数作用域,没有块级作用域。这种情况出现一些问题:
-
局部变量可能覆盖全局变量 如:
var tmp = 100; (function() { console.log(tmp)//undefined if (false) { var tmp = 10; } })()
上述代码示例中,在自调函数中访问全局变量tmp。但由于在函数作用域定义了局部变量tmp(虽然初始化的语句不会被执行,但因为声明提高局部变量仍旧存在),导致输出结果为undefined
-
在循环体中用于计数的变量泄露为全局变量
当我们使用var关键字来声明变量的时候
for (var i = 0; i < 10; i++) { console.log(i) // 0 1 2 3 4 5 6 7 8 9 } //循环体执行完毕之后,变量i的值为10--跳出循环体的条件 console.log(i) //10
我们可以看到,当我们在执行完for循环的时候,i并没有释放,依然当做全局变量
当我们使用let关键词来声明的时候
for (let i = 0; i < 10; i++) { console.log(i) } //全局作用域 console.log(i) //ReferenceError: i is not defined
在全局作用域输出的时候他会报错
循环语句与数组
经典面试题,下面代码运行的结果是什么?
var arr = [];
for (var i = 0; i < 10; i++) {
arr[i] = function() {
return i;
}
}
console.log(arr[5]())
我猜你的答案肯定是5。
但是正确答案为10,他为什么是10呢,下面我用一张图来说明
那我们再换一种写法,吧var换成let看看结果会是怎么样的。
var arr = [];
for (let i = 0; i < 10; i++) {
arr[i] = function() {
return i;
}
}
console.log(arr[5]())//5
当我们换成let的话,他的结果就变了,数组下标是几就输出几。
let只能用于块级作用域中。
函数的声明
1.在声明函数中用var来声明函数
if (true) {
//块级作用域
function fn() {
console.log('this is function')
}
//等价于
var fn = function() {
console.log('this is function')
}
}
//全局作用域
fn() //this is function
可以看出,即使ES6允许定义函数,上述代码用var声明函数依旧是全局
2.再声明函数中用let定义函数
if (true) {
//块级作用域
let fn = function() {
console.log('this is function.')
}
}
//全局作用域
fn() //fn is not defined
用let定义函数,才有块级作用域.