ES6新语法之---块作用域let/const(2)
前言:
ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言的下一代标准,已经在 2015 年 6 月正式发布了,这节学习掌握ES6中的let/const。
1.JavaScript中的作用域
1>函数作用域
JavaScript中变量作用域的基本单元一直是函数(function)
<script> // 此处的a在{}中声明赋值 if (true) { var a = 3 console.log(a) //打印3 } console.log(a) //打印3 // JavaScript中函数是变量作用域的基本单元 function fn2() { var b = "5"; console.log(b); //打印5 } fn2(); console.log(b);//会报错 Uncaught ReferenceError: b is not defined </script>
2>"真假"块作用域
除了JavaScript外的很多编程语言都支持块作用域(比如:项目中使用的java)
举例:"假"块作用域
我们在for循环的头部定义了变量i,通常是只想在for循环内部的上下文中使用i,但最后我们却可以在for之外访问到它,这样就造成了i对整个函数作用域的污染。
for (var i = 0; i < 10; i++) { console.log(i) } console.log(i) //打印出10
举例:"真"块作用域
JavaScript的ES3规范中规定try/catch中的catch分句会创建一个块作用域,其中声明的变量仅在catch内部有效。
<script> try { undefined() //执行一个非法操作来强制制造一个异常 } catch (error) { console.log(err) //能够正常执行,err仅在catch的作用域中有效 } console.log() //ReferenceError: err is not defined </script>
2.ES6中的let
ES6引入了新的let关键字,提供le除var以外的另一种变量声明方式。let关键字可以将变量绑定到所在的任意作用域中。
1>let声明的变量只在当前代码块有效
{ let a = 10; var b = 1; } console.log(a) // ReferenceError: a is not defined console.log(b) // 1
2>let在for循环中的使用
let funcs = []; for (let i = 0; i< 5; i++) { funcs.push(function () { console.log(i) }); } funcs[3](); //打印结果是3
上例中,for循环头部的let i 不只为for循环本身声明了一个i,而是为循环的每一次迭代都声明了一个新的变量i,这样数组成员中变量i都是独立唯一的。
var funcs = []; // 此处用var声明了全局变量i for (var i = 0; i< 5; i++) { funcs.push(function () { //此处的i指向的就是全局变量i console.log(i) }); } funcs[3](); 5//最后数组成员中的i指向都是同一个全局变量i
上例中,for循环头部的var i 声明了一个全局变量,数组成员中变量i指向的都是同一个全局变量。
3>不存在变量提升
var声明的变量会发生变量提升现象,既在变量声明之前可以对变量进行访问,结果是'undefined'
let声明归属于块作用域,但是直到在块中出现才会初始化。
{ console.log(a); //undefined console.log(b); //ReferenceError var a; var b; }
在let声明/初始化之前访问let声明的变量会导致错误,这个错误严格叫做临时死亡区(Temporal Dead Zone, TDZ)
对于TDZ值和未声明值,typeof结果是不同的,举例:
{ if (typeof a === "undefined") { //条件成立,未声明变量默认为undefined console.log("cool"); } if (typeof b === "undefined") { //ReferenceError //... } let b; }
建议:let声明的变量最好置于当前作用域开始的位置。
4>不允许重复声明
let不允许在相同的作用域中声明同一个变量,也不允许在函数内部重新声明其参数。
举例:同一个作用域
// 报错 function func() { let a = 10; var a = 1; } // 报错 function func() { let a = 10; let a = 1; }
举例:声明函数参数
function func(arg) { let arg; // 报错 } function func(arg) { { let arg; // 不报错 } }
3.ES6中的const
const
声明一个只读的常量。一旦声明,常量的值就不能改变。
{ const a = 2; console.log(a); //const声明的常量,值不允许再改变 a = 3; //TypeError }
变量a的值在声明时必须显示初始化,而且之后就不允许改变。
注意:
常量不是对这个值的限制,而是对赋值的那个变量的限制。
举例:值为复杂类型
{ const a = [1, 2, 3]; //此时a指向的是[1, 2, 3]的地址 a.push(4); //[1, 2, 3]内容发生了改变,但是对象地址本身没有改变。 console.log(a); //const声明的常量,值不允许再改变 a = 3; //TypeError //变量a重新赋值就会报错 }
上例中,const限制是a变量本省,而非[1, 2, 3]这个数组对象。
注意:
1.const
实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指针,const
只能保证这个指针是固定的,至于它指向的数据结构是不是可变的,就完全不能控制了
2.const同样不支持变量提升。
3.const同样存在临时死亡区(TDZ)