ES6系列之let和const
let声明符
ES6 新增的
let
命令,用来声明变量。它的用法类似于var
,但是所声明的变量,只在let
命令所在的代码块内有效。
注意
let 声明变量
不会进行变量提升 无法在声明之前使用let声明的变量
let声明的变量不容许重复声明 预解析阶段会对let声明的变量锁定 所有其他在同一个域下的声明都会报错
let会开启块级作用域
基础特性
if(true){
let a = 'let';
var b = 'var';
}
console.log(b); //var
console.log(a); //Uncaught ReferenceError: a is not defined
---------------------------------------------------------
//使用let,声明的变量仅在块级作用域内({ })有效,最后输出的是 6。
for(let i = 0; i < 10; i++){
}
console.log(i); //Uncaught ReferenceError: i is not defined
上面代码在代码块之中,分别用let
和var
声明了两个变量。然后在代码块之外调用这两个变量,结果let
声明的变量报错,var
声明的变量返回了正确的值。这表明,let
声明的变量只在它所在的代码块有效。
// 在循环体表达式中let声明迭代变量i是let声明的,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量
var a = [];
for (let i = 0; i < 10; i++) {
a.push(function () {
console.log(i);
});
}
a[5](); // 5
// 在循环体表达式中let声明 和循环体中let 声明同名变量是不冲突的 设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。相当于每次循环{}内部都会单独开一个局部作用域
for (let i = 0; i < 3; i++) {
let i = 'abc';
console.log(i);
}
不进行变量提升
var
命令会发生“变量提升”现象,即变量可以在声明之前使用,值为undefined
。
let
命令改变了语法行为,它所声明的变量一定要在声明后使用,否则报错。 这一点需要特殊注意
// var 的情况
console.log(num); // 输出undefined
var num = 2;
-------------------------
// let 的情况
console.log(age); // 报错ReferenceError
let age = 16;
致死域(暂时性死区)
只要块级作用域内存在
let
命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。
//正常调用
let num = 10;
if (true) {
console.log(num); //10
}
------------------
//if { }内部let为独立局部作用域 访问为 20
let num = 10;
if (true) {
let num = 20;
console.log(num); //20
}
------------------
//发生致死 因为fn局部作用域中有let声明num 所以在作用域内 不能先调用再使用let声明。
let num = 10;
function fn() {
console.log(num); //Uncaught ReferenceError: Cannot access 'num' before initialization
let num = 20;
}
fn();
------------------
//typeof操作影响
typeof num;
let num; //Uncaught ReferenceError: Cannot access 'num' before initialization
------------------
let num = 10;
if (true) {
var num = 20;
console.log(num); //报错 Identifier 'num' has already been declared
}
var num;
let num = 10;
if (true) {
num = 20;
console.log(num); ////报错 Identifier 'num' has already been declared
}
-------------------
//如果在某一个作用域中有let声明某一个变量 那在这个作用域中就不能再let声明之前使用这个变量
let num = 10; //全局作用域
function fn() {//
console.log(num); //Uncaught ReferenceError: Cannot access 'num' before initialization
let num = 20; //局部作用域
}
fn();
-------------------
function fn() {
console.log(num);
num = 20; //局部作用域
}
fn();
let num = 10; //全局作用域 报错 num is not defined
-------------------
//作用域链: 外部作用域无法访问内部作用域的私有变量 作用域访问只能由内之外 由下至上
function fn() {
var x = 20;
}
fn();
console.log(x);// 报错 x is not defined
/*
1. for if 没有独立的局部作用域
2. i 是全局变量(当前案例环境下) i指代内存地址 [内存地址:[值]]
3. 每一个地址里面的值在每个时刻都只可能是一个唯一的状态(值)
4. 内部push的fn fn内部作用域引用了外部作用域的变量 i
5. 当arr[1]() 执行的时候 解析函数内部变量指向 发现i指向全局 i 对应地址当前存储的值是 5
6. 循环中每次循环的时候 立刻调用一个IIFE匿名函数 并将i的值
7. 左查询(赋值) 右查询(调用) 作为实参传递的时候是右查询
8. IIFE (结构体表达式)(调用前面的结构体 传递实参)
fn()
9. 块级别作用域
*/
var arr = [];
var i = 0;
for (; i < 5; i++) {
(function (i) {
var i = 0; //i=1; i=2
arr.push(function () {
console.log(i);
})
})(i);
}
console.log(i);
// i
arr[1](); //5
var w = 10;
function fn(w) {
console.log(w); //30
// var w;
//函数的形参会作为函数的局部变量隐式声明
w = 20;
// console.log(w); //20
}
fn(w); //变量作为实参传递的时候 是将当前时刻变量的值传入
fn(调用w);//右查询 w 左查询
x = 300; //x为左查询 (赋值)
y = x; //x为右查询 y为左查询
当程序的控制流程在新的作用域(module function 或 block 作用域)进行实例化时,在此作用域中用let/const声明的变量会先在作用域中被创建出来,但因此时还未进行词法绑定,所以是不能被访问的,如果访问就会抛出错误。因此,在这运行流程进入作用域创建变量,到变量可以被访问之间的这一段时间,就称之为暂时死区。
es6规定 let
/const
会使区域形成封闭的作用域 若在声明之前使用变量就会发生错误, 在代码块
内使用let命令声明变量之前 该变量都无法使用,称为“暂时性死区”(temporal dead zone,简称 TDZ)。总之,暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。使用let 确保先 声明 再 使用 变量 不要混用let和var在同一个作用域链上 避免死区
无法重复声明
let
不允许在相同作用域内,重复声明同一个变量。
//发生致死 因为var会变量提升。
let num = 10;
if (true) {
var num = 20; //Uncaught SyntaxError: Identifier 'num' has already been declared
console.log(num);
}
块作用域 (block scope)
在ES5中,只全局作用域和函数作用域。这会导致函数作用域覆盖了全局作用域;亦或者循环中的变量泄露为全局变量。
EcmaScript 6引入了块级作用域(block scope),块级作用域只能在块中被访问,以下两种情况可以创建块级作用域的变量。
- 在函数中
- 在被
{
和}
包裹的块中
{
var x = 10;
}
console.log(x); //10
------------------
{
let x = 10;
}
console.log(x);//x is not defined
------------------
//函数有两个代码块,都声明了变量num,运行后输出 3。这表示外层代码块不受内层代码块的影响。如果两次都使用var定义变量num,最后输出的值才是 6。
function fn() {
let num = 3;
if (true) {
let num = 6;
}
console.log(num); // 3
}
fn();
-----------------------
//作用域链: 外部作用域无法访问内部作用域的私有变量 作用域访问只能由内之外 由下至上
function fn() {
var x = 20;
}
fn();
console.log(x);// 报错 x is not defined
块作用域下的函数声明
ES5 规定,函数只能在顶层作用域和函数作用域之中声明,不能在块级作用域声明。
ES6 的块级作用域必须有大括号,如果没有大括号,JavaScript 引擎就认为不存在块级作用域。
function a() {
console.log('我是全局作用域内声明的函数a');
}
(function () {
if (false) {
function a() {
console.log('我是块级作用域内声明的函数a');
}
}
a(); //报错 a is not a function
})();
----------------------
function a() {
console.log('我是全局作用域内声明的函数a');
}
(function () {
var a;
if (false) {
a = function () {
console.log('我是块级作用域内声明的函数a');
}
}
a(); //报错 a is not a function
})();
------------------
//用{}不会变量冲突
{
let oSlider = document.querySelector('.slider');
let index = 0;
oSlider.onmouseover = function () {
this.innerHTML = index++;
}
}
{
let oSlider = document.querySelector('.other-slider');
let index = 0;
oSlider.onmouseover = function () {
this.innerHTML = index++;
}
}
为了减轻因此产生的不兼容问题,ES6 在附录 B里面规定,浏览器的实现可以不遵守上面的规定,有自己的行为方式。
- 允许在块级作用域内声明函数。
- 函数声明类似于
var
,即会提升到全局作用域或函数作用域的头部。 - 同时,函数声明还会提升到所在的块级作用域的头部。
const声明符
const
声明一个只读的常量。一旦声明,常量的值就不能改变。 const与let在 块作用域 重复声明 致死域的问题上是一致的const
的作用域与let
命令相同:只在声明所在的块级作用域内有效。
const DATA = '我是常量 不能改变哦';
DATA // '我是常量 不能改变哦'
DATA = '改一个试试看';
// TypeError: Assignment to constant variable.
const
声明的变量不得改变值,这意味着,const
一旦声明变量,就必须立即初始化,不能留到以后赋值。
const 声明常量
常量:
是一个固定值 不会改变 不能改变 是客观事实提供的固定值 PI E lg(10) sin(0)
编程中的常量: 保证变量值的安全性
const 只读变量(客观值 固定值)
const 声明的变量值 不容许修改
拥有let所有的特性
变量不会提升
不容许先调用后声明
不容许重复声明
能够开启块作用域
const声明的变量声明时必须赋值
const的值如果是引用类型 值的属性可以修改
常量变量名称 建议 全大写 _链接多个单词
声明时必须赋值
const X;
//Uncaught SyntaxError: Missing initializer in const declaration
值为对象
const KEY_MAP = {
a:1,
b:2
}
KEY_MAP['a'] = 2; //不报错
-----------
const ARRAY = [];
ARRAY.push('something'); // 可执行
ARRAY.length = 0; // 可执行
ARRAY = ['something']; // 报错
//const的值如果是引用类型 值的属性可以修改
document.onscroll = function (e) {
const scrollData = {
top: document.documentElement.scrollTop,
left: document.documentElement.scrollLeft,
}
console.log(scrollData.top);
scrollData.top = 10; //可修改
console.log(scrollData.top);
}