let
和 const
命令
let
命令
- let 声明的变量只在块级作用域内有效
- 不存在变量提升
- 不能重复声明
- 存在暂时性死区
变量提升
// var 会有变量提升
console.log(a);//undefined,说明变量a提升了为:var a;
var a = 10;
console.log(a);//10
// console.log(a1);//报错
let a1 = 10;
console.log(a1);//10
暂时性死区
只要块级作用域内存在let
命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。
如果区块中存在
let
和const
命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。
// 暂时性死区
var a = 100;
function fn() {
// console.log(a);//报错,变量a的“死区”。
let a = 10;
console.log(a);//10
}
fn();
console.log(a);//100
不能重复声明
let
不允许在相同作用域内,重复声明同一个变量。
// let 不能重复声明同一个变量,var可以
{
var a = 10;
var a = 20;
console.log(a);//不报错,20
let b = 10;
let b = 20;
console.log(b);//报错
}
function fn() {
let a = 10;
var a = 1;
console.log(a);
}
fn();//错误
因此,不能在函数内部重新声明参数。
function func(arg) {
let arg;// 报错
}
func(); // 报错
function func(arg) {
{
let arg = 'yami';
console.log(arg);
}
}
func();// yami
只在块级作用域内有效
// let声明的变量是块级作用域,不存在变量提升
{
let b = 10;
console.log(b);//10
}
console.log(b);//报错,不在块级作用域内
为什么需要块级作用域?
-
第一种场景,内层变量可能会覆盖外层变量。
var b = "我是外部b"; function fn1() { //这个地方undefined存在变量提升,导致内层b变量覆盖了外层的b变量 console.log(b);//undefined { var b = "我是内部b"; console.log(b); } } fn1();//undefined 我是内部b(内层变量覆盖外层变量)
解決:
let a = "我是外部a"; function fn() { console.log(a); { let a = "我是内部a"; console.log(a); } } fn();//我是外部a 我是内部a (沒有覆蓋)
-
第二种场景,用来计数的循环变量泄露为全局变量。
var array = [1, 2, 3]; for (var j = 0; j < array.length; j++) { console.log(array[j]); } console.log('j=' + j);//3
上面代码中,变量
j
只用来控制循环,但是循环结束后,它并没有消失,泄露成了全局变量。然而使用
let
则不会有这种现象,会报错:var array = [1, 2, 3]; for (let i = 0; i < array.length; i++) { console.log(array[i]); } console.log(i);//报错
块级作用域与函数声明
function f() {
console.log('I am outside!');
}
(function () {
if (false) {
// 重复声明一次函数f
function f() { console.log('I am inside!'); }
}
f();// f is not a function
}());
理论上会得到“I am outside!”。因为块级作用域内声明的函数类似于let
,对作用域之外没有影响。但是,如果你真的在 ES6 浏览器中运行一下上面的代码,是会报错的,这是为什么呢?
浏览器的 ES6 环境中,块级作用域内声明的函数,行为类似于var
声明的变量,导致变量提升。上面的例子实际运行的代码如下。
function f() {
console.log('I am outside!');
}
(function () {
var f = undefined;
if (false) {
// 重复声明一次函数f
function f() { console.log('I am inside!'); }
}
f();// f is not a function
}());
- 应该避免在块级作用域内声明函数。如果确实需要,也应该写成函数表达式,而不是函数声明语句。
- ES6 的块级作用域必须有大括号{},如果没有大括号,JavaScript 引擎就认为不存在块级作用域。
- 函数声明也是如此,严格模式下,函数只能声明在当前作用域的顶层。
//建议写成函数表达式:let f=function(){ }
{
let a = 1;
let f = function () {
console.log(a);
}
f();
}
//块级作用域必须有大括号{}
if (true) {
let b=2;
}
'use strict';
if (true) {
function f() {
console.log('严格模式下也要加{}')
}
f();
}
const
命令
基本用法
实际开发的项目中,大部分情况下建议使用const
声明。
问自己,这个变量是否需要修改。如果需要修改 直接改成let
。
let
和const
的使用建议:在默认情况下用
const
,而只有你在知道变量值需要被修改的情况下使用let
。
const
声明一个只读的常量。一旦声明,常量的值就不能改变。const
声明的变量不得改变值,这意味着,const
一旦声明变量,就必须立即初始化,不能留到以后赋值。const
的作用域与let
命令相同:只在声明所在的块级作用域内有效。const
命令声明的常量也是不提升,同样存在暂时性死区,只能在声明的位置后面使用const
声明的常量,也与let
一样不可重复声明。
本质
常量对象可以更改
可以更改常量对象的属性:( 但是无法重新为常量对象赋值)
// 可以创建 const 对象:
const car = {type:"porsche", model:"911", color:"Black"};
// 可以更改属性:
car.color = "White";
// 可以添加属性:
car.owner = "Bill";
常量数组可以更改
您可以更改常量数组的元素:
// 可以创建常量数组:
const cars = ["Audi", "BMW", "porsche"];
// 可以更改元素:
cars[0] = "Honda";
// 可以添加元素:
cars.push("Volvo");
但是您无法重新为常量数组赋值:
const cars = ["Audi", "BMW", "porsche"];
cars = ["Honda", "Toyota", "Volvo"]; // ERROR
如果真的想将对象冻结,应该使用Object.freeze
方法。
const foo = Object.freeze({});
// 常规模式时,下面一行不起作用;
// 严格模式时,该行会报错
foo.prop = 123;