let
ES6新增了let
命令,用来声明变量。它的用法类似于var
,但是所声明的变量,只在let
命令所在的代码块内有效。
{
var a = 1;
let b = 2;
}
a; // 1
b; // ReferenceError: b is not defined
上面代码在代码块中,分别用let和var声明了两个变量,在代码块之外调用这两个变量,let声明报错,var返回了正常的值。说明,let声明的变量只在它所在的代码块有效。
let
不存在变量提升,它所声明的变量一定要在声明后使用,否则报错。
// var 的情况
console.log(foo); // 输出undefined
var foo = 2;
// let 的情况
console.log(bar); // 报错ReferenceError
let bar = 2;
上面代码中,变量foo用var命令声明,会发生变量提升,即脚本开始运行时,变量foo已经存在了,但是没有值,所以会输出undefined。变量bar用let命令声明,不会发生变量提升。这表示在声明它之前,变量bar是不存在的,这时如果用到它,就会抛出一个错误。
ES6 明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。
不允许重复声明,重复声明会报错。
{
var a = 1;
let b = 2;
let b = 3; // SyntaxError: Identifier 'b' has already been declared
}
a;
b;
let 为Js新增了块级作用域。
const
const
声明一个只读的常量。一旦声明,常量的值就不能改变。
const PI = 3.1415;
PI // 3.1415
PI = 3;
// TypeError: Assignment to constant variable.
const声明的变量不得改变值,这意味着,const一旦声明变量,就必须立即初始化,不能留到以后赋值。
const foo;
// SyntaxError: Missing initializer in const declaration
对于const来说,只声明不赋值,就会报错。
const
的作用域与let
命令相同:只在声明所在的块级作用域内有效。
if (true) {
console.log(MAX); // ReferenceError 无变量提升
const MAX = 5;
}
MAX // Uncaught ReferenceError: MAX is not defined 暂时性死区
在常量MAX声明之前就调用,结果报错。const命令声明的常量也是不提升,同样存在暂时性死区,只能在声明的位置后面使用。
const
声明的常量,也与let
一样不可重复声明。
let/const没有变量提升
在实际使用过程中,如果提前使用,会直接报错
console.log("foo", foo)
// Uncaught ReferenceError: Cannot access 'foo' before initializatio
let foo = "foo"
这些变量会被创建在包含他们的词法环境被实例化时,但是不可以访问它们,直到词法绑定被求值。
意思是,在词法环境创建的时候,let声明的变量就已经创建了,但是在声明之后才能获取到值。
作用域提升:在声明变量的作用域中,如果这个变量可以在声明之前被访问,那么我们可以称之为作用域提升。
let、const没有进行作用域提升,但是在执行上下文创建阶段被创建出来
var
var具有变量提升
变量提升:JS函数有一个特点,它会先扫描整个函数体语句,把所有声明的变量提升到函数顶部,但是JS引擎会自动提升变量的声明,但不会提升变量的赋值。
console.log(foo); // 输出undefined
var foo = 2;
上述代码表示var定义变量时的变量提升。
三者和window的关系
var声明变量会在window上添加属性,但是let/const不会添加任何属性。
let a1 = "foo"
var a2 = "boo"
const a3 = "baz"
console.log(window);
window打印:
可以看到window打印,可以看到 var定义的变量a2在window内
但是没有其他两个变量
因为其他两个存储在与全局词法环境对象关联的声明性环境记录中。
每一个执行上下文 回关联到一个变量环境(VariableEnvironment)中,在执行代码中,变量和函数的声明会作为环境变量(Environment Record)添加到变量环境中。
查看的两种方式
查看的两种方式不同,但是表示的内容是一致的
第一种:Scope(作用域)-> Script(脚本)
第二种:new Function()
块级作用域
块作用域 这里的块指的是大括号{} 也就是说 在大括号中定义的let变量那么在大括号的外部无法访问
那么可以产生块作用域的语句都有哪些 if for while do…while switch 立即执行函数等
这里使用的是 ES5的代码块 {}
let a4 = "qux"
{
console.log("a4", a4); // qux
let a1 = "foo"
var a2 = "boo"
const a3 = "baz"
}
console.log("a1", a1); // 报错
console.log("a2", a2); // boo
console.log("a3", a3); // 报错
在ES6新增了块级作用域,并且通过let、const、function、class声明的标识符等都具备块级作用域限制
{
let foo = "foo"
function bar() {
console.log("bar");
}
class Person { }
}
// console.log("foo", foo);
// Uncaught ReferenceError: foo is not defined
bar()
// bar
var p = new Person()
// Uncaught ReferenceError: Person is not defined
但是我们会发现函数拥有块级作用域,但是外面依然是可以访问的:这是因为引擎会对函数的声明进行特殊的处理,允许像var那样进行提升
暂时性死区(TDZ)
它表达的意思是在一个代码中,使用let、const声明的变量,在声明之前,变量都是不可以访问的,我们将这种现象称之为 temporal dead zone(暂时性死区,TDZ) ;
let foo = "foo"
if (true) {
console.log(foo);
// Uncaught ReferenceError: Cannot access 'foo' before initialization
let foo = "abc"
}
三者区别及共同点
let、var :定义变量
const : 只能定义常量
let、const :产生块级作用域,无变量提升,不允许重复声明
var : 无块级作用域,有变量提升,允许重复声明
let、const :只在声明所在的块级作用域内有效。
总结
var表现出来的特征性:如作用域提升、window全局对象、没有块级作用域等都是遗留问题
在实际开发中推荐 let、const