1.块级作用域
块级作用域
在ECMAScript6(以下简称ES6)之前,ECMAScript的作用域只有两种:全局作用域;和函数作用域。
但ES6的到来,为我们提供了‘块级作用域’。凡是带{}都是块级作用域,if(){} for(){} 对象{} 。注意function后是函数作用域。
块级作用域并不影响var声明的变量。var声明的变量的性质和原来一样,还是具有‘变量提升’的特性。
而块级作用域通过新增命令let和const来体现。在块级作用域下,var 和function跟在window下一样。如果没有出现let或者const就不用考虑块级作用域了。
即:
var定义的变量,没有块作用域的概念,可以跨块访问。
let定义的变量, 有块作用域的概念,不能跨块访问。
const用来定义常量,有块作用域的概念,不能跨块访问。
他们三个都有函数作用域的概念。都是用来定义变量的。
<script>
//在块级作用域下,var 和function跟在window下一样
{
console.log(sing)
sing();
function sing(){
console.log('aaa');
}
}
window.sing();
</script>
2.let关键字
在ES6之前,我们一般都用var来定义变量
使用 let 声明的变量与 var 声明的变量有许多的不同,比如:没有变量提升
暂时性死区
禁止重复声明
块级作用域
脱离全局作用域
等等….
(1)禁止重复声明let不允许在相同作用域内,重复声明同一个变量。
<script> console.log(a); //不会执行 var a; let a; //报错Uncaught SyntaxError: Identifier 'a' has already been declared </script>
(2)暂时性死区/临时性死区(Temporal Dead Zone)TDZ:(let在定义变量之前进行访问变量时所引起的现象):
在let声明变量前,使用该变量,它是会报错的,而不是像var那样会‘变量提升’。
其实说let没有‘变量提升’的特性,不太对。或者说它提升了,但是ES6规定对用用let定义的变量:在执行let声明语句前,不能使用该变量。这种语法叫做暂时性死区
ES6 规定暂时性死区和 let、const 语句不出现变量提升,主要是为了减少运行时出现错误,防止在变量声明之前就使用这个变量,从而导致意料之外的行为。
总之,暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。具体可以看下面的代码调试截图
参考截图1
参考截图2:
参考截图3:
<script>
var a1=3;
console.log('hhhhh')
if(true){ //块级作用域
console.log('jjjjj')
let a4;
console.log(a4);//undefined(不会报错!!!!!)
console.log(a3);//undefined
console.log(a2);//undefined
console.log(a1); //报错 Uncaught ReferenceError: Cannot access 'a1' before initialization(在初始化之前无法访问“a1”)
let a1=4;
var a2;
}
var a3=1
</script>
断点调试上述代码,当刚进入if中时,右侧出现Block(代表块级作用域),可以看见此时已经有a1、a4,但是上面代码仍旧报错了,而不是输出undefined。因为此时不可访问(暂时性死区)。
<script>
let a=10;
function sing(){
//函数作用域
console.log('jjjj');
console.log(a) //报错,因为必须要执行过初始化语句后才能访问。
let a=9;
function hh(){
var b;
console.log(a);
}
hh();
}
sing();
</script>
(3)脱离全局作用域,并未脱离函数作用域
在全局作用域中使用 let 、const声明的变量,将不会绑定在全局作用域 window 或是 global。也不会成为window的属性。
<script>
var a1=4;
let a2=9;
const a3=8;
console.log(a1);//4
console.log(a2);//9
console.log(a3);//8
console.log(window.a1);//4
console.log(window.a2);//undefined
console.log(window.a3);//unedfined
</script>
当代码执行第16行时,注意右边scope下有两个作用域:一个是scipt,一个是Global。
函数作用域中不像全局作用域中会出现script。
<script>
//function中无script。函数作用域中不像全局作用域中会出现script。
function sing(){
console.log('jjjjj');
let a=9;
if(true){
let a=9;
console.log(a);
}
}
sing();
</script>
当代码进入函数内部,执行第13行代码时:看右侧Scope区域。
当代码执行到第16行(第16行还未执行)时,看右侧Scope区域。
3.const
与let声明变量的区别是,const声明的是常量,声明时就必须进行初始化。其值一旦被设定后不可更改。其余与let相同。
在编写程序过程中,如果存储的数据不需要变化,则用const。因为js解析引擎不需要实时监控值的变化,效率比较高,所以使用const关键字比let关键字好。
<script>
console.log('hhhh')//不会执行。
const a;//声明时未初始化,报错
a=9;
</script>
4.三者对比
三者区别
(1)其是否会挂载到window上
var声明的全局变量会挂载在window上,而let和const声明的全局变量不会挂载到window上,它形成了一个块作用域
(2)是否存在变量提升
var声明变量存在变量提升,let和const不存在变量提升
(3)暂时性死区
let 和 const 定义的变量在执行声明语句之前调用,会抛出错误(形成了暂时性死区),而 var 不会。
(4)是否一旦声明,必须立即初始化赋值
var 和 let 声明的时候可以不初始化赋值,const声明时必须赋值。
(5)同一作用域下是否可以重复声明
同一作用域下let和const不允许重复声明,否则会报错,而var可以
(6)是否可以修改
var和let可以可以修改声明的变量,const不可以。
const 声明的是一个只读的常量。一旦声明,常量的值就不能改变( 如果声明的常量是一个对象的话,那么不能改变的是对象的引用地址)
(7)是否有块作用域
let和const有块作用域,var没有块作用域。
5.let解决for循环问题
<script>
var funcs = [];
for(var i=0; i<4; i++) {
funcs.push(function(){
console.log(i);
});
}
funcs.forEach(function(func) {
func(); // 输出4次数字4
});
</script>
为了解决上述问题
方法1:直接将for循环中var改成let即可
方法2:使用立即调用函数表达式(IIFE)/闭包
方法1:直接将for循环中var改成let即可
方法2:使用立即调用函数表达式(IIFE)/闭包
<script> var funcs = []; for(var i=0; i<4; i++) { funcs.push((function(value){ return function(){ console.log(value); } }(i))); } funcs.forEach(function(func) { func(); }); </script>