块级作用域绑定
var声明及变量提升机制
在ES6之前,在函数作用域中或者全局作用域中通过var
关键字来声明变量,无论是在代码的哪个位置,这条声明语句都会提到最顶部来执行,这就是变量声明提升。
注意:只是声明提升,初始化并没有提升。
function getStudent(name){
if(name){
var age=25;
}else{
console.log("name不存在");
}
console.log(age); //undefined
}
如果按照预想的代码的执行顺序,当name
有值时才会创建变量age
,可是执行代码发现,即使不传入name
,判断语句外的输出语句并没有报错,而是输出undefined
。
这就是变量声明提升。
块级声明
ES6前是没有块级作用域的,比如{}
外可以访问内部的变量。
let声明
- 声明变量
- 作用域限制在当前代码块
- 声明不会提升
- 禁止重声明(同一作用域不行,可以覆盖外部同名变量)
function getStudent(name){
if(name){
let age=25;
console.log(age); //25
}else{
console.log("name不存在");
}
console.log(age); //age is not defined
}
和上文一样的代码,只是将age
的命名关键字从var
改成了let
,在执行getStudent()
和getStudent("axuebin")
时都会报错。
原因:
- 在if语句内部执行之后,
age
变量将立即被销毁 - 如果
name
为空,则永远都不会创建age
变量
const声明
- 声明常量
- 必须初始化
- 不可更改
- 作用域限制在当前代码块
- 声明不会提升
- 禁止重声明(同一作用域不行,可以覆盖外部同名变量)
如果用const
来声明对象,则对象中的值可以修改。
临时死区(Temporal Dead Zone)
JavaScript引擎在扫面代码发现声明变量时,遇到var
则提升到作用域顶部,遇到let
和const
则放到TDZ中。当执行了变量声明语句后,TDZ中的变量才能正常访问。
循环中的块作用域绑定
我们经常使用for循环:
for(var i=0;i<10;i++){
console.log(i); //0,1,2,3,4,5,6,7,8,9
}
console.log(i) //10
发现了什么?
在for循环执行后,我们仍然可以访问到变量i
。
把var
换成let
就解决了
for(let i=0;i<10;i++){
console.log(i); //0,1,2,3,4,5,6,7,8,9
}
console.log(i) //i is not defined
讲闭包时setTimeout循环各一秒输出i的jin例子
for(var i=0;i<10;i++){
setTimeout(function(){
console.log(i); //10,10,10.....
},1000)
}
很显然,上面的代码输出了10次的10,setTimeout
在执行了循环之后才执行,此时i
已经是10了~
之前,我们这样做 ~
for(var i=0;i<10;i++){
setTimeout((function(i){
console.log(i); //0,1,2,3,4,5,6,7,8,9
})(i),1000)
}
现在,我们这样做 ~ 来看看把var
改成let
会怎样~
for(let i=0;i<10;i++){
setTimeout(function(){
console.log(i); //0,1,2,3,4,5,6,7,8,9
},1000)
}
全局块作用域绑定
- 全局对象是最顶层的对象,在浏览器环境指的是window对象,在 Node.js 指的是global对象。 ES5 之中,全局对象的属性与全局变量是等价的。
window.a = 1;
a // 1
a = 2;
window.a // 2
- 未声明的全局变量,自动成为全局对象window的属性,这被认为是 JavaScript 语言最大的设计败笔之一。
- ES6 为了改变这一点,一方面规定,为了保持兼容性,var命令和function命令声明的全局变量,依旧是全局对象的属性;
- 另一方面规定,let命令、const命令、class命令声明的全局变量,不属于全局对象的属性。也就是说,从 ES6 开始,全局变量将逐步与全局对象的属性脱钩。
var a = 1;
// 如果在 Node 的 REPL 环境,可以写成 global.a
// 或者采用通用方法,写成 this.a
window.a // 1
let b = 1;
window.b // undefined
- 上面代码中,全局变量a由var命令声明,所以它是全局对象的属性;全局变量b由let命令声明,所以它不是全局对象的属性,返回undefined