作用域
在es5中,只有全局作用域,函数作用域。在es6中出现了块作用域。
函数作用域指变量在一个函数开始到结束都可以被访问到。
块作用域
以大括号{}为标识确定块作用域。
在es5中使用匿名立即执行函数表达式,在es6中直接引入了块作用域这个概念,代替了匿名立即执行函数表达式。
let 和const 声明的变量有块作用域,而var声明的变量没有块作用域。
块作用域使得内部块的变量无法被外部块访问,开发人员就可以在内外部块中声明相同名字的变量。避免了内外部块的变量互相影响。
const
const通常用于定义常量。它需要在声明时就进行赋值,否则会报Missing initializer in const declaration错误。const定义的值不能被修改,它保存的是一个地址,如果是普通变量,就是普通变量的地址,如果保存的是引用类型变量,保存的就是指向对象的指针的地址。
这里涉及到堆空间和栈空间。普通变量在栈空间开辟空间,然后存储值。引用变量在堆空间开辟空间,然后在栈空间中保存堆空间的地址,引用变量实际是存储在堆空间的。所以使用const设置一个常量为引用类型时,保存的是堆空间中的地址,地址不变,但地址中存储的内容可以改变,这时就相当于声明了一个指针。
如果要真正保持引用对象保持不变,可以使用object.freze()方法。冰冻对象跟复制对象一样,都存在深浅两种。
let
经常能够看到下面的例子:
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 10
这是一个陷阱。
由于var没有块作用域,所以每次循环的时候实际操作的都是同一个i,最后访问的也是同一个i,它并不是我们想要的效果。
正确的方法是将var改为let,这样每次循环都会新生成一个i,游览器会记住上一个i值,这能达到想要的效果。
重复声明?
在同一个作用域内,不允许重复声明一个变量。这会报错。
变量提升?
- 什么是变量提升?
变量提升让我们能够在变量声明前使用变量。游览器在编译时,会先把所有的声明提升到前面。变量提升之后,如果变量是被使用后才声明赋值的,在访问这个变量时也不会报错,只不过他是undefined。
表面上看这个规则减少了错误,但是同时也造成了许多不必要的麻烦,正确的方法应该是变量先声明后使用,这样开发者更加心中有数。 - let,const,var都有变量提升吗?
只有var存在变量提升,let,const都没有变量提升,这也是es6进步之处。
能否在块中写函数?
在es6中规定了可以将函数写到块中,等同于使用var声明变量,存在变量提升,块作用域的函数声明会提升到全局作用域或者函数作用域头部。如果在块中有个函数f(){},在提升后为f=undefined;
TDZ
暂时性死区。
由于let 和const不存在变量提升,即使在代码后面对某一个变量进行了声明赋值,但是在声明赋值代码行之前,这个变量都是不可使用的,使用就会报错reference error,这就是暂时性死区。
规范的方法还是先声明后使用。
全局变量和顶级对象属性
- 顶级对象
在游览器中顶级对象是window,在node中顶级对象是global。 - 全局变量是顶级对象的属性(以下是在游览器环境)?
在es5中,全局变量就是顶级对象的属性,所有全局变量都可以通过window. 访问。这就导致无法在代码编译时发现全部的错误,因为有些变量可能是通过window. 动态声明的。
在es6中,使用let,声明的变量就不再是顶级对象的属性,无法使用window. 访问到。es6开始逐渐剥离全局对象和顶级对象。但是使用var声明的变量依然是顶级对象的属性。